ultra-fast-box 0.0.1

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.
Files changed (140) hide show
  1. checksums.yaml +7 -0
  2. data/shoulda-matchers-8.0.1/LICENSE +22 -0
  3. data/shoulda-matchers-8.0.1/README.md +567 -0
  4. data/shoulda-matchers-8.0.1/docs/errors/NonCaseSwappableValueError.md +111 -0
  5. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/callback_matcher.rb +121 -0
  6. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +70 -0
  7. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/flash_store.rb +86 -0
  8. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/permit_matcher.rb +445 -0
  9. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +96 -0
  10. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_template_matcher.rb +84 -0
  11. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +145 -0
  12. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +115 -0
  13. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +148 -0
  14. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_matcher.rb +207 -0
  15. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_params.rb +62 -0
  16. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  17. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +228 -0
  18. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_matcher.rb +163 -0
  19. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +107 -0
  20. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller.rb +24 -0
  21. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
  22. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
  23. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +232 -0
  24. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
  25. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
  26. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
  27. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
  28. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
  29. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher.rb +695 -0
  30. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
  31. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +78 -0
  32. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/errors.rb +52 -0
  33. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +125 -0
  34. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/helpers.rb +37 -0
  35. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +45 -0
  36. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +67 -0
  37. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +45 -0
  38. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +44 -0
  39. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
  40. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
  41. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
  42. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  43. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
  44. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
  45. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
  46. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers.rb +14 -0
  47. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +180 -0
  48. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +128 -0
  49. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +567 -0
  50. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +185 -0
  51. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +272 -0
  52. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +664 -0
  53. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +575 -0
  54. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +711 -0
  55. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +428 -0
  56. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +62 -0
  57. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher.rb +195 -0
  58. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_message_finder.rb +63 -0
  59. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validator.rb +127 -0
  60. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model.rb +96 -0
  61. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +217 -0
  62. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matcher.rb +2134 -0
  63. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +74 -0
  64. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +62 -0
  65. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
  66. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +105 -0
  67. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +101 -0
  68. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +89 -0
  69. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +142 -0
  70. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +71 -0
  71. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
  72. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +76 -0
  73. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +42 -0
  74. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +65 -0
  75. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
  76. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +784 -0
  77. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
  78. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_attached_matcher.rb +343 -0
  79. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +366 -0
  80. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +287 -0
  81. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +104 -0
  82. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +95 -0
  83. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
  84. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
  85. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
  86. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/serialize_matcher.rb +203 -0
  87. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/model.rb +58 -0
  88. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +39 -0
  89. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +49 -0
  90. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +55 -0
  91. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness.rb +14 -0
  92. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +1197 -0
  93. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record.rb +37 -0
  94. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/configuration.rb +31 -0
  95. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double.rb +103 -0
  96. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_collection.rb +60 -0
  97. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +31 -0
  98. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/method_call.rb +44 -0
  99. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/object_double.rb +43 -0
  100. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +32 -0
  101. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/stub_implementation.rb +31 -0
  102. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/world.rb +63 -0
  103. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak.rb +38 -0
  104. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/error.rb +29 -0
  105. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error.rb +15 -0
  106. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher.rb +540 -0
  107. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent.rb +11 -0
  108. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration.rb +70 -0
  109. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  110. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  111. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/action_controller.rb +33 -0
  112. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  113. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  114. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  115. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/rails.rb +31 -0
  116. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/routing.rb +29 -0
  117. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries.rb +16 -0
  118. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/rails.rb +12 -0
  119. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/registry.rb +28 -0
  120. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  121. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  122. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  123. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  124. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  125. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  126. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  127. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations.rb +43 -0
  128. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_collection.rb +99 -0
  129. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_context.rb +36 -0
  130. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/rails_shim.rb +190 -0
  131. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/routing.rb +10 -0
  132. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util/word_wrap.rb +211 -0
  133. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util.rb +110 -0
  134. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/version.rb +6 -0
  135. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/warn.rb +28 -0
  136. data/shoulda-matchers-8.0.1/lib/shoulda/matchers.rb +25 -0
  137. data/shoulda-matchers-8.0.1/lib/shoulda-matchers.rb +1 -0
  138. data/shoulda-matchers-8.0.1/shoulda-matchers.gemspec +43 -0
  139. data/ultra-fast-box.gemspec +12 -0
  140. metadata +179 -0
