shoulda-matchers 3.0.0.rc1 → 3.0.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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -3
  3. data/Gemfile.lock +12 -41
  4. data/NEWS.md +118 -26
  5. data/README.md +34 -11
  6. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +0 -0
  7. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +4 -0
  8. data/gemfiles/4.0.0.gemfile +2 -3
  9. data/gemfiles/4.0.0.gemfile.lock +47 -77
  10. data/gemfiles/4.0.1.gemfile +2 -3
  11. data/gemfiles/4.0.1.gemfile.lock +51 -79
  12. data/gemfiles/4.1.gemfile +2 -3
  13. data/gemfiles/4.1.gemfile.lock +73 -103
  14. data/gemfiles/4.2.gemfile +2 -3
  15. data/gemfiles/4.2.gemfile.lock +90 -124
  16. data/lib/shoulda/matchers.rb +1 -0
  17. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +6 -8
  18. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +1 -3
  19. data/lib/shoulda/matchers/action_controller/flash_store.rb +1 -8
  20. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +140 -88
  21. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +2 -5
  22. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +5 -10
  23. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +2 -4
  24. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -3
  25. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +3 -5
  26. data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -7
  27. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +35 -9
  28. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +3 -3
  29. data/lib/shoulda/matchers/active_model.rb +57 -1
  30. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +2 -5
  31. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +162 -54
  32. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +5 -2
  33. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +1 -3
  34. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +24 -11
  35. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +4 -3
  36. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +0 -2
  37. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +4 -3
  38. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +2 -1
  39. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +15 -13
  40. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +3 -3
  41. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +3 -3
  42. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +4 -4
  43. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +8 -8
  44. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +8 -8
  45. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +12 -14
  46. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +10 -4
  47. data/lib/shoulda/matchers/active_model/validation_matcher.rb +0 -3
  48. data/lib/shoulda/matchers/active_model/validator.rb +0 -8
  49. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +4 -6
  50. data/lib/shoulda/matchers/active_record/association_matcher.rb +58 -43
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -2
  52. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +3 -5
  53. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +3 -5
  54. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -4
  55. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +3 -5
  56. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +7 -7
  57. data/lib/shoulda/matchers/doublespeak/double.rb +10 -1
  58. data/lib/shoulda/matchers/doublespeak/double_collection.rb +13 -5
  59. data/lib/shoulda/matchers/doublespeak/method_call.rb +10 -1
  60. data/lib/shoulda/matchers/doublespeak/object_double.rb +2 -1
  61. data/lib/shoulda/matchers/doublespeak/world.rb +10 -0
  62. data/lib/shoulda/matchers/error.rb +4 -0
  63. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +11 -10
  64. data/lib/shoulda/matchers/integrations/libraries.rb +1 -0
  65. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
  66. data/lib/shoulda/matchers/integrations/libraries/active_model.rb +1 -1
  67. data/lib/shoulda/matchers/integrations/libraries/active_record.rb +1 -1
  68. data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -1
  69. data/lib/shoulda/matchers/integrations/libraries/routing.rb +27 -0
  70. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
  71. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
  72. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
  73. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
  74. data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +2 -2
  75. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
  76. data/lib/shoulda/matchers/routing.rb +10 -0
  77. data/lib/shoulda/matchers/version.rb +1 -1
  78. data/script/SUPPORTED_VERSIONS +1 -1
  79. data/spec/acceptance/independent_matchers_spec.rb +103 -42
  80. data/spec/doublespeak_spec_helper.rb +5 -1
  81. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +34 -11
  82. data/spec/support/acceptance/helpers/rspec_helpers.rb +9 -13
  83. data/spec/support/acceptance/helpers/step_helpers.rb +13 -0
  84. data/spec/support/acceptance/matchers/have_output.rb +1 -1
  85. data/spec/support/acceptance/matchers/indicate_number_of_tests_was_run_matcher.rb +1 -1
  86. data/spec/support/tests/command_runner.rb +5 -1
  87. data/spec/support/unit/helpers/active_record_versions.rb +0 -4
  88. data/spec/support/unit/shared_examples/set_session_or_flash.rb +8 -3
  89. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +198 -39
  90. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +269 -102
  91. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +24 -0
  92. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +118 -101
  93. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +0 -82
  94. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +148 -121
  95. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +20 -8
  96. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +64 -183
  97. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +14 -0
  98. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +60 -0
  99. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +23 -7
  100. data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +242 -0
  101. data/spec/unit_spec_helper.rb +4 -0
  102. data/tasks/documentation.rb +35 -0
  103. metadata +9 -8
  104. data/Guardfile +0 -5
  105. data/cucumber.yml +0 -1
  106. data/lib/shoulda/matchers/active_model/validator_with_captured_range_error.rb +0 -12
