class Shoulda::Matchers::ActiveRecord::DefineEnumForMatcher
@private
def actual_default_value
def actual_default_value attribute_schema = if model.respond_to?(:_default_attributes) model._default_attributes[attribute_name.to_s] else model.attributes_to_define_after_schema_loads[attribute_name.to_s] end if Kernel.const_defined?('ActiveModel::Attribute::UserProvidedDefault') && attribute_schema.is_a?(::ActiveModel::Attribute::UserProvidedDefault) attribute_schema = attribute_schema.marshal_dump end value = case attribute_schema in [_, { default: default_value } ] default_value in [_, default_value, *] default_value in [_, default_value] default_value end value.respond_to?(:call) ? value.call : value end
def actual_enum_values
def actual_enum_values model.send(attribute_name.to_s.pluralize) end
def backed_by_column_of_type(expected_column_type)
def backed_by_column_of_type(expected_column_type) options[:expected_column_type] = expected_column_type self end
def column
def column key = attribute_name.to_s column_name = model.attribute_alias(key) || key model.columns_hash[column_name] end
def column_type_matches?
def column_type_matches? if column.type == expected_column_type.to_sym true else @failure_message_continuation = "However, #{attribute_name.inspect} is "\ "#{Shoulda::Matchers::Util.a_or_an(column.type)}"\ ' column' false end end
def default_value_matches?
def default_value_matches? return true if options[:default].blank? if actual_default_value.nil? @failure_message_continuation = 'However, no default value was set' return false end if actual_default_value == expected_default_value true else String.new.tap do |message| message << 'However, the default value is ' message << Shoulda::Matchers::Util.inspect_value(actual_default_value) @failure_message_continuation = message end false end end
def description
def description description = "#{simple_description} backed by " description << Shoulda::Matchers::Util.a_or_an(expected_column_type) if expected_enum_values.any? description << ' with values ' description << Shoulda::Matchers::Util.inspect_value( expected_enum_values, ) end if options[:prefix] description << ", prefix: #{options[:prefix].inspect}" end if options[:suffix] description << ", suffix: #{options[:suffix].inspect}" end description end
def enum_defined?
def enum_defined? if model.defined_enums.include?(attribute_name.to_s) true else @failure_message_continuation = "no such enum exists on #{model}" false end end
def enum_value_methods_exist?
def enum_value_methods_exist? if options[:instance_methods] return true if instance_methods_exist? message = missing_methods_message message << " (we can't tell which)" if [expected_prefix, expected_suffix].any? @failure_message_continuation = message false elsif instance_methods_exist? message = "#{attribute_name.inspect} does map to these values" message << ' with instance methods, but expected no instance methods' @failure_message_continuation = message false else true end end
def enum_values_match?
def enum_values_match? passed = expected_enum_values.empty? || normalized_actual_enum_values == normalized_expected_enum_values if passed true else @failure_message_continuation = "However, #{attribute_name.inspect} actually maps " + presented_enum_mapping(normalized_actual_enum_values) false end end
def exclude_scopes?
def exclude_scopes? !options[:scopes] end
def expectation # rubocop:disable Metrics/MethodLength
def expectation # rubocop:disable Metrics/MethodLength if enum_defined? expectation = "#{simple_description} backed by " expectation << Shoulda::Matchers::Util.a_or_an(expected_column_type) if expected_enum_values.any? expectation << ', mapping ' expectation << presented_enum_mapping( normalized_expected_enum_values, ) end if options[:default].present? expectation << ', with a default value of ' expectation << Shoulda::Matchers::Util.inspect_value(expected_default_value) end if expected_validating? expectation << ', and being validated ' expectation << if expected_allowing_nil? 'allowing nil values' else 'not allowing nil values' end end if expected_prefix expectation << if expected_suffix ', ' else ' and ' end expectation << 'prefixing accessor methods with ' expectation << "#{expected_prefix}_".inspect end if expected_suffix expectation << if expected_prefix ', and ' else ' and ' end expectation << 'suffixing accessor methods with ' expectation << "_#{expected_suffix}".inspect end if exclude_scopes? expectation << ' with no scopes' end expectation else simple_description end end
def expected_allowing_nil?
def expected_allowing_nil? options[:allowing_nil].present? end
def expected_column_type
def expected_column_type options[:expected_column_type] || :integer end
def expected_default_value
def expected_default_value options[:default].respond_to?(:call) ? options[:default].call : options[:default] end
def expected_enum_value_names
def expected_enum_value_names to_array(expected_enum_values) end
def expected_enum_values
def expected_enum_values options[:expected_enum_values] end
def expected_instance_methods
def expected_instance_methods methods = expected_enum_value_names.map do |name| [expected_prefix, name, expected_suffix]. select(&:present?). join('_') end methods.flat_map do |m| ["#{m}?".to_sym, "#{m}!".to_sym] end end
def expected_instance_methods?
def expected_instance_methods? options[:instance_methods] end
def expected_prefix
def expected_prefix if options.include?(:prefix) if options[:prefix] == true attribute_name else options[:prefix] end end end
def expected_singleton_methods
def expected_singleton_methods expected_enum_value_names.map do |name| [expected_prefix, name, expected_suffix]. select(&:present?). join('_'). to_sym end end
def expected_suffix
def expected_suffix if options.include?(:suffix) if options[:suffix] == true attribute_name else options[:suffix] end end end
def expected_validating?
def expected_validating? options[:validating].present? end
def failure_message
def failure_message message = if enum_defined? "Expected #{model} to #{expectation}. " else "Expected #{model} to #{expectation}, but " end message << "#{failure_message_continuation}." Shoulda::Matchers.word_wrap(message) end
def failure_message_when_negated
def failure_message_when_negated message = "Expected #{model} not to #{expectation}, but it did." Shoulda::Matchers.word_wrap(message) end
def find_enum_validator
def find_enum_validator record.class.validators.detect do |validator| validator.kind == :inclusion && validator.attributes.include?(attribute_name.to_s) && validator.options[:in] == expected_enum_value_names end end
def initialize(attribute_name)
def initialize(attribute_name) @attribute_name = attribute_name @options = { expected_enum_values: [], scopes: true, instance_methods: true } end
def instance_methods_exist?
def instance_methods_exist? expected_instance_methods.all? do |method| record.methods.include?(method) end end
def matches?(subject)
def matches?(subject) @record = subject enum_defined? && enum_values_match? && column_type_matches? && enum_value_methods_exist? && scope_presence_matches? && default_value_matches? && validating_matches? end
def missing_methods_message
def missing_methods_message message = "#{attribute_name.inspect} does map to these " message << 'values, but the enum is ' if expected_prefix if expected_suffix message << 'configured with either a different prefix or ' message << 'suffix, or no prefix or suffix at all' else message << 'configured with either a different prefix or no ' message << 'prefix at all' end elsif expected_suffix message << 'configured with either a different suffix or no ' message << 'suffix at all' elsif expected_instance_methods? message << 'configured with no instance methods' else '' end end
def model
def model record.class end
def normalized_actual_enum_values
def normalized_actual_enum_values to_hash(actual_enum_values) end
def normalized_expected_enum_values
def normalized_expected_enum_values to_hash(expected_enum_values) end
def presented_enum_mapping(enum_values)
def presented_enum_mapping(enum_values) enum_values. map { |output_to_input| output_to_input. map(&Shoulda::Matchers::Util.method(:inspect_value)). join(' to ') }. to_sentence end
def scope_presence_matches?
def scope_presence_matches? if exclude_scopes? if singleton_methods_exist? message = "#{attribute_name.inspect} does map to these values " message << 'but class scope methods were present' @failure_message_continuation = message false else true end elsif singleton_methods_exist? true else if enum_defined? message = 'But the class scope methods are not present' else message = missing_methods_message message << 'or the class scope methods are not present' message << " (we can't tell which)" end @failure_message_continuation = message false end end
def simple_description
def simple_description "define :#{attribute_name} as an enum" end
def singleton_methods_exist?
def singleton_methods_exist? expected_singleton_methods.all? do |method| model.singleton_methods.include?(method) end end
def to_array(value)
def to_array(value) if value.is_a?(Array) value.map(&:to_s) else value.keys.map(&:to_s) end end
def to_hash(value)
def to_hash(value) if value.is_a?(Array) value.each_with_index.inject({}) do |hash, (item, index)| hash.merge!(item.to_s => index) end else value.stringify_keys end end
def validating(value = true, allowing_nil: false)
def validating(value = true, allowing_nil: false) options[:validating] = value options[:allowing_nil] = allowing_nil self end
def validating_matches?
def validating_matches? return true if options[:validating].nil? validator = find_enum_validator if expected_validating? == !!validator if validator&.options&.dig(:allow_nil).present? == expected_allowing_nil? true else @failure_message_continuation = "However, #{attribute_name.inspect} is allowing nil values" false end else @failure_message_continuation = if expected_validating? "However, #{attribute_name.inspect} is not being validated" else "However, #{attribute_name.inspect} is being validated" end false end end
def with_default(default_value)
def with_default(default_value) options[:default] = default_value self end
def with_prefix(expected_prefix = true)
def with_prefix(expected_prefix = true) options[:prefix] = expected_prefix self end
def with_suffix(expected_suffix = true)
def with_suffix(expected_suffix = true) options[:suffix] = expected_suffix self end
def with_values(expected_enum_values)
def with_values(expected_enum_values) options[:expected_enum_values] = expected_enum_values self end
def without_instance_methods
def without_instance_methods options[:instance_methods] = false self end
def without_scopes
def without_scopes options[:scopes] = false self end