Skip to content

Commit

Permalink
feat: Add without_instance_methods qualifier to enum matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
vaot committed Jun 12, 2024
1 parent 3c88e1c commit 46632ce
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 9 deletions.
46 changes: 40 additions & 6 deletions lib/shoulda/matchers/active_record/define_enum_for_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,29 @@ module ActiveRecord
# validating(allowing_nil: true)
# end
#
# @return [DefineEnumForMatcher]
# ##### without_instance_methods
#
# Use `without_instance_methods` to exclude the check for instance methods.
#
# class Issue < ActiveRecord::Base
# enum status: [:open, :closed], instance_methods: false
# end
#
# # RSpec
# RSpec.describe Issue, type: :model do
# it do
# should define_enum_for(:status).
# without_instance_methods
# end
# end
#
# # Minitest (Shoulda)
# class ProcessTest < ActiveSupport::TestCase
# should define_enum_for(:status).
# without_instance_methods
# end
#
# @return [DefineEnumForMatcher]
def define_enum_for(attribute_name)
DefineEnumForMatcher.new(attribute_name)
end
Expand All @@ -263,7 +284,7 @@ def define_enum_for(attribute_name)
class DefineEnumForMatcher
def initialize(attribute_name)
@attribute_name = attribute_name
@options = { expected_enum_values: [], scopes: true }
@options = { expected_enum_values: [], scopes: true, instance_methods: true }
end

def description
Expand Down Expand Up @@ -319,6 +340,11 @@ def without_scopes
self
end

def without_instance_methods
options[:instance_methods] = false
self
end

def with_default(default_value)
options[:default] = default_value
self
Expand Down Expand Up @@ -531,16 +557,24 @@ def model
end

def enum_value_methods_exist?
if instance_methods_exist?
true
else
message = missing_methods_message
if options[:instance_methods]
return true if instance_methods_exist?

message = missing_methods_message
message << " (we can't tell which)"
@failure_message_continuation = message

false
elsif instance_methods_exist?
message = missing_methods_message

message << '. Expected no instance methods, but instance methods were present'

@failure_message_continuation = message

false
else
true
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,75 @@ def self.statuses
end
end
end

describe 'qualified with #without_instance_methods' do
context 'if instance methods are set to false on the enum but without_instance_methods is not used' do
it 'rejects with failure message' do
record = build_record_with_array_values(
attribute_name: :attr,
instance_methods: false,
)

matcher = lambda do
expect(record).
to define_enum_for(:attr).
with_values(['published', 'unpublished', 'draft'])
end

message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping ‹"published"› to ‹0›, ‹"unpublished"› to ‹1›, and ‹"draft"› to
‹2›. (we can't tell which).
MESSAGE

expect(&matcher).to fail_with_message(message)
end
end

context 'if instance methods are set to false on the enum' do
it 'matches' do
record = build_record_with_array_values(
attribute_name: :attr,
instance_methods: false,
)

matcher = lambda do
define_enum_for(:attr).
with_values(['published', 'unpublished', 'draft']).
without_instance_methods
end

expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE)
Expected Example not to define :attr as an enum backed by an integer,
mapping ‹"published"› to ‹0›, ‹"unpublished"› to ‹1›, and ‹"draft"› to
‹2›, but it did.
MESSAGE
end
end

context 'if instance methods are not set to false on the enum' do
it 'rejects with failure message' do
record = build_record_with_array_values(attribute_name: :attr)

matcher = lambda do
expect(record).
to define_enum_for(:attr).
with_values(['published', 'unpublished', 'draft']).
without_instance_methods
end

message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping ‹"published"› to ‹0›, ‹"unpublished"› to ‹1›, and ‹"draft"› to
‹2›. . Expected no instance methods, but instance methods were present.
MESSAGE

expect(&matcher).to fail_with_message(message)
end
end
end
end

if rails_version =~ '~> 6.0'
Expand Down Expand Up @@ -1198,7 +1267,8 @@ def build_record_with_array_values(
attribute_alias: nil,
scopes: true,
default: nil,
validate: false
validate: false,
instance_methods: true
)
build_record_with_enum_attribute(
model_name: model_name,
Expand All @@ -1211,6 +1281,7 @@ def build_record_with_array_values(
scopes: scopes,
default: default,
validate: validate,
instance_methods: instance_methods
)
end

Expand Down Expand Up @@ -1244,7 +1315,8 @@ def build_record_with_enum_attribute(
prefix: false,
suffix: false,
default: nil,
validate: false
validate: false,
instance_methods: true
)
enum_name = attribute_alias || attribute_name
model = define_model(
Expand All @@ -1262,7 +1334,7 @@ def build_record_with_enum_attribute(
}

if rails_version >= 7.0
model.enum(enum_name, values, prefix: prefix, suffix: suffix, validate: validate, default: default)
model.enum(enum_name, values, prefix: prefix, suffix: suffix, validate: validate, default: default, instance_methods: instance_methods)
else
params.merge!(_scopes: scopes)
model.enum(params)
Expand Down

0 comments on commit 46632ce

Please sign in to comment.