@@ -0,0 +1,74 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ module AssociationMatchers
5
+ # @private
6
+ class CounterCacheMatcher
7
+ attr_accessor :missing_option
8
+
9
+ def initialize(counter_cache, name)
10
+ @counter_cache = counter_cache
11
+ @name = name
12
+ @missing_option = ''
13
+ end
14
+
15
+ def description
16
+ "counter_cache => #{counter_cache}"
17
+ end
18
+
19
+ def matches?(subject)
20
+ self.subject = ModelReflector.new(subject, name)
21
+
22
+ if correct_value?
23
+ true
24
+ else
25
+ self.missing_option = "#{name} should have #{description}"
26
+ false
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ attr_accessor :subject, :counter_cache, :name
33
+
34
+ def correct_value?
35
+ expected = normalize_value
36
+
37
+ if expected.is_a?(Hash)
38
+ option_verifier.correct_for_hash?(
39
+ :counter_cache,
40
+ expected,
41
+ )
42
+ else
43
+ option_verifier.correct_for_string?(
44
+ :counter_cache,
45
+ expected,
46
+ )
47
+ end
48
+ end
49
+
50
+ def option_verifier
51
+ @_option_verifier ||= OptionVerifier.new(subject)
52
+ end
53
+
54
+ def normalize_value
55
+ if Rails::VERSION::STRING >= '7.2'
56
+ case counter_cache
57
+ when true
58
+ { active: true, column: nil }
59
+ when String, Symbol
60
+ { active: true, column: counter_cache.to_s }
61
+ when Hash
62
+ { active: true, column: nil }.merge!(counter_cache)
63
+ else
64
+ raise ArgumentError, 'Invalid counter_cache option'
65
+ end
66
+ else
67
+ counter_cache
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,62 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ module AssociationMatchers
5
+ # @private
6
+ class DependentMatcher
7
+ attr_accessor :missing_option
8
+
9
+ def initialize(dependent, name)
10
+ @dependent = dependent
11
+ @name = name
12
+ @missing_option = ''
13
+ end
14
+
15
+ def description
16
+ "dependent => #{dependent}"
17
+ end
18
+
19
+ def matches?(subject)
20
+ self.subject = ModelReflector.new(subject, name)
21
+
22
+ if option_matches?
23
+ true
24
+ else
25
+ self.missing_option = generate_missing_option
26
+ false
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ attr_accessor :subject, :dependent, :name
33
+
34
+ private
35
+
36
+ def option_verifier
37
+ @_option_verifier ||= OptionVerifier.new(subject)
38
+ end
39
+
40
+ def option_matches?
41
+ option_verifier.correct_for?(option_type, :dependent, dependent)
42
+ end
43
+
44
+ def option_type
45
+ case dependent
46
+ when true, false then :boolean
47
+ else :string
48
+ end
49
+ end
50
+
51
+ def generate_missing_option
52
+ [
53
+ "#{name} should have",
54
+ (dependent == true ? 'a' : dependent),
55
+ 'dependency',
56
+ ].join(' ')
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,41 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ module AssociationMatchers
5
+ # @private
6
+ class InverseOfMatcher
7
+ attr_accessor :missing_option
8
+
9
+ def initialize(inverse_of, name)
10
+ @inverse_of = inverse_of
11
+ @name = name
12
+ @missing_option = ''
13
+ end
14
+
15
+ def description
16
+ "inverse_of => #{inverse_of}"
17
+ end
18
+
19
+ def matches?(subject)
20
+ self.subject = ModelReflector.new(subject, name)
21
+
22
+ if option_verifier.correct_for_string?(:inverse_of, inverse_of)
23
+ true
24
+ else
25
+ self.missing_option = "#{name} should have #{description}"
26
+ false
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ attr_accessor :subject, :inverse_of, :name
33
+
34
+ def option_verifier
35
+ @_option_verifier ||= OptionVerifier.new(subject)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,105 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ module AssociationMatchers
5
+ # @private
6
+ class JoinTableMatcher
7
+ attr_reader :failure_message
8
+
9
+ alias :missing_option :failure_message
10
+
11
+ delegate :model_class, :join_table_name, :associated_class, :options,
12
+ :name, :option_verifier, to: :association_matcher
13
+
14
+ delegate :connection, to: :model_class
15
+
16
+ def initialize(association_matcher, reflector)
17
+ @association_matcher = association_matcher
18
+ @reflector = reflector
19
+ end
20
+
21
+ def matches?(_subject)
22
+ join_table_option_correct? &&
23
+ join_table_exists? &&
24
+ join_table_has_correct_columns?
25
+ end
26
+
27
+ def join_table_option_correct?
28
+ if options.key?(:join_table_name)
29
+ if option_verifier.correct_for_string?(
30
+ :join_table,
31
+ options[:join_table_name],
32
+ )
33
+ true
34
+ else
35
+ @failure_message = "#{name} should use"\
36
+ " #{options[:join_table_name].inspect} for :join_table option"
37
+ false
38
+ end
39
+ else
40
+ true
41
+ end
42
+ end
43
+
44
+ def join_table_exists?
45
+ if connection.data_sources.
46
+ include?(join_table_name.to_s)
47
+ true
48
+ else
49
+ @failure_message = missing_table_message
50
+ false
51
+ end
52
+ end
53
+
54
+ def join_table_has_correct_columns?
55
+ if missing_columns.empty?
56
+ true
57
+ else
58
+ @failure_message = missing_columns_message
59
+ false
60
+ end
61
+ end
62
+
63
+ protected
64
+
65
+ attr_reader :association_matcher, :reflector
66
+
67
+ private
68
+
69
+ delegate :foreign_key, :association_foreign_key, to: :reflector
70
+
71
+ def missing_columns
72
+ @_missing_columns ||= expected_join_table_columns.reject do |key|
73
+ actual_join_table_columns.include?(key.to_s)
74
+ end
75
+ end
76
+
77
+ def expected_join_table_columns
78
+ [foreign_key, association_foreign_key]
79
+ end
80
+
81
+ def actual_join_table_columns
82
+ connection.columns(join_table_name).map(&:name)
83
+ end
84
+
85
+ def missing_table_message
86
+ "join table #{join_table_name} doesn't exist"
87
+ end
88
+
89
+ def missing_columns_message
90
+ missing = missing_columns.join(', ')
91
+ "join table #{join_table_name} missing #{column_label}: #{missing}"
92
+ end
93
+
94
+ def column_label
95
+ if missing_columns.length > 1
96
+ 'columns'
97
+ else
98
+ 'column'
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,101 @@
1
+ require 'delegate'
2
+
3
+ module Shoulda
4
+ module Matchers
5
+ module ActiveRecord
6
+ module AssociationMatchers
7
+ # @private
8
+ class ModelReflection < SimpleDelegator
9
+ def initialize(reflection)
10
+ super(reflection)
11
+ @reflection = reflection
12
+ @subject = reflection.active_record
13
+ end
14
+
15
+ def associated_class
16
+ if polymorphic?
17
+ subject
18
+ else
19
+ reflection.klass
20
+ end
21
+ end
22
+
23
+ def polymorphic?
24
+ reflection.options[:polymorphic]
25
+ end
26
+
27
+ def through?
28
+ reflection.options[:through]
29
+ end
30
+
31
+ def join_table_name
32
+ join_table_name =
33
+ has_and_belongs_to_many_name_table_name || reflection.join_table
34
+ join_table_name.to_s
35
+ end
36
+
37
+ def association_relation(related_instance)
38
+ relation = associated_class.all
39
+
40
+ if reflection.scope
41
+ # Source: AR::Associations::AssociationScope#eval_scope
42
+ relation.instance_exec(related_instance, &reflection.scope)
43
+ else
44
+ relation
45
+ end
46
+ end
47
+
48
+ def foreign_key
49
+ if has_and_belongs_to_many_reflection
50
+ has_and_belongs_to_many_reflection.foreign_key
51
+ elsif reflection.respond_to?(:foreign_key)
52
+ reflection.foreign_key
53
+ else
54
+ reflection.primary_key_name
55
+ end
56
+ end
57
+
58
+ def association_foreign_key
59
+ if has_and_belongs_to_many_reflection
60
+ join_model = has_and_belongs_to_many_reflection.options[:class]
61
+ join_model.right_reflection.foreign_key
62
+ else
63
+ reflection.association_foreign_key
64
+ end
65
+ end
66
+
67
+ def validate_inverse_of_through_association!
68
+ if through?
69
+ reflection.check_validity!
70
+ end
71
+ end
72
+
73
+ def has_and_belongs_to_many_name
74
+ reflection.options[:through]
75
+ end
76
+
77
+ def strict_loading?
78
+ reflection.options.fetch(:strict_loading, subject.strict_loading_by_default)
79
+ end
80
+
81
+ protected
82
+
83
+ attr_reader :reflection, :subject
84
+
85
+ private
86
+
87
+ def has_and_belongs_to_many_name_table_name
88
+ has_and_belongs_to_many_reflection&.table_name
89
+ end
90
+
91
+ def has_and_belongs_to_many_reflection
92
+ @_has_and_belongs_to_many_reflection ||=
93
+ if has_and_belongs_to_many_name
94
+ @subject.reflect_on_association(has_and_belongs_to_many_name)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,89 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ module AssociationMatchers
5
+ # @private
6
+ class ModelReflector
7
+ delegate(
8
+ :associated_class,
9
+ :association_foreign_key,
10
+ :foreign_key,
11
+ :foreign_type,
12
+ :has_and_belongs_to_many_name,
13
+ :join_table_name,
14
+ :polymorphic?,
15
+ :validate_inverse_of_through_association!,
16
+ to: :reflection,
17
+ )
18
+
19
+ delegate(
20
+ :through?,
21
+ to: :reflection,
22
+ allow_nil: true,
23
+ )
24
+
25
+ def initialize(subject, name)
26
+ @subject = subject
27
+ @name = name
28
+ end
29
+
30
+ def association_relation
31
+ reflection.association_relation(subject)
32
+ end
33
+
34
+ def reflection
35
+ @_reflection ||= reflect_on_association(name)
36
+ end
37
+
38
+ def reflect_on_association(name)
39
+ reflection = model_class.reflect_on_association(name)
40
+
41
+ if reflection
42
+ ModelReflection.new(reflection)
43
+ end
44
+ end
45
+
46
+ def model_class
47
+ subject.class
48
+ end
49
+
50
+ def build_relation_with_clause(name, value)
51
+ case name
52
+ when :conditions
53
+ associated_class.where(value)
54
+ when :order
55
+ associated_class.order(value)
56
+ else
57
+ raise ArgumentError, "Unknown clause '#{name}'"
58
+ end
59
+ end
60
+
61
+ def extract_relation_clause_from(relation, name)
62
+ case name
63
+ when :conditions
64
+ relation.where_values_hash
65
+ when :order
66
+ relation.order_values.map do |value|
67
+ value_as_sql(value)
68
+ end.join(', ')
69
+ else
70
+ raise ArgumentError, "Unknown clause '#{name}'"
71
+ end
72
+ end
73
+
74
+ protected
75
+
76
+ attr_reader :subject, :name
77
+
78
+ def value_as_sql(value)
79
+ if value.respond_to?(:to_sql)
80
+ value.to_sql
81
+ else
82
+ value
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,142 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ module AssociationMatchers
5
+ # @private
6
+ class OptionVerifier
7
+ delegate :reflection, to: :reflector
8
+
9
+ DEFAULT_VALUE_OF_OPTIONS = {
10
+ has_many: {
11
+ validate: true,
12
+ },
13
+ }.freeze
14
+ RELATION_OPTIONS = [:conditions, :order].freeze
15
+
16
+ def initialize(reflector)
17
+ @reflector = reflector
18
+ end
19
+
20
+ def correct_for_string?(name, expected_value)
21
+ correct_for?(:string, name, expected_value)
22
+ end
23
+
24
+ def correct_for_boolean?(name, expected_value)
25
+ correct_for?(:boolean, name, expected_value)
26
+ end
27
+
28
+ def correct_for_hash?(name, expected_value)
29
+ correct_for?(:hash, name, expected_value)
30
+ end
31
+
32
+ def correct_for_constant?(name, expected_unresolved_value)
33
+ correct_for?(:constant, name, expected_unresolved_value)
34
+ end
35
+
36
+ def correct_for_relation_clause?(name, expected_value)
37
+ correct_for?(:relation_clause, name, expected_value)
38
+ end
39
+
40
+ def correct_for?(*args)
41
+ expected_value, name, type = args.reverse
42
+
43
+ if expected_value.nil?
44
+ true
45
+ else
46
+ type_cast_expected_value = type_cast(
47
+ type,
48
+ expected_value_for(type, name, expected_value),
49
+ )
50
+ actual_value = type_cast(type, actual_value_for(name))
51
+ type_cast_expected_value == actual_value
52
+ end
53
+ end
54
+
55
+ def actual_value_for(name)
56
+ if RELATION_OPTIONS.include?(name)
57
+ actual_value_for_relation_clause(name)
58
+ else
59
+ method_name = "actual_value_for_#{name}"
60
+ if respond_to?(method_name, true)
61
+ __send__(method_name)
62
+ else
63
+ actual_value_for_option(name)
64
+ end
65
+ end
66
+ end
67
+
68
+ protected
69
+
70
+ attr_reader :reflector
71
+
72
+ def type_cast(type, value)
73
+ case type
74
+ when :string, :relation_clause
75
+ value.to_s
76
+ when :boolean
77
+ !!value
78
+ when :hash
79
+ Hash(value).stringify_keys
80
+ else
81
+ value
82
+ end
83
+ end
84
+
85
+ def expected_value_for(type, name, value)
86
+ if RELATION_OPTIONS.include?(name)
87
+ expected_value_for_relation_clause(name, value)
88
+ elsif type == :constant
89
+ expected_value_for_constant(value)
90
+ else
91
+ value
92
+ end
93
+ end
94
+
95
+ def expected_value_for_relation_clause(name, value)
96
+ relation = reflector.build_relation_with_clause(name, value)
97
+ reflector.extract_relation_clause_from(relation, name)
98
+ end
99
+
100
+ def expected_value_for_constant(name)
101
+ namespace = Shoulda::Matchers::Util.deconstantize(
102
+ reflector.model_class.to_s,
103
+ )
104
+
105
+ ["#{namespace}::#{name}", name].each do |path|
106
+ constant = Shoulda::Matchers::Util.safe_constantize(path)
107
+
108
+ if constant
109
+ return constant
110
+ end
111
+ end
112
+ end
113
+
114
+ def actual_value_for_relation_clause(name)
115
+ reflector.extract_relation_clause_from(
116
+ reflector.association_relation,
117
+ name,
118
+ )
119
+ end
120
+
121
+ def actual_value_for_class_name
122
+ reflector.associated_class
123
+ end
124
+
125
+ def actual_value_for_strict_loading
126
+ reflection.strict_loading?
127
+ end
128
+
129
+ def actual_value_for_option(name)
130
+ option_value = reflection.options[name]
131
+
132
+ if option_value.nil?
133
+ DEFAULT_VALUE_OF_OPTIONS.dig(reflection.macro, name)
134
+ else
135
+ option_value
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,71 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ module AssociationMatchers
5
+ # @private
6
+ class OptionalMatcher
7
+ attr_reader :missing_option
8
+
9
+ def initialize(attribute_name, optional)
10
+ @attribute_name = attribute_name
11
+ @optional = optional
12
+ @submatcher = ActiveModel::AllowValueMatcher.new(nil).
13
+ for(attribute_name)
14
+ @missing_option = ''
15
+ end
16
+
17
+ def description
18
+ "optional: #{optional}"
19
+ end
20
+
21
+ def matches?(subject)
22
+ if submatcher_passes?(subject)
23
+ true
24
+ else
25
+ @missing_option = build_missing_option
26
+
27
+ false
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :attribute_name, :optional, :submatcher
34
+
35
+ def submatcher_passes?(subject)
36
+ if optional
37
+ submatcher.matches?(subject)
38
+ else
39
+ submatcher.does_not_match?(subject)
40
+ end
41
+ end
42
+
43
+ def build_missing_option
44
+ String.new('and for the record ').tap do |missing_option_string|
45
+ missing_option_string <<
46
+ if optional
47
+ 'not to '
48
+ else
49
+ 'to '
50
+ end
51
+
52
+ missing_option_string << 'fail validation if '\
53
+ ":#{attribute_name} is unset; i.e., either the association "\
54
+ 'should have been defined with `optional: '\
55
+ "#{optional.inspect}`, or there "
56
+
57
+ missing_option_string <<
58
+ if optional
59
+ 'should not '
60
+ else
61
+ 'should '
62
+ end
63
+
64
+ missing_option_string << "be a presence validation on :#{attribute_name}"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end