shoulda-matchers 4.1.2 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/{MIT-LICENSE → LICENSE} +1 -1
 - data/README.md +168 -86
 - data/lib/shoulda/matchers/action_controller/callback_matcher.rb +4 -2
 - data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +3 -2
 - data/lib/shoulda/matchers/action_controller/permit_matcher.rb +26 -21
 - data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +6 -8
 - data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +6 -8
 - data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +16 -13
 - data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +2 -1
 - data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -6
 - data/lib/shoulda/matchers/action_controller/route_params.rb +1 -1
 - data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +19 -13
 - data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +18 -16
 - data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +31 -29
 - data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +1 -1
 - data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +5 -5
 - data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +2 -2
 - data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +1 -1
 - data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +1 -1
 - data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +1 -1
 - data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +51 -25
 - data/lib/shoulda/matchers/active_model/helpers.rb +1 -1
 - data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +32 -30
 - data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +1 -1
 - data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +1 -1
 - data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +9 -1
 - data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +2 -2
 - data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +9 -8
 - data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +28 -46
 - data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +33 -9
 - data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +71 -26
 - data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +2 -2
 - data/lib/shoulda/matchers/active_model/validation_matcher.rb +31 -6
 - data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +2 -4
 - data/lib/shoulda/matchers/active_model/validation_message_finder.rb +2 -4
 - data/lib/shoulda/matchers/active_model/validator.rb +3 -3
 - data/lib/shoulda/matchers/active_record.rb +26 -23
 - data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +6 -3
 - data/lib/shoulda/matchers/active_record/association_matcher.rb +100 -44
 - data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +5 -2
 - data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +4 -4
 - data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +1 -1
 - data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +11 -6
 - data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +14 -15
 - data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +30 -8
 - data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +23 -5
 - data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +3 -3
 - data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +1 -1
 - data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +3 -3
 - data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +3 -2
 - data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -5
 - data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +8 -8
 - data/lib/shoulda/matchers/active_record/have_attached_matcher.rb +185 -0
 - data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +40 -18
 - data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
 - data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +106 -0
 - data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +12 -10
 - data/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
 - data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +30 -9
 - data/lib/shoulda/matchers/active_record/serialize_matcher.rb +13 -9
 - data/lib/shoulda/matchers/active_record/uniqueness.rb +1 -1
 - data/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +1 -3
 - data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +0 -2
 - data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +80 -73
 - data/lib/shoulda/matchers/doublespeak.rb +2 -1
 - data/lib/shoulda/matchers/doublespeak/double.rb +1 -1
 - data/lib/shoulda/matchers/doublespeak/double_collection.rb +3 -3
 - data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +8 -5
 - data/lib/shoulda/matchers/doublespeak/object_double.rb +1 -1
 - data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +1 -5
 - data/lib/shoulda/matchers/doublespeak/world.rb +2 -2
 - data/lib/shoulda/matchers/error.rb +1 -1
 - data/lib/shoulda/matchers/independent.rb +0 -1
 - data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +14 -13
 - data/lib/shoulda/matchers/integrations/configuration.rb +1 -1
 - data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
 - data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -2
 - data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
 - data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
 - data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
 - data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
 - data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
 - data/lib/shoulda/matchers/rails_shim.rb +10 -21
 - data/lib/shoulda/matchers/util.rb +16 -4
 - data/lib/shoulda/matchers/util/word_wrap.rb +8 -8
 - data/lib/shoulda/matchers/version.rb +1 -1
 - data/lib/shoulda/matchers/warn.rb +3 -3
 - data/shoulda-matchers.gemspec +10 -7
 - metadata +11 -9
 - data/lib/shoulda/matchers/independent/delegate_method_matcher/stubbed_target.rb +0 -37
 
| 
         @@ -18,7 +18,7 @@ module Shoulda 
     | 
|
| 
       18 
18 
     | 
    
         
             
                        @reflector = reflector
         
     | 
| 
       19 