@@ -12,6 +12,7 @@ require 'shoulda/matchers/warn'
12
12
  require 'shoulda/matchers/action_controller'
13
13
  require 'shoulda/matchers/active_model'
14
14
  require 'shoulda/matchers/active_record'
15
+ require 'shoulda/matchers/routing'
15
16
 
16
17
  module Shoulda
17
18
  module Matchers
@@ -14,7 +14,7 @@ module Shoulda
14
14
  # it { should_not use_before_filter(:prevent_ssl) }
15
15
  # end
16
16
  #
17
- # # Test::Unit
17
+ # # Minitest (Shoulda)
18
18
  # class UsersControllerTest < ActionController::TestCase
19
19
  # should use_before_filter(:authenticate_user!)
20
20
  # should_not use_before_filter(:prevent_ssl)
@@ -39,7 +39,7 @@ module Shoulda
39
39
  # it { should_not use_after_filter(:destroy_user) }
40
40
  # end
41
41
  #
42
- # # Test::Unit
42
+ # # Minitest (Shoulda)
43
43
  # class IssuesControllerTest < ActionController::TestCase
44
44
  # should use_after_filter(:log_activity)
45
45
  # should_not use_after_filter(:destroy_user)
@@ -64,7 +64,7 @@ module Shoulda
64
64
  # it { should_not use_before_action(:prevent_ssl) }
65
65
  # end
66
66
  #
67
- # # Test::Unit
67
+ # # Minitest (Shoulda)
68
68
  # class UsersControllerTest < ActionController::TestCase
69
69
  # should use_before_action(:authenticate_user!)
70
70
  # should_not use_before_action(:prevent_ssl)
@@ -89,7 +89,7 @@ module Shoulda
89
89
  # it { should_not use_after_action(:destroy_user) }
90
90
  # end
91
91
  #
92
- # # Test::Unit
92
+ # # Minitest (Shoulda)
93
93
  # class IssuesControllerTest < ActionController::TestCase
94
94
  # should use_after_action(:log_activity)
95
95
  # should_not use_after_action(:destroy_user)
@@ -114,7 +114,7 @@ module Shoulda
114
114
  # it { should_not use_around_filter(:save_view_context) }
115
115
  # end
116
116
  #
117
- # # Test::Unit
117
+ # # Minitest (Shoulda)
118
118
  # class ChangesControllerTest < ActionController::TestCase
119
119
  # should use_around_filter(:wrap_in_transaction)
120
120
  # should_not use_around_filter(:save_view_context)
@@ -139,7 +139,7 @@ module Shoulda
139
139
  # it { should_not use_around_action(:save_view_context) }
140
140
  # end
141
141
  #
142
- # # Test::Unit
142
+ # # Minitest (Shoulda)
143
143
  # class ChangesControllerTest < ActionController::TestCase
144
144
  # should use_around_action(:wrap_in_transaction)
145
145
  # should_not use_around_action(:save_view_context)
@@ -169,12 +169,10 @@ module Shoulda
169
169
  def failure_message
170
170
  "Expected that #{controller_class.name} would have :#{method_name} as a #{kind}_#{callback_type}"
171
171
  end
172
- alias failure_message_for_should failure_message
173
172
 
174
173
  def failure_message_when_negated
175
174
  "Expected that #{controller_class.name} would not have :#{method_name} as a #{kind}_#{callback_type}"
176
175
  end
177
- alias failure_message_for_should_not failure_message_when_negated
178
176
 
179
177
  def description
