shoulda-matchers 3.0.0.rc1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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