19 
     | 
    
         
             
                      end
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                      def matches?( 
     | 
| 
      
 21 
     | 
    
         
            +
                      def matches?(_subject)
         
     | 
| 
       22 
22 
     | 
    
         
             
                        join_table_option_correct? &&
         
     | 
| 
       23 
23 
     | 
    
         
             
                          join_table_exists? &&
         
     | 
| 
       24 
24 
     | 
    
         
             
                          join_table_has_correct_columns?
         
     | 
| 
         @@ -26,10 +26,14 @@ module Shoulda 
     | 
|
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
                      def join_table_option_correct?
         
     | 
| 
       28 
28 
     | 
    
         
             
                        if options.key?(:join_table_name)
         
     | 
| 
       29 
     | 
    
         
            -
                          if option_verifier.correct_for_string?( 
     | 
| 
      
 29 
     | 
    
         
            +
                          if option_verifier.correct_for_string?(
         
     | 
| 
      
 30 
     | 
    
         
            +
                            :join_table,
         
     | 
| 
      
 31 
     | 
    
         
            +
                            options[:join_table_name],
         
     | 
| 
      
 32 
     | 
    
         
            +
                          )
         
     | 
| 
       30 
33 
     | 
    
         
             
                            true
         
     | 
| 
       31 
34 
     | 
    
         
             
                          else
         
     | 
| 
       32 
     | 
    
         
            -
                            @failure_message = "#{name} should use 
     | 
| 
      
 35 
     | 
    
         
            +
                            @failure_message = "#{name} should use"\
         
     | 
| 
      
 36 
     | 
    
         
            +
                              " #{options[:join_table_name].inspect} for :join_table option"
         
     | 
| 
       33 
37 
     | 
    
         
             
                            false
         
     | 
| 
       34 
38 
     | 
    
         
             
                          end
         
     | 
| 
       35 
39 
     | 
    
         
             
                        else
         
     | 
| 
         @@ -38,7 +42,8 @@ module Shoulda 
     | 
|
| 
       38 
42 
     | 
    
         
             
                      end
         
     | 
| 
       39 
43 
     | 
    
         | 
| 
       40 
44 
     | 
    
         
             
                      def join_table_exists?
         
     | 
| 
       41 
     | 
    
         
            -
                        if RailsShim.tables_and_views(connection). 
     | 
| 
      
 45 
     | 
    
         
            +
                        if RailsShim.tables_and_views(connection).
         
     | 
| 
      
 46 
     | 
    
         
            +
                            include?(join_table_name.to_s)
         
     | 
| 
       42 
47 
     | 
    
         
             
                          true
         
     | 
| 
       43 
48 
     | 
    
         
             
                        else
         
     | 
| 
       44 
49 
     | 
    
         
             
                          @failure_message = missing_table_message
         
     | 
| 
         @@ -64,8 +69,8 @@ module Shoulda 
     | 
|
| 
       64 
69 
     | 
    
         
             
                      delegate :foreign_key, :association_foreign_key, to: :reflector
         
     | 
| 
       65 
70 
     | 
    
         | 
| 
       66 
71 
     | 
    
         
             
                      def missing_columns
         
     | 
| 
       67 
     | 
    
         
            -
                        @ 
     | 
| 
       68 
     | 
    
         
            -
                           
     | 
| 
      
 72 
     | 
    
         
            +
                        @_missing_columns ||= expected_join_table_columns.reject do |key|
         
     | 
| 
      
 73 
     | 
    
         
            +
                          actual_join_table_columns.include?(key.to_s)
         
     | 
| 
       69 
74 
     | 
    
         
             
                        end
         
     | 
| 
       70 
75 
     | 
    
         
             
                      end
         
     | 
| 
       71 
76 
     | 
    
         | 
| 
         @@ -26,21 +26,16 @@ module Shoulda 
     | 
|
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
                      def join_table_name
         
     | 
| 
       28 
28 
     | 
    
         
             
                        join_table_name =
         
     | 
| 
       29 
     | 
    
         
            -
                           
     | 
| 
       30 
     | 
    
         
            -
                            has_and_belongs_to_many_name_table_name
         
     | 
| 
       31 
     | 
    
         
            -
                          else
         
     | 
| 
       32 
     | 
    
         
            -
                            reflection.join_table
         
     | 
| 
       33 
     | 
    
         
            -
                          end
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
      
 29 
     | 
    
         
            +
                          has_and_belongs_to_many_name_table_name || reflection.join_table
         
     | 
| 
       35 
30 
     | 
    
         
             
                        join_table_name.to_s
         
     | 
| 
       36 
31 
     | 
    
         
             
                      end
         
     | 
| 
       37 
32 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
                      def association_relation
         
     | 
| 
      
 33 
     | 
    
         
            +
                      def association_relation(related_instance)
         
     | 
| 
       39 
34 
     | 
    
         
             
                        relation = associated_class.all
         
     | 
| 
       40 
35 
     | 
    
         | 
| 
       41 
36 
     | 
    
         
             
                        if reflection.scope
         
     | 
| 
       42 
37 
     | 
    
         
             
                          # Source: AR::Associations::AssociationScope#eval_scope
         
     | 
| 
       43 
     | 
    
         
            -
                          relation.instance_exec( 
     | 
| 
      
 38 
     | 
    
         
            +
                          relation.instance_exec(related_instance, &reflection.scope)
         
     | 
| 
       44 
39 
     | 
    
         
             
                        else
         
     | 
| 
       45 
40 
     | 
    
         
             
                          relation
         
     | 
| 
       46 
41 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -65,20 +60,24 @@ module Shoulda 
     | 
|
| 
       65 
60 
     | 
    
         
             
                        end
         
     | 
| 
       66 
61 
     | 
    
         
             
                      end
         
     | 
| 
       67 
62 
     | 
    
         | 
| 
      
 63 
     | 
    
         
            +
                      def validate_inverse_of_through_association!
         
     | 
| 
      
 64 
     | 
    
         
            +
                        if through?
         
     | 
| 
      
 65 
     | 
    
         
            +
                          reflection.check_validity!
         
     | 
| 
      
 66 
     | 
    
         
            +
                        end
         
     | 
| 
      
 67 
     | 
    
         
            +
                      end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                      def has_and_belongs_to_many_name
         
     | 
| 
      
 70 
     | 
    
         
            +
                        reflection.options[:through]
         
     | 
| 
      
 71 
     | 
    
         
            +
                      end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
       68 
73 
     | 
    
         
             
                      protected
         
     | 
| 
       69 
74 
     | 
    
         | 
| 
       70 
75 
     | 
    
         
             
                      attr_reader :reflection, :subject
         
     | 
| 
       71 
76 
     | 
    
         | 
| 
       72 
77 
     | 
    
         
             
                      private
         
     | 
| 
       73 
78 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
                      def has_and_belongs_to_many_name
         
     | 
| 
       75 
     | 
    
         
            -
                        reflection.options[:through]
         
     | 
| 
       76 
     | 
    
         
            -
                      end
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
79 
     | 
    
         
             
                      def has_and_belongs_to_many_name_table_name
         
     | 
| 
       79 
     | 
    
         
            -
                         
     | 
| 
       80 
     | 
    
         
            -
                          has_and_belongs_to_many_reflection.table_name
         
     | 
| 
       81 
     | 
    
         
            -
                        end
         
     | 
| 
      
 80 
     | 
    
         
            +
                        has_and_belongs_to_many_reflection&.table_name
         
     | 
| 
       82 
81 
     | 
    
         
             
                      end
         
     | 
| 
       83 
82 
     | 
    
         | 
| 
       84 
83 
     | 
    
         
             
                      def has_and_belongs_to_many_reflection
         
     | 
| 
         @@ -4,17 +4,34 @@ module Shoulda 
     | 
|
| 
       4 
4 
     | 
    
         
             
                  module AssociationMatchers
         
     | 
| 
       5 
5 
     | 
    
         
             
                    # @private
         
     | 
| 
       6 
6 
     | 
    
         
             
                    class ModelReflector
         
     | 
| 
       7 
     | 
    
         
            -
                      delegate 
     | 
| 
       8 
     | 
    
         
            -
                        : 
     | 
| 
       9 
     | 
    
         
            -
                        :association_foreign_key, 
     | 
| 
      
 7 
     | 
    
         
            +
                      delegate(
         
     | 
| 
      
 8 
     | 
    
         
            +
                        :associated_class,
         
     | 
| 
      
 9 
     | 
    
         
            +
                        :association_foreign_key,
         
     | 
| 
      
 10 
     | 
    
         
            +
                        :foreign_key,
         
     | 
| 
      
 11 
     | 
    
         
            +
                        :has_and_belongs_to_many_name,
         
     | 
| 
      
 12 
     | 
    
         
            +
                        :join_table_name,
         
     | 
| 
      
 13 
     | 
    
         
            +
                        :polymorphic?,
         
     | 
| 
      
 14 
     | 
    
         
            +
                        :validate_inverse_of_through_association!,
         
     | 
| 
      
 15 
     | 
    
         
            +
                        to: :reflection,
         
     | 
| 
      
 16 
     | 
    
         
            +
                      )
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                      delegate(
         
     | 
| 
      
 19 
     | 
    
         
            +
                        :through?,
         
     | 
| 
      
 20 
     | 
    
         
            +
                        to: :reflection,
         
     | 
| 
      
 21 
     | 
    
         
            +
                        allow_nil: true,
         
     | 
| 
      
 22 
     | 
    
         
            +
                      )
         
     | 
| 
       10 
23 
     | 
    
         | 
| 
       11 
24 
     | 
    
         
             
                      def initialize(subject, name)
         
     | 
| 
       12 
25 
     | 
    
         
             
                        @subject = subject
         
     | 
| 
       13 
26 
     | 
    
         
             
                        @name = name
         
     | 
| 
       14 
27 
     | 
    
         
             
                      end
         
     | 
| 
       15 
28 
     | 
    
         | 
| 
      
 29 
     | 
    
         
            +
                      def association_relation
         
     | 
| 
      
 30 
     | 
    
         
            +
                        reflection.association_relation(subject)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       16 
33 
     | 
    
         
             
                      def reflection
         
     | 
| 
       17 
     | 
    
         
            -
                        @ 
     | 
| 
      
 34 
     | 
    
         
            +
                        @_reflection ||= reflect_on_association(name)
         
     | 
| 
       18 
35 
     | 
    
         
             
                      end
         
     | 
| 
       19 
36 
     | 
    
         | 
| 
       20 
37 
     | 
    
         
             
                      def reflect_on_association(name)
         
     | 
| 
         @@ -31,9 +48,12 @@ module Shoulda 
     | 
|
| 
       31 
48 
     | 
    
         | 
| 
       32 
49 
     | 
    
         
             
                      def build_relation_with_clause(name, value)
         
     | 
| 
       33 
50 
     | 
    
         
             
                        case name
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                           
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
      
 51 
     | 
    
         
            +
                        when :conditions
         
     | 
| 
      
 52 
     | 
    
         
            +
                          associated_class.where(value)
         
     | 
| 
      
 53 
     | 
    
         
            +
                        when :order
         
     | 
| 
      
 54 
     | 
    
         
            +
                          associated_class.order(value)
         
     | 
| 
      
 55 
     | 
    
         
            +
                        else
         
     | 
| 
      
 56 
     | 
    
         
            +
                          raise ArgumentError, "Unknown clause '#{name}'"
         
     | 
| 
       37 
57 
     | 
    
         
             
                        end
         
     | 
| 
       38 
58 
     | 
    
         
             
                      end
         
     | 
| 
       39 
59 
     | 
    
         | 
| 
         @@ -42,7 +62,9 @@ module Shoulda 
     | 
|
| 
       42 
62 
     | 
    
         
             
                        when :conditions
         
     | 
| 
       43 
63 
     | 
    
         
             
                          relation.where_values_hash
         
     | 
| 
       44 
64 
     | 
    
         
             
                        when :order
         
     | 
| 
       45 
     | 
    
         
            -
                          relation.order_values.map  
     | 
| 
      
 65 
     | 
    
         
            +
                          relation.order_values.map do |value|
         
     | 
| 
      
 66 
     | 
    
         
            +
                            value_as_sql(value)
         
     | 
| 
      
 67 
     | 
    
         
            +
                          end.join(', ')
         
     | 
| 
       46 
68 
     | 
    
         
             
                        else
         
     | 
| 
       47 
69 
     | 
    
         
             
                          raise ArgumentError, "Unknown clause '#{name}'"
         
     | 
| 
       48 
70 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -6,7 +6,12 @@ module Shoulda 
     | 
|
| 
       6 
6 
     | 
    
         
             
                    class OptionVerifier
         
     | 
| 
       7 
7 
     | 
    
         
             
                      delegate :reflection, to: :reflector
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                       
     | 
| 
      
 9 
     | 
    
         
            +
                      DEFAULT_VALUE_OF_OPTIONS = {
         
     | 
| 
      
 10 
     | 
    
         
            +
                        has_many: {
         
     | 
| 
      
 11 
     | 
    
         
            +
                          validate: true,
         
     | 
| 
      
 12 
     | 
    
         
            +
                        },
         
     | 
| 
      
 13 
     | 
    
         
            +
                      }.freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
                      RELATION_OPTIONS = [:conditions, :order].freeze
         
     | 
| 
       10 
15 
     | 
    
         | 
| 
       11 
16 
     | 
    
         
             
                      def initialize(reflector)
         
     | 
| 
       12 
17 
     | 
    
         
             
                        @reflector = reflector
         
     | 
| 
         @@ -40,7 +45,7 @@ module Shoulda 
     | 
|
| 
       40 
45 
     | 
    
         
             
                        else
         
     | 
| 
       41 
46 
     | 
    
         
             
                          type_cast_expected_value = type_cast(
         
     | 
| 
       42 
47 
     | 
    
         
             
                            type,
         
     | 
| 
       43 
     | 
    
         
            -
                            expected_value_for(type, name, expected_value)
         
     | 
| 
      
 48 
     | 
    
         
            +
                            expected_value_for(type, name, expected_value),
         
     | 
| 
       44 
49 
     | 
    
         
             
                          )
         
     | 
| 
       45 
50 
     | 
    
         
             
                          actual_value = type_cast(type, actual_value_for(name))
         
     | 
| 
       46 
51 
     | 
    
         
             
                          type_cast_expected_value == actual_value
         
     | 
| 
         @@ -55,7 +60,7 @@ module Shoulda 
     | 
|
| 
       55 
60 
     | 
    
         
             
                          if respond_to?(method_name, true)
         
     | 
| 
       56 
61 
     | 
    
         
             
                            __send__(method_name)
         
     | 
| 
       57 
62 
     | 
    
         
             
                          else
         
     | 
| 
       58 
     | 
    
         
            -
                             
     | 
| 
      
 63 
     | 
    
         
            +
                            actual_value_for_option(name)
         
     | 
| 
       59 
64 
     | 
    
         
             
                          end
         
     | 
| 
       60 
65 
     | 
    
         
             
                        end
         
     | 
| 
       61 
66 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -94,7 +99,7 @@ module Shoulda 
     | 
|
| 
       94 
99 
     | 
    
         | 
| 
       95 
100 
     | 
    
         
             
                      def expected_value_for_constant(name)
         
     | 
| 
       96 
101 
     | 
    
         
             
                        namespace = Shoulda::Matchers::Util.deconstantize(
         
     | 
| 
       97 
     | 
    
         
            -
                          reflector.model_class.to_s
         
     | 
| 
      
 102 
     | 
    
         
            +
                          reflector.model_class.to_s,
         
     | 
| 
       98 
103 
     | 
    
         
             
                        )
         
     | 
| 
       99 
104 
     | 
    
         | 
| 
       100 
105 
     | 
    
         
             
                        ["#{namespace}::#{name}", name].each do |path|
         
     | 
| 
         @@ -107,12 +112,25 @@ module Shoulda 
     | 
|
| 
       107 
112 
     | 
    
         
             
                      end
         
     | 
| 
       108 
113 
     | 
    
         | 
| 
       109 
114 
     | 
    
         
             
                      def actual_value_for_relation_clause(name)
         
     | 
| 
       110 
     | 
    
         
            -
                        reflector.extract_relation_clause_from( 
     | 
| 
      
 115 
     | 
    
         
            +
                        reflector.extract_relation_clause_from(
         
     | 
| 
      
 116 
     | 
    
         
            +
                          reflector.association_relation,
         
     | 
| 
      
 117 
     | 
    
         
            +
                          name,
         
     | 
| 
      
 118 
     | 
    
         
            +
                        )
         
     | 
| 
       111 
119 
     | 
    
         
             
                      end
         
     | 
| 
       112 
120 
     | 
    
         | 
| 
       113 
121 
     | 
    
         
             
                      def actual_value_for_class_name
         
     | 
| 
       114 
122 
     | 
    
         
             
                        reflector.associated_class
         
     | 
| 
       115 
123 
     | 
    
         
             
                      end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                      def actual_value_for_option(name)
         
     | 
| 
      
 126 
     | 
    
         
            +
                        option_value = reflection.options[name]
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                        if option_value.nil?
         
     | 
| 
      
 129 
     | 
    
         
            +
                          DEFAULT_VALUE_OF_OPTIONS.dig(reflection.macro, name)
         
     | 
| 
      
 130 
     | 
    
         
            +
                        else
         
     | 
| 
      
 131 
     | 
    
         
            +
                          option_value
         
     | 
| 
      
 132 
     | 
    
         
            +
                        end
         
     | 
| 
      
 133 
     | 
    
         
            +
                      end
         
     | 
| 
       116 
134 
     | 
    
         
             
                    end
         
     | 
| 
       117 
135 
     | 
    
         
             
                  end
         
     | 
| 
       118 
136 
     | 
    
         
             
                end
         
     | 
| 
         @@ -32,9 +32,9 @@ module Shoulda 
     | 
|
| 
       32 
32 
     | 
    
         
             
                            end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                          missing_option << (
         
     | 
| 
       35 
     | 
    
         
            -
                            'fail validation if ' 
     | 
| 
       36 
     | 
    
         
            -
                            ":#{attribute_name} is unset; i.e., either the association " 
     | 
| 
       37 
     | 
    
         
            -
                            'should have been defined with `optional: ' 
     | 
| 
      
 35 
     | 
    
         
            +
                            'fail validation if '\
         
     | 
| 
      
 36 
     | 
    
         
            +
                            ":#{attribute_name} is unset; i.e., either the association "\
         
     | 
| 
      
 37 
     | 
    
         
            +
                            'should have been defined with `optional: '\
         
     | 
| 
       38 
38 
     | 
    
         
             
                            "#{optional.inspect}`, or there "
         
     | 
| 
       39 
39 
     | 
    
         
             
                          )
         
     | 
| 
       40 
40 
     | 
    
         | 
| 
         @@ -33,9 +33,9 @@ module Shoulda 
     | 
|
| 
       33 
33 
     | 
    
         
             
                            end
         
     | 
| 
       34 
34 
     | 
    
         | 
| 
       35 
35 
     | 
    
         
             
                          missing_option << (
         
     | 
| 
       36 
     | 
    
         
            -
                            'fail validation if ' 
     | 
| 
       37 
     | 
    
         
            -
                            ":#{attribute_name} is unset; i.e., either the association " 
     | 
| 
       38 
     | 
    
         
            -
                            'should have been defined with `required: ' 
     | 
| 
      
 36 
     | 
    
         
            +
                            'fail validation if '\
         
     | 
| 
      
 37 
     | 
    
         
            +
                            ":#{attribute_name} is unset; i.e., either the association "\
         
     | 
| 
      
 38 
     | 
    
         
            +
                            'should have been defined with `required: '\
         
     | 
| 
       39 
39 
     | 
    
         
             
                            "#{required.inspect}`, or there "
         
     | 
| 
       40 
40 
     | 
    
         
             
                          )
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
         @@ -22,7 +22,8 @@ module Shoulda 
     | 
|
| 
       22 
22 
     | 
    
         
             
                        if option_verifier.correct_for_string?(:source, source)
         
     | 
| 
       23 
23 
     | 
    
         
             
                          true
         
     | 
| 
       24 
24 
     | 
    
         
             
                        else
         
     | 
| 
       25 
     | 
    
         
            -
                          self.missing_option = 
     | 
| 
      
 25 
     | 
    
         
            +
                          self.missing_option =
         
     | 
| 
      
 26 
     | 
    
         
            +
                            "#{name} should have #{source} as source option"
         
     | 
| 
       26 
27 
     | 
    
         
             
                          false
         
     | 
| 
       27 
28 
     | 
    
         
             
                        end
         
     | 
| 
       28 
29 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -32,7 +33,7 @@ module Shoulda 
     | 
|
| 
       32 
33 
     | 
    
         
             
                      attr_accessor :subject, :source, :name
         
     | 
| 
       33 
34 
     | 
    
         | 
| 
       34 
35 
     | 
    
         
             
                      def option_verifier
         
     | 
| 
       35 
     | 
    
         
            -
                        @ 
     | 
| 
      
 36 
     | 
    
         
            +
                        @_option_verifier ||= OptionVerifier.new(subject)
         
     | 
| 
       36 
37 
     | 
    
         
             
                      end
         
     | 
| 
       37 
38 
     | 
    
         
             
                    end
         
     | 
| 
       38 
39 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -29,13 +29,14 @@ module Shoulda 
     | 
|
| 
       29 
29 
     | 
    
         
             
                        if through_reflection.present?
         
     | 
| 
       30 
30 
     | 
    
         
             
                          true
         
     | 
| 
       31 
31 
     | 
    
         
             
                        else
         
     | 
| 
       32 
     | 
    
         
            -
                          self.missing_option = 
     | 
| 
      
 32 
     | 
    
         
            +
                          self.missing_option =
         
     | 
| 
      
 33 
     | 
    
         
            +
                            "#{name} does not have any relationship to #{through}"
         
     | 
| 
       33 
34 
     | 
    
         
             
                          false
         
     | 
| 
       34 
35 
     | 
    
         
             
                        end
         
     | 
| 
       35 
36 
     | 
    
         
             
                      end
         
     | 
| 
       36 
37 
     | 
    
         | 
| 
       37 
38 
     | 
    
         
             
                      def through_reflection
         
     | 
| 
       38 
     | 
    
         
            -
                        @ 
     | 
| 
      
 39 
     | 
    
         
            +
                        @_through_reflection ||= subject.reflect_on_association(through)
         
     | 
| 
       39 
40 
     | 
    
         
             
                      end
         
     | 
| 
       40 
41 
     | 
    
         | 
| 
       41 
42 
     | 
    
         
             
                      def through_association_correct?
         
     | 
| 
         @@ -43,8 +44,9 @@ module Shoulda 
     | 
|
| 
       43 
44 
     | 
    
         
             
                          true
         
     | 
| 
       44 
45 
     | 
    
         
             
                        else
         
     | 
| 
       45 
46 
     | 
    
         
             
                          self.missing_option =
         
     | 
| 
       46 
     | 
    
         
            -
                            "Expected #{name} to have #{name} through #{through}, " 
     | 
| 
       47 
     | 
    
         
            -
                             
     | 
| 
      
 47 
     | 
    
         
            +
                            "Expected #{name} to have #{name} through #{through}, "\
         
     | 
| 
      
 48 
     | 
    
         
            +
                            'but got it through ' +
         
     | 
| 
      
 49 
     | 
    
         
            +
                            option_verifier.actual_value_for(:through).to_s
         
     | 
| 
       48 
50 
     | 
    
         
             
                          false
         
     | 
| 
       49 
51 
     | 
    
         
             
                        end
         
     | 
| 
       50 
52 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -54,7 +56,7 @@ module Shoulda 
     | 
|
| 
       54 
56 
     | 
    
         
             
                      attr_accessor :through, :name, :subject
         
     | 
| 
       55 
57 
     | 
    
         | 
| 
       56 
58 
     | 
    
         
             
                      def option_verifier
         
     | 
| 
       57 
     | 
    
         
            -
                        @ 
     | 
| 
      
 59 
     | 
    
         
            +
                        @_option_verifier ||= OptionVerifier.new(subject)
         
     | 
| 
       58 
60 
     | 
    
         
             
                      end
         
     | 
| 
       59 
61 
     | 
    
         
             
                    end
         
     | 
| 
       60 
62 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -237,7 +237,7 @@ module Shoulda 
     | 
|
| 
       237 
237 
     | 
    
         
             
                          "Expected #{model} to #{expectation}, but "
         
     | 
| 
       238 
238 
     | 
    
         
             
                        end
         
     | 
| 
       239 
239 
     | 
    
         | 
| 
       240 
     | 
    
         
            -
                      message << failure_message_continuation 
     | 
| 
      
 240 
     | 
    
         
            +
                      message << "#{failure_message_continuation}."
         
     | 
| 
       241 
241 
     | 
    
         | 
| 
       242 
242 
     | 
    
         
             
                      Shoulda::Matchers.word_wrap(message)
         
     | 
| 
       243 
243 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -252,7 +252,7 @@ module Shoulda 
     | 
|
| 
       252 
252 
     | 
    
         
             
                    attr_reader :attribute_name, :options, :record,
         
     | 
| 
       253 
253 
     | 
    
         
             
                      :failure_message_continuation
         
     | 
| 
       254 
254 
     | 
    
         | 
| 
       255 
     | 
    
         
            -
                    def expectation
         
     | 
| 
      
 255 
     | 
    
         
            +
                    def expectation # rubocop:disable Metrics/MethodLength
         
     | 
| 
       256 
256 
     | 
    
         
             
                      if enum_defined?
         
     | 
| 
       257 
257 
     | 
    
         
             
                        expectation = "#{simple_description} backed by "
         
     | 
| 
       258 
258 
     | 
    
         
             
                        expectation << Shoulda::Matchers::Util.a_or_an(expected_column_type)
         
     | 
| 
         @@ -358,8 +358,8 @@ module Shoulda 
     | 
|
| 
       358 
358 
     | 
    
         
             
                        true
         
     | 
| 
       359 
359 
     | 
    
         
             
                      else
         
     | 
| 
       360 
360 
     | 
    
         
             
                        @failure_message_continuation =
         
     | 
| 
       361 
     | 
    
         
            -
                          "However, #{attribute_name.inspect} is " 
     | 
| 
       362 
     | 
    
         
            -
                          Shoulda::Matchers::Util.a_or_an(column.type) 
     | 
| 
      
 361 
     | 
    
         
            +
                          "However, #{attribute_name.inspect} is "\
         
     | 
| 
      
 362 
     | 
    
         
            +
                          "#{Shoulda::Matchers::Util.a_or_an(column.type)}"\
         
     | 
| 
       363 
363 
     | 
    
         
             
                          ' column'
         
     | 
| 
       364 
364 
     | 
    
         
             
                        false
         
     | 
| 
       365 
365 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -421,9 +421,9 @@ module Shoulda 
     | 
|
| 
       421 
421 
     | 
    
         
             
                    def expected_prefix
         
     | 
| 
       422 
422 
     | 
    
         
             
                      if options.include?(:prefix)
         
     | 
| 
       423 
423 
     | 
    
         
             
                        if options[:prefix] == true
         
     | 
| 
       424 
     | 
    
         
            -
                          attribute_name 
     | 
| 
      
 424 
     | 
    
         
            +
                          attribute_name
         
     | 
| 
       425 
425 
     | 
    
         
             
                        else
         
     | 
| 
       426 
     | 
    
         
            -
                          options[:prefix] 
     | 
| 
      
 426 
     | 
    
         
            +
                          options[:prefix]
         
     | 
| 
       427 
427 
     | 
    
         
             
                        end
         
     | 
| 
       428 
428 
     | 
    
         
             
                      end
         
     | 
| 
       429 
429 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -431,9 +431,9 @@ module Shoulda 
     | 
|
| 
       431 
431 
     | 
    
         
             
                    def expected_suffix
         
     | 
| 
       432 
432 
     | 
    
         
             
                      if options.include?(:suffix)
         
     | 
| 
       433 
433 
     | 
    
         
             
                        if options[:suffix] == true
         
     | 
| 
       434 
     | 
    
         
            -
                          attribute_name 
     | 
| 
      
 434 
     | 
    
         
            +
                          attribute_name
         
     | 
| 
       435 
435 
     | 
    
         
             
                        else
         
     | 
| 
       436 
     | 
    
         
            -
                          options[:suffix] 
     | 
| 
      
 436 
     | 
    
         
            +
                          options[:suffix]
         
     | 
| 
       437 
437 
     | 
    
         
             
                        end
         
     | 
| 
       438 
438 
     | 
    
         
             
                      end
         
     | 
| 
       439 
439 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -0,0 +1,185 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Shoulda
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Matchers
         
     | 
| 
      
 3 
     | 
    
         
            +
                module ActiveRecord
         
     | 
| 
      
 4 
     | 
    
         
            +
                  # The `have_one_attached` matcher tests usage of the
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # `has_one_attached` macro.
         
     | 
| 
      
 6 
     | 
    
         
            +
                  #
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # #### Example
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  #     class User < ApplicationRecord
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #       has_one_attached :avatar
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #     # RSpec
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #     RSpec.describe User, type: :model do
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #       it { should have_one_attached(:avatar) }
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #     # Minitest (Shoulda)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #     class UserTest < ActiveSupport::TestCase
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #       should have_one_attached(:avatar)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @return [HaveAttachedMatcher]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def have_one_attached(name)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    HaveAttachedMatcher.new(:one, name)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  # The `have_many_attached` matcher tests usage of the
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # `has_many_attached` macro.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # #### Example
         
     | 
| 
      
 33 
     | 
    
         
            +
                  #
         
     | 
| 
      
 34 
     | 
    
         
            +
                  #     class Message < ApplicationRecord
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #       has_many_attached :images
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  #
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #     # RSpec
         
     | 
| 
      
 39 
     | 
    
         
            +
                  #     RSpec.describe Message, type: :model do
         
     | 
| 
      
 40 
     | 
    
         
            +
                  #       it { should have_many_attached(:images) }
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #     # Minitest (Shoulda)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #     class MessageTest < ActiveSupport::TestCase
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #       should have_many_attached(:images)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # @return [HaveAttachedMatcher]
         
     | 
| 
      
 49 
     | 
    
         
            +
                  #
         
     | 
| 
      
 50 
     | 
    
         
            +
                  def have_many_attached(name)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    HaveAttachedMatcher.new(:many, name)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  # @private
         
     | 
| 
      
 55 
     | 
    
         
            +
                  class HaveAttachedMatcher
         
     | 
| 
      
 56 
     | 
    
         
            +
                    attr_reader :name
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    def initialize(macro, name)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      @macro = macro
         
     | 
| 
      
 60 
     | 
    
         
            +
                      @name = name
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    def description
         
     | 
| 
      
 64 
     | 
    
         
            +
                      "have a has_#{macro}_attached called #{name}"
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    def failure_message
         
     | 
| 
      
 68 
     | 
    
         
            +
                      <<-MESSAGE
         
     | 
| 
      
 69 
     | 
    
         
            +
            Expected #{expectation}, but this could not be proved.
         
     | 
| 
      
 70 
     | 
    
         
            +
              #{@failure}
         
     | 
| 
      
 71 
     | 
    
         
            +
                      MESSAGE
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    def failure_message_when_negated
         
     | 
| 
      
 75 
     | 
    
         
            +
                      <<-MESSAGE
         
     | 
| 
      
 76 
     | 
    
         
            +
            Did not expect #{expectation}, but it does.
         
     | 
| 
      
 77 
     | 
    
         
            +
                      MESSAGE
         
     | 
| 
      
 78 
     | 
    
         
            +
                    end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                    def expectation
         
     | 
| 
      
 81 
     | 
    
         
            +
                      "#{model_class.name} to #{description}"
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                    def matches?(subject)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      @subject = subject
         
     | 
| 
      
 86 
     | 
    
         
            +
                      reader_attribute_exists? &&
         
     | 
| 
      
 87 
     | 
    
         
            +
                        writer_attribute_exists? &&
         
     | 
| 
      
 88 
     | 
    
         
            +
                        attachments_association_exists? &&
         
     | 
| 
      
 89 
     | 
    
         
            +
                        blobs_association_exists? &&
         
     | 
| 
      
 90 
     | 
    
         
            +
                        eager_loading_scope_exists?
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                    private
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    attr_reader :subject, :macro
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                    def reader_attribute_exists?
         
     | 
| 
      
 98 
     | 
    
         
            +
                      if subject.respond_to?(name)
         
     | 
| 
      
 99 
     | 
    
         
            +
                        true
         
     | 
| 
      
 100 
     | 
    
         
            +
                      else
         
     | 
| 
      
 101 
     | 
    
         
            +
                        @failure = "#{model_class.name} does not have a :#{name} method."
         
     | 
| 
      
 102 
     | 
    
         
            +
                        false
         
     | 
| 
      
 103 
     | 
    
         
            +
                      end
         
     | 
| 
      
 104 
     | 
    
         
            +
                    end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    def writer_attribute_exists?
         
     | 
| 
      
 107 
     | 
    
         
            +
                      if subject.respond_to?("#{name}=")
         
     | 
| 
      
 108 
     | 
    
         
            +
                        true
         
     | 
| 
      
 109 
     | 
    
         
            +
                      else
         
     | 
| 
      
 110 
     | 
    
         
            +
                        @failure = "#{model_class.name} does not have a :#{name}= method."
         
     | 
| 
      
 111 
     | 
    
         
            +
                        false
         
     | 
| 
      
 112 
     | 
    
         
            +
                      end
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    def attachments_association_exists?
         
     | 
| 
      
 116 
     | 
    
         
            +
                      if attachments_association_matcher.matches?(subject)
         
     | 
| 
      
 117 
     | 
    
         
            +
                        true
         
     | 
| 
      
 118 
     | 
    
         
            +
                      else
         
     | 
| 
      
 119 
     | 
    
         
            +
                        @failure = attachments_association_matcher.failure_message
         
     | 
| 
      
 120 
     | 
    
         
            +
                        false
         
     | 
| 
      
 121 
     | 
    
         
            +
                      end
         
     | 
| 
      
 122 
     | 
    
         
            +
                    end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    def attachments_association_matcher
         
     | 
| 
      
 125 
     | 
    
         
            +
                      @_attachments_association_matcher ||=
         
     | 
| 
      
 126 
     | 
    
         
            +
                        AssociationMatcher.new(
         
     | 
| 
      
 127 
     | 
    
         
            +
                          :"has_#{macro}",
         
     | 
| 
      
 128 
     | 
    
         
            +
                          attachments_association_name,
         
     | 
| 
      
 129 
     | 
    
         
            +
                        ).
         
     | 
| 
      
 130 
     | 
    
         
            +
                          conditions(name: name).
         
     | 
| 
      
 131 
     | 
    
         
            +
                          class_name('ActiveStorage::Attachment').
         
     | 
| 
      
 132 
     | 
    
         
            +
                          inverse_of(:record)
         
     | 
| 
      
 133 
     | 
    
         
            +
                    end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                    def attachments_association_name
         
     | 
| 
      
 136 
     | 
    
         
            +
                      case macro
         
     | 
| 
      
 137 
     | 
    
         
            +
                      when :one then "#{name}_attachment"
         
     | 
| 
      
 138 
     | 
    
         
            +
                      when :many then "#{name}_attachments"
         
     | 
| 
      
 139 
     | 
    
         
            +
                      end
         
     | 
| 
      
 140 
     | 
    
         
            +
                    end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                    def blobs_association_exists?
         
     | 
| 
      
 143 
     | 
    
         
            +
                      if blobs_association_matcher.matches?(subject)
         
     | 
| 
      
 144 
     | 
    
         
            +
                        true
         
     | 
| 
      
 145 
     | 
    
         
            +
                      else
         
     | 
| 
      
 146 
     | 
    
         
            +
                        @failure = blobs_association_matcher.failure_message
         
     | 
| 
      
 147 
     | 
    
         
            +
                        false
         
     | 
| 
      
 148 
     | 
    
         
            +
                      end
         
     | 
| 
      
 149 
     | 
    
         
            +
                    end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                    def blobs_association_matcher
         
     | 
| 
      
 152 
     | 
    
         
            +
                      @_blobs_association_matcher ||=
         
     | 
| 
      
 153 
     | 
    
         
            +
                        AssociationMatcher.new(
         
     | 
| 
      
 154 
     | 
    
         
            +
                          :"has_#{macro}",
         
     | 
| 
      
 155 
     | 
    
         
            +
                          blobs_association_name,
         
     | 
| 
      
 156 
     | 
    
         
            +
                        ).
         
     | 
| 
      
 157 
     | 
    
         
            +
                          through(attachments_association_name).
         
     | 
| 
      
 158 
     | 
    
         
            +
                          class_name('ActiveStorage::Blob').
         
     | 
| 
      
 159 
     | 
    
         
            +
                          source(:blob)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                    def blobs_association_name
         
     | 
| 
      
 163 
     | 
    
         
            +
                      case macro
         
     | 
| 
      
 164 
     | 
    
         
            +
                      when :one then "#{name}_blob"
         
     | 
| 
      
 165 
     | 
    
         
            +
                      when :many then "#{name}_blobs"
         
     | 
| 
      
 166 
     | 
    
         
            +
                      end
         
     | 
| 
      
 167 
     | 
    
         
            +
                    end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                    def eager_loading_scope_exists?
         
     | 
| 
      
 170 
     | 
    
         
            +
                      if model_class.respond_to?("with_attached_#{name}")
         
     | 
| 
      
 171 
     | 
    
         
            +
                        true
         
     | 
| 
      
 172 
     | 
    
         
            +
                      else
         
     | 
| 
      
 173 
     | 
    
         
            +
                        @failure = "#{model_class.name} does not have a " \
         
     | 
| 
      
 174 
     | 
    
         
            +
                                   ":with_attached_#{name} scope."
         
     | 
| 
      
 175 
     | 
    
         
            +
                        false
         
     | 
| 
      
 176 
     | 
    
         
            +
                      end
         
     | 
| 
      
 177 
     | 
    
         
            +
                    end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                    def model_class
         
     | 
| 
      
 180 
     | 
    
         
            +
                      subject.class
         
     | 
| 
      
 181 
     | 
    
         
            +
                    end
         
     | 
| 
      
 182 
     | 
    
         
            +
                  end
         
     | 
| 
      
 183 
     | 
    
         
            +
                end
         
     | 
| 
      
 184 
     | 
    
         
            +
              end
         
     | 
| 
      
 185 
     | 
    
         
            +
            end
         
     |