180
178
  "have :#{method_name} as a #{kind}_#{callback_type}"
@@ -14,7 +14,7 @@ module Shoulda
14
14
  # it { should filter_param(:secret_key) }
15
15
  # end
16
16
  #
17
- # # Test::Unit
17
+ # # Minitest (Shoulda)
18
18
  # class ApplicationControllerTest < ActionController::TestCase
19
19
  # should filter_param(:secret_key)
20
20
  # end
@@ -38,12 +38,10 @@ module Shoulda
38
38
  def failure_message
39
39
  "Expected #{@key} to be filtered; filtered keys: #{filtered_keys.join(', ')}"
40
40
  end
41
- alias failure_message_for_should failure_message
42
41
 
43
42
  def failure_message_when_negated
44
43
  "Did not expect #{@key} to be filtered"
45
44
  end
46
- alias failure_message_for_should_not failure_message_when_negated
47
45
 
48
46
  def description
49
47
  "filter #{@key}"
@@ -26,7 +26,7 @@ module Shoulda
26
26
  end
27
27
 
28
28
  def has_key?(key)
29
- values_to_check.include?(key)
29
+ values_to_check.include?(key.to_s)
30
30
  end
31
31
 
32
32
  def has_value?(expected_value)
@@ -54,7 +54,6 @@ module Shoulda
54
54
  controller.flash.dup.tap do |flash|
55
55
  copy_flashes(controller.flash, flash)
56
56
  copy_discard_if_necessary(controller.flash, flash)
57
- # sweep_flash_if_necessary(flash)
58
57
  end
59
58
  end
60
59
 
@@ -68,12 +67,6 @@ module Shoulda
68
67
  new_flash.instance_variable_set('@discard', discard)
69
68
  end
70
69
 
71
- def sweep_flash_if_necessary(flash)
72
- unless @use_now
73
- flash.sweep
74
- end
75
- end
76
-
77
70
  def set_values
78
71
  flash.instance_variable_get('@flashes')
79
72
  end
@@ -37,17 +37,35 @@ module Shoulda
37
37
  # # RSpec
38
38
  # describe UsersController do
39
39
  # it do
40
+ # params = {
41
+ # user: {
42
+ # first_name: 'John',
43
+ # last_name: 'Doe',
44
+ # email: 'johndoe@example.com',
45
+ # password: 'password'
46
+ # }
47
+ # }
40
48
  # should permit(:first_name, :last_name, :email, :password).
41
- # for(:create).
49
+ # for(:create, params: params).
42
50
  # on(:user)
43
51
  # end
44
52
  # end
45
53
  #
46
- # # Test::Unit
54
+ # # Minitest (Shoulda)
47
55
  # class UsersControllerTest < ActionController::TestCase
48
- # should permit(:first_name, :last_name, :email, :password).
49
- # for(:create).
50
- # on(:user)
56
+ # should "(for POST #create) restrict parameters on :user to first_name, last_name, email, and password" do
57
+ # params = {
58
+ # user: {
59
+ # first_name: 'John',
60
+ # last_name: 'Doe',
61
+ # email: 'johndoe@example.com',
62
+ # password: 'password'
63
+ # }
64
+ # }
65
+ # should permit(:first_name, :last_name, :email, :password).
66
+ # for(:create, params: params).
67
+ # on(:user)
68
+ # end
51
69
  # end
52
70
  #
53
71
  # If your action requires query parameters in order to work, then you'll
@@ -83,21 +101,41 @@ module Shoulda
83
101
  # end
84
102
  #
85
103
  # it do
104
+ # params = {
105
+ # id: 1,
106
+ # user: {
107
+ # first_name: 'Jon',
108
+ # last_name: 'Doe',
109
+ # email: 'jondoe@example.com',
110
+ # password: 'password'
111
+ # }
112
+ # }
86
113
  # should permit(:first_name, :last_name, :email, :password).
87
- # for(:update, params: { id: 1 }).
114
+ # for(:update, params: params).
88
115
  # on(:user)
89
116
  # end
90
117
  # end
91
118
  #
92
- # # Test::Unit
119
+ # # Minitest (Shoulda)
93
120
  # class UsersControllerTest < ActionController::TestCase
94
121
  # setup do
95
122
  # create(:user, id: 1)
96
123
  # end
97
124
  #
98
- # should permit(:first_name, :last_name, :email, :password).
99
- # for(:update, params: { id: 1 }).
100
- # on(:user)
125
+ # should "(for PATCH #update) restrict parameters on :user to :first_name, :last_name, :email, and :password" do
126
+ # params = {
127
+ # id: 1,
128
+ # user: {
129
+ # first_name: 'Jon',
130
+ # last_name: 'Doe',
131
+ # email: 'jondoe@example.com',
132
+ # password: 'password'
133
+ # }
134
+ # }
135
+ # should permit(:first_name, :last_name, :email, :password).
136
+ # for(:update, params: params).
137
+ # on(:user)
138
+ # end
101
139
  # end
102
140
  #
103
141
  # Finally, if you have an action that isn't one of the seven resourceful
@@ -136,21 +174,25 @@ module Shoulda
136
174
  # end
137
175
  #
138
176
  # it do
177
+ # params = { id: 1, user: { activated: true } }
139
178
  # should permit(:activated).
140
- # for(:toggle, params: { id: 1 }, verb: :put).
179
+ # for(:toggle, params: params, verb: :put).
141
180
  # on(:user)
142
181
  # end
143
182
  # end
144
183
  #
145
- # # Test::Unit
184
+ # # Minitest (Shoulda)
146
185
  # class UsersControllerTest < ActionController::TestCase
147
186
  # setup do
148
187
  # create(:user, id: 1)
149
188
  # end
150
189
  #
151
- # should permit(:activated).
152
- # for(:toggle, params: { id: 1 }, verb: :put).
153
- # on(:user)
190
+ # should "(for PUT #toggle) restrict parameters on :user to :activated" do
191
+ # params = { id: 1, user: { activated: true } }
192
+ # should permit(:activated).
193
+ # for(:toggle, params: params, verb: :put).
194
+ # on(:user)
195
+ # end
154
196
  # end
155
197
  #
156
198
  # @return [PermitMatcher]
@@ -163,13 +205,13 @@ module Shoulda
163
205
  class PermitMatcher
164
206
  attr_writer :stubbed_params
165
207
 
166
- def initialize(expected_permitted_params)
167
- @expected_permitted_params = expected_permitted_params
208
+ def initialize(expected_permitted_parameter_names)
209
+ @expected_permitted_parameter_names = expected_permitted_parameter_names
168
210
  @action = nil
169
211
  @verb = nil
170
212
  @request_params = {}
171
- @subparameter = nil
172
- @parameters_doubles = ParametersDoubles.new
213
+ @subparameter_name = nil
214
+ @parameters_double_registry = CompositeParametersDoubleRegistry.new
173
215
  end
174
216
 
175
217
  def for(action, options = {})
@@ -184,9 +226,8 @@ module Shoulda
184
226
  self
185
227
  end
186
228
 
187
- def on(subparameter)
188
- @subparameter = subparameter
189
- @parameters_doubles = SliceOfParametersDoubles.new(subparameter)
229
+ def on(subparameter_name)
230
+ @subparameter_name = subparameter_name
190
231
  self
191
232
  end
192
233
 
@@ -196,78 +237,84 @@ module Shoulda
196
237
  end
197
238
 
198
239
  def description
199
- "(on #{verb.upcase} ##{action}) " + expectation
240
+ "(for #{verb.upcase} ##{action}) " + expectation
200
241
  end
201
242
 
202
243
  def matches?(controller)
203
244
  @controller = controller
204
245
  ensure_action_and_verb_present!
205
246
 
206
- parameters_doubles.register
247
+ parameters_double_registry.register
207
248
 
208
249
  Doublespeak.with_doubles_activated do
209
250
  context.__send__(verb, action, request_params)
210
251
  end
211
252
 
212
- unpermitted_params.empty?
253
+ unpermitted_parameter_names.empty?
213
254
  end
214
255
 
215
256
  def failure_message
216
257
  "Expected #{verb.upcase} ##{action} to #{expectation},\nbut #{reality}."
217
258
  end
218
- alias failure_message_for_should failure_message
219
259
 
220
260
  def failure_message_when_negated
221
261
  "Expected #{verb.upcase} ##{action} not to #{expectation},\nbut it did."
222
262
  end
223
- alias failure_message_for_should_not failure_message_when_negated
224
263
 
225
264
  protected
226
265
 
227
- attr_reader :controller, :double_collections_by_param, :action, :verb,
228
- :request_params, :expected_permitted_params, :context, :subparameter,
229
- :parameters_doubles
266
+ attr_reader :controller, :double_collections_by_parameter_name, :action, :verb,
267
+ :request_params, :expected_permitted_parameter_names, :context, :subparameter_name,
268
+ :parameters_double_registry
230
269
 
231
270
  def expectation
232
271
  message = 'restrict parameters '
233
272
 
234
- if subparameter
235
- message << " for #{subparameter.inspect}"
273
+ if subparameter_name
274
+ message << "on #{subparameter_name.inspect} "
236
275
  end
237
276
 
238
- message << 'to ' + format_param_names(expected_permitted_params)
277
+ message << 'to ' + format_parameter_names(expected_permitted_parameter_names)
239
278
 
240
279
  message
241
280
  end
242
281
 
243
282
  def reality
244
- if actual_permitted_params.empty?
283
+ if actual_permitted_parameter_names.empty?
245
284
  'it did not restrict any parameters'
246
285
  else
247
286
  'the restricted parameters were ' +
248
- format_param_names(actual_permitted_params) +
287
+ format_parameter_names(actual_permitted_parameter_names) +
249
288
  ' instead'
250
289
  end
251
290
  end
252
291
 
253
- def format_param_names(param_names)
254
- param_names.map(&:inspect).to_sentence
292
+ def format_parameter_names(parameter_names)
293
+ parameter_names.map(&:inspect).to_sentence
255
294
  end
256
295
 
257
- def actual_permitted_params
258
- parameters_doubles.permitted_params
296
+ def actual_permitted_parameter_names
297
+ @_actual_permitted_parameter_names ||= begin
298
+ if subparameter_name
299
+ options = { for: subparameter_name }
300
+ else
301
+ options = {}
302
+ end
303
+
304
+ parameters_double_registry.permitted_parameter_names(options)
305
+ end
259
306
  end
260
307
 
261
308
  def permit_called?
262
- actual_permitted_params.any?
309
+ actual_permitted_parameter_names.any?
263
310
  end
264
311
 
265
- def unpermitted_params
266
- expected_permitted_params - actual_permitted_params
312
+ def unpermitted_parameter_names
313
+ expected_permitted_parameter_names - actual_permitted_parameter_names
267
314
  end
268
315
 
269
- def verified_permitted_params
270
- expected_permitted_params & actual_permitted_params
316
+ def verified_permitted_parameter_names
317
+ expected_permitted_parameter_names & actual_permitted_parameter_names
271
318
  end
272
319
 
273
320
  def ensure_action_and_verb_present!
@@ -287,57 +334,63 @@ module Shoulda
287
334
  end
288
335
  end
289
336
 
290
- def param_names_as_sentence
291
- expected_permitted_params.map(&:inspect).to_sentence
337
+ def parameter_names_as_sentence
338
+ expected_permitted_parameter_names.map(&:inspect).to_sentence
292
339
  end
293
340
 
294
341
  # @private
295
- class ParametersDoubles
296
- def self.permitted_params_within(double_collection)
297
- double_collection.calls_to(:permit).map(&:args).flatten
298
- end
299
-
342
+ class CompositeParametersDoubleRegistry
300
343
  def initialize
301
- klass = ::ActionController::Parameters
302
- @double_collection = Doublespeak.double_collection_for(klass)
344
+ @parameters_double_registries_by_params = {}
303
345
  end
304
346
 
305
347
  def register
306
- double_collection.register_proxy(:permit)
348
+ double_collection = Doublespeak.double_collection_for(
349
+ ::ActionController::Parameters.singleton_class
350
+ )
351
+ double_collection.register_proxy(:new).to_return do |call|
352
+ params = call.return_value
353
+ parameters_double_registry = ParametersDoubleRegistry.new(params)
354
+ parameters_double_registry.register
355
+ parameters_double_registries_by_params[params] =
356
+ parameters_double_registry
357
+ end
307
358
  end
308
359
 
309
- def permitted_params
310
- ParametersDoubles.permitted_params_within(double_collection)
360
+ def permitted_parameter_names(options = {})
361
+ parameters_double_registries_by_params.flat_map do |params, double_registry|
362
+ double_registry.permitted_parameter_names(options)
363
+ end
311
364
  end
312
365
 
313
366
  protected
314
367
 
315
- attr_reader :double_collection
368
+ attr_reader :parameters_double_registries_by_params
316
369
  end
317
370
 
318
371
  # @private
319
- class SliceOfParametersDoubles
372
+ class ParametersDoubleRegistry
320
373
  TOP_LEVEL = Object.new
321
374
 
322
- def initialize(subparameter)
323
- klass = ::ActionController::Parameters
375
+ def self.permitted_parameter_names_within(double_collection)
376
+ double_collection.calls_to(:permit).map(&:args).flatten
377
+ end
324
378
 
325
- @subparameter = subparameter
326
- @double_collections_by_param = {
327
- TOP_LEVEL => Doublespeak.double_collection_for(klass)
328
- }
379
+ def initialize(params)
380
+ @params = params
381
+ @double_collections_by_parameter_name = {}
329
382
  end
330
383
 
331
384
  def register
332
- top_level_collection = double_collections_by_param[TOP_LEVEL]
333
- double_permit_on(top_level_collection)
334
- double_require_on(top_level_collection)
385
+ register_double_for_permit_against(params, TOP_LEVEL)
335
386
  end
336
387
 
337
- def permitted_params
338
- if double_collections_by_param.key?(subparameter)
339
- ParametersDoubles.permitted_params_within(
340
- double_collections_by_param[subparameter]
388
+ def permitted_parameter_names(args = {})
389
+ subparameter_name = args.fetch(:for, TOP_LEVEL)
390
+
391
+ if double_collections_by_parameter_name.key?(subparameter_name)
392
+ self.class.permitted_parameter_names_within(
393
+ double_collections_by_parameter_name[subparameter_name]
341
394
  )
342
395
  else
343
396
  []
@@ -346,31 +399,30 @@ module Shoulda
346
399
 
347
400
  protected
348
401
 
349
- attr_reader :subparameter, :double_collections_by_param
402
+ attr_reader :params, :double_collections_by_parameter_name
350
403
 
351
404
  private
352
405
 
353
- def double_permit_on(double_collection)
354
- double_collection.register_proxy(:permit)
355
- end
406
+ def register_double_for_permit_against(params, subparameter_name)
407
+ klass = params.singleton_class
356
408
 
357
- def double_require_on(double_collection)
358
- double_collections_by_param = @double_collections_by_param
359
- require_double = double_collection.register_proxy(:require)
409
+ double_collection = Doublespeak.double_collection_for(klass)
410
+ register_double_for_permit_on(double_collection)
411
+ register_double_for_require_on(double_collection)
360
412
 
361
- require_double.to_return do |call|
362
- param_name = call.args.first
363
- params = call.return_value
364
- double_collections_by_param[param_name] ||=
365
- double_permit_against(params)
366
- end
413
+ double_collections_by_parameter_name[subparameter_name] =
414
+ double_collection
367
415
  end
368
416
 
369
- def double_permit_against(params)
370
- klass = params.singleton_class
417
+ def register_double_for_permit_on(double_collection)
418
+ double_collection.register_proxy(:permit)
419
+ end
371
420
 
372
- Doublespeak.double_collection_for(klass).tap do |double_collection|
373
- double_permit_on(double_collection)
421
+ def register_double_for_require_on(double_collection)
422
+ double_collection.register_proxy(:require).to_return do |call|
423
+ params = call.return_value
424
+ subparameter_name = call.args.first
425
+ register_double_for_permit_against(params, subparameter_name)
374
426
  end
375
427
  end
376
428
  end