class Shoulda::Matchers::ActiveModel::ValidatePresenceOfMatcher
@private
def allows_and_double_checks_value_of!(value)
def allows_and_double_checks_value_of!(value) allows_value_of(value, @expected_message) rescue ActiveModel::AllowValueMatcher::AttributeChangedValueError raise ActiveModel::CouldNotSetPasswordError.create(model) end
def allows_original_or_typecast_value?(value)
def allows_original_or_typecast_value?(value) allows_value_of(value, @expected_message) end
def association?
def association? association_reflection.present? end
def association_name
def association_name association_reflection.name end
def association_options
def association_options association_reflection&.options end
def association_reflection
def association_reflection model.try(:reflect_on_association, @attribute) end
def attachment?
def attachment? model_has_associations?( ["#{@attribute}_attachment", "#{@attribute}_attachments"], ) end
def attribute_accepts_string_values?
def attribute_accepts_string_values? if association? false elsif attribute_serialization_coder.respond_to?(:object_class) attribute_serialization_coder.object_class == String else RailsShim.supports_full_attributes_api?(model) && attribute_type.try(:type) == :string end end
def attribute_serialization_coder
def attribute_serialization_coder RailsShim.attribute_serialization_coder_for(model, @attribute) end
def attribute_type
def attribute_type RailsShim.attribute_type_for(model, @attribute) end
def belongs_to_association_being_validated?
def belongs_to_association_being_validated? association? && association_reflection.macro == :belongs_to end
def belongs_to_association_configured_to_be_required?
def belongs_to_association_configured_to_be_required? association_options[:optional] == false || association_options[:required] == true end
def collection_association?
def collection_association? association? && [:has_many, :has_and_belongs_to_many].include?( association_reflection.macro, ) end
def disallowed_values
def disallowed_values if collection_association? [Array.new] elsif attachment? [nil] else values = [] if attribute_accepts_string_values? && !expects_to_allow_blank? values << '' end if !expects_to_allow_nil? && !expects_to_allow_blank? values << nil end values end end
def disallows_and_double_checks_value_of!(value)
def disallows_and_double_checks_value_of!(value) disallows_value_of(value, @expected_message) rescue ActiveModel::AllowValueMatcher::AttributeChangedValueError raise ActiveModel::CouldNotSetPasswordError.create(model) end
def disallows_original_or_typecast_value?(value)
def disallows_original_or_typecast_value?(value) disallows_value_of(value, @expected_message) end
def does_not_match?(subject)
def does_not_match?(subject) super(subject) possibly_ignore_interference_by_writer if secure_password_being_validated? ignore_interference_by_writer.default_to(when: :blank?) disallowed_values.any? do |value| allows_and_double_checks_value_of!(value) end else (expects_to_allow_nil? && disallows_value_of(nil)) || (expects_to_allow_blank? && disallows_value_of('')) || disallowed_values.any? do |value| allows_original_or_typecast_value?(value) end end end
def example_of_belongs_to(with: nil)
def example_of_belongs_to(with: nil) initial_call = "should belong_to(:#{association_name})" inside = if with "#{initial_call}.#{with.first}(#{with.second})" else initial_call end if Shoulda::Matchers.integrations.test_frameworks.any?(&:n_unit?) inside else "it { #{inside} }" end end
def failure_message
def failure_message message = super if should_add_footnote_about_belongs_to? message << "\n\n" message << Shoulda::Matchers.word_wrap(<<-MESSAGE.strip, indent: 2) etting this error because #{reason_for_existing_presence_validation}. resence validation doesn't use "can't be blank", the usual validation but "must exist" instead. t said, did you know that the `belong_to` matcher can test this on for you? Instead of using `validate_presence_of`, try tions_for_belongs_to} MESSAGE end message end
def initialize(attribute)
def initialize(attribute) super @expected_message = :blank end
def matches?(subject)
def matches?(subject) super(subject) possibly_ignore_interference_by_writer if secure_password_being_validated? && Shoulda::Matchers::RailsShim.active_model_lt_7? ignore_interference_by_writer.default_to(when: :blank?) disallowed_values.all? do |value| disallows_and_double_checks_value_of!(value) end else (!expects_to_allow_nil? || allows_value_of(nil)) && (!expects_to_allow_blank? || allows_value_of('')) && disallowed_values.all? do |value| disallows_original_or_typecast_value?(value) end end end
def model
def model @subject.class end
def model_has_associations?(associations)
def model_has_associations?(associations) associations.any? do |association| !!model.try(:reflect_on_association, association) end end
def possibly_ignore_interference_by_writer
def possibly_ignore_interference_by_writer if secure_password_being_validated? && RailsShim.active_model_lt_7? ignore_interference_by_writer.default_to(when: :blank?) end end
def presence_validation_exists_on_attribute?
def presence_validation_exists_on_attribute? model._validators.include?(@attribute) end
def reason_for_existing_presence_validation
def reason_for_existing_presence_validation if belongs_to_association_configured_to_be_required? "you've instructed your `belongs_to` association to add a "\ 'presence validation to the attribute' else # assume ::ActiveRecord::Base.belongs_to_required_by_default == true 'ActiveRecord is configured to add a presence validation to all '\ '`belongs_to` associations, and this includes yours' end end
def secure_password_being_validated?
def secure_password_being_validated? Shoulda::Matchers::RailsShim.digestible_attributes_in(@subject). include?(@attribute) end
def should_add_footnote_about_belongs_to?
def should_add_footnote_about_belongs_to? belongs_to_association_being_validated? && presence_validation_exists_on_attribute? end
def simple_description
def simple_description "validate that :#{@attribute} cannot be empty/falsy" end
def suggestions_for_belongs_to
def suggestions_for_belongs_to if belongs_to_association_configured_to_be_required? <<~MESSAGE one of the following instead, depending on your use case: #{example_of_belongs_to(with: [:optional, false])} #{example_of_belongs_to(with: [:required, true])} MESSAGE else <<~MESSAGE the following instead: #{example_of_belongs_to} MESSAGE end end