thoughtbot--shoulda-matchers/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb

149 lines
3.7 KiB
Ruby

module Shoulda
module Matchers
module ActiveRecord
# The `define_enum_for` matcher is used to test that the `enum` macro has
# been used to decorate an attribute with enum methods.
#
# class Process < ActiveRecord::Base
# enum status: [:running, :stopped, :suspended]
# end
#
# # RSpec
# RSpec.describe Process, type: :model do
# it { should define_enum_for(:status) }
# end
# end
#
# # Minitest (Shoulda)
# class ProcessTest < ActiveSupport::TestCase
# should define_enum_for(:status)
# end
#
# #### Qualifiers
#
# ##### with
#
# Use `with` to test that the enum has been defined with a certain set of
# known values.
#
# class Process < ActiveRecord::Base
# enum status: [:running, :stopped, :suspended]
# end
#
# # RSpec
# RSpec.describe Process, type: :model do
# it do
# should define_enum_for(:status).
# with([:running, :stopped, :suspended])
# end
# end
#
# # Minitest (Shoulda)
# class ProcessTest < ActiveSupport::TestCase
# should define_enum_for(:status).
# with([:running, :stopped, :suspended])
# end
#
# @return [DefineEnumForMatcher]
#
def define_enum_for(attribute_name)
DefineEnumForMatcher.new(attribute_name)
end
# @private
class DefineEnumForMatcher
def initialize(attribute_name)
@attribute_name = attribute_name
@options = {}
end
def with(expected_enum_values)
options[:expected_enum_values] = expected_enum_values
self
end
def matches?(subject)
@record = subject
enum_defined? && enum_values_match? && column_type_is_integer?
end
def failure_message
"Expected #{expectation}"
end
alias :failure_message_for_should :failure_message
def failure_message_when_negated
"Did not expect #{expectation}"
end
alias :failure_message_for_should_not :failure_message_when_negated
def description
desc = "define :#{attribute_name} as an enum"
if options[:expected_enum_values]
desc << " with #{options[:expected_enum_values]}"
end
desc << " and store the value in a column with an integer type"
desc
end
protected
attr_reader :record, :attribute_name, :options
def expectation
"#{model.name} to #{description}"
end
def expected_enum_values
hashify(options[:expected_enum_values]).with_indifferent_access
end
def actual_enum_values
model.send(attribute_name.to_s.pluralize)
end
def enum_defined?
model.defined_enums.include?(attribute_name.to_s)
end
def enum_values_match?
expected_enum_values.empty? || actual_enum_values == expected_enum_values
end
def column_type_is_integer?
column.type == :integer
end
def column
model.columns_hash[attribute_name.to_s]
end
def model
record.class
end
def hashify(value)
if value.nil?
return {}
end
if value.is_a?(Array)
new_value = {}
value.each_with_index do |v, i|
new_value[v] = i
end
new_value
else
value
end
end
end
end
end
end