durable_parameters 0.2.3

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +853 -0
  4. data/Rakefile +29 -0
  5. data/app/params/account_params.rb.example +38 -0
  6. data/app/params/application_params.rb +16 -0
  7. data/lib/durable_parameters/adapters/hanami.rb +138 -0
  8. data/lib/durable_parameters/adapters/rage.rb +124 -0
  9. data/lib/durable_parameters/adapters/rails.rb +280 -0
  10. data/lib/durable_parameters/adapters/sinatra.rb +91 -0
  11. data/lib/durable_parameters/core/application_params.rb +334 -0
  12. data/lib/durable_parameters/core/configuration.rb +83 -0
  13. data/lib/durable_parameters/core/forbidden_attributes_protection.rb +48 -0
  14. data/lib/durable_parameters/core/parameters.rb +643 -0
  15. data/lib/durable_parameters/core/params_registry.rb +110 -0
  16. data/lib/durable_parameters/core.rb +15 -0
  17. data/lib/durable_parameters/log_subscriber.rb +34 -0
  18. data/lib/durable_parameters/railtie.rb +65 -0
  19. data/lib/durable_parameters/version.rb +7 -0
  20. data/lib/durable_parameters.rb +41 -0
  21. data/lib/generators/rails/USAGE +12 -0
  22. data/lib/generators/rails/durable_parameters_controller_generator.rb +17 -0
  23. data/lib/generators/rails/templates/controller.rb +94 -0
  24. data/lib/legacy/action_controller/application_params.rb +235 -0
  25. data/lib/legacy/action_controller/parameters.rb +524 -0
  26. data/lib/legacy/action_controller/params_registry.rb +108 -0
  27. data/lib/legacy/active_model/forbidden_attributes_protection.rb +40 -0
  28. data/test/action_controller_required_params_test.rb +36 -0
  29. data/test/action_controller_tainted_params_test.rb +29 -0
  30. data/test/active_model_mass_assignment_taint_protection_test.rb +25 -0
  31. data/test/application_params_array_test.rb +245 -0
  32. data/test/application_params_edge_cases_test.rb +361 -0
  33. data/test/application_params_test.rb +893 -0
  34. data/test/controller_generator_test.rb +31 -0
  35. data/test/core_parameters_test.rb +2376 -0
  36. data/test/durable_parameters_test.rb +115 -0
  37. data/test/enhanced_error_messages_test.rb +120 -0
  38. data/test/gemfiles/Gemfile.rails-3.0.x +14 -0
  39. data/test/gemfiles/Gemfile.rails-3.1.x +14 -0
  40. data/test/gemfiles/Gemfile.rails-3.2.x +14 -0
  41. data/test/log_on_unpermitted_params_test.rb +49 -0
  42. data/test/metadata_validation_test.rb +294 -0
  43. data/test/multi_parameter_attributes_test.rb +38 -0
  44. data/test/parameters_core_methods_test.rb +503 -0
  45. data/test/parameters_integration_test.rb +553 -0
  46. data/test/parameters_permit_test.rb +491 -0
  47. data/test/parameters_require_test.rb +9 -0
  48. data/test/parameters_taint_test.rb +98 -0
  49. data/test/params_registry_concurrency_test.rb +422 -0
  50. data/test/params_registry_test.rb +112 -0
  51. data/test/permit_by_model_test.rb +227 -0
  52. data/test/raise_on_unpermitted_params_test.rb +32 -0
  53. data/test/test_helper.rb +38 -0
  54. data/test/transform_params_edge_cases_test.rb +526 -0
  55. data/test/transformation_test.rb +360 -0
  56. metadata +223 -0
@@ -0,0 +1,503 @@
1
+ require 'test_helper'
2
+
3
+ class ParametersCoreMethods < Minitest::Test
4
+ def setup
5
+ @params = ActionController::Parameters.new(
6
+ name: 'John',
7
+ email: 'john@example.com',
8
+ age: 30,
9
+ admin: true,
10
+ profile: {
11
+ bio: 'Developer',
12
+ location: 'NYC'
13
+ },
14
+ tags: ['ruby', 'rails']
15
+ )
16
+ end
17
+
18
+ # Test initialization
19
+ def test_initialization_with_nil
20
+ assert_raises(NoMethodError) { ActionController::Parameters.new(nil) }
21
+ end
22
+
23
+ def test_initialization_with_empty_hash
24
+ params = ActionController::Parameters.new({})
25
+ assert_equal({}, params.permit!.to_h)
26
+ assert params.permitted?
27
+ end
28
+
29
+ def test_initialization_with_hash
30
+ params = ActionController::Parameters.new(key: 'value')
31
+ assert_equal 'value', params[:key]
32
+ assert !params.permitted?
33
+ end
34
+
35
+ # Test permitted flag
36
+ def test_permitted_starts_false
37
+ assert !@params.permitted?
38
+ end
39
+
40
+ def test_permit_bang_sets_permitted_true
41
+ @params.permit!
42
+ assert @params.permitted?
43
+ end
44
+
45
+ def test_permit_bang_returns_self
46
+ result = @params.permit!
47
+ assert_equal @params.object_id, result.object_id
48
+ end
49
+
50
+ # Test require method
51
+ def test_require_returns_value_when_present
52
+ params = ActionController::Parameters.new(user: { name: 'John' })
53
+ result = params.require(:user)
54
+ assert_equal 'John', result[:name]
55
+ end
56
+
57
+ def test_require_raises_when_key_missing
58
+ assert_raises(ActionController::ParameterMissing) do
59
+ @params.require(:nonexistent)
60
+ end
61
+ end
62
+
63
+ def test_require_raises_when_value_empty
64
+ params = ActionController::Parameters.new(user: nil)
65
+ assert_raises(ActionController::ParameterMissing) do
66
+ params.require(:user)
67
+ end
68
+ end
69
+
70
+ def test_require_raises_when_value_empty_string
71
+ params = ActionController::Parameters.new(user: '')
72
+ assert_raises(ActionController::ParameterMissing) do
73
+ params.require(:user)
74
+ end
75
+ end
76
+
77
+ def test_require_raises_when_value_empty_array
78
+ params = ActionController::Parameters.new(user: [])
79
+ assert_raises(ActionController::ParameterMissing) do
80
+ params.require(:user)
81
+ end
82
+ end
83
+
84
+ def test_require_raises_when_value_empty_hash
85
+ params = ActionController::Parameters.new(user: {})
86
+ assert_raises(ActionController::ParameterMissing) do
87
+ params.require(:user)
88
+ end
89
+ end
90
+
91
+ def test_require_accepts_string_key
92
+ params = ActionController::Parameters.new(user: { name: 'John' })
93
+ result = params.require('user')
94
+ assert_equal 'John', result[:name]
95
+ end
96
+
97
+ def test_require_sets_required_key_on_result
98
+ params = ActionController::Parameters.new(user: { name: 'John' })
99
+ result = params.require(:user)
100
+ assert_equal :user, result.required_key
101
+ end
102
+
103
+ def test_required_alias_works
104
+ params = ActionController::Parameters.new(user: { name: 'John' })
105
+ result = params.required(:user)
106
+ assert_equal 'John', result[:name]
107
+ end
108
+
109
+ # Test permit method
110
+ def test_permit_with_single_key
111
+ permitted = @params.permit(:name)
112
+ assert_equal 'John', permitted[:name]
113
+ assert_nil permitted[:email]
114
+ assert permitted.permitted?
115
+ end
116
+
117
+ def test_permit_with_multiple_keys
118
+ permitted = @params.permit(:name, :email, :age)
119
+ assert_equal 'John', permitted[:name]
120
+ assert_equal 'john@example.com', permitted[:email]
121
+ assert_equal 30, permitted[:age]
122
+ assert_nil permitted[:admin]
123
+ assert permitted.permitted?
124
+ end
125
+
126
+ def test_permit_with_string_keys
127
+ permitted = @params.permit('name', 'email')
128
+ assert_equal 'John', permitted[:name]
129
+ assert_equal 'john@example.com', permitted[:email]
130
+ assert permitted.permitted?
131
+ end
132
+
133
+ def test_permit_returns_new_instance
134
+ permitted = @params.permit(:name)
135
+ refute_equal @params.object_id, permitted.object_id
136
+ end
137
+
138
+ def test_permit_doesnt_modify_original
139
+ permitted = @params.permit(:name)
140
+ assert !@params.permitted?
141
+ assert permitted.permitted?
142
+ end
143
+
144
+ # Test [] accessor
145
+ def test_bracket_accessor_with_symbol
146
+ assert_equal 'John', @params[:name]
147
+ end
148
+
149
+ def test_bracket_accessor_with_string
150
+ assert_equal 'John', @params['name']
151
+ end
152
+
153
+ def test_bracket_accessor_returns_nil_for_missing_key
154
+ assert_nil @params[:nonexistent]
155
+ end
156
+
157
+ def test_bracket_accessor_converts_hash_to_parameters
158
+ profile = @params[:profile]
159
+ assert profile.is_a?(ActionController::Parameters)
160
+ assert_equal 'Developer', profile[:bio]
161
+ end
162
+
163
+ # Test fetch method
164
+ def test_fetch_returns_value_when_present
165
+ assert_equal 'John', @params.fetch(:name)
166
+ end
167
+
168
+ def test_fetch_raises_when_key_missing
169
+ assert_raises(ActionController::ParameterMissing) do
170
+ @params.fetch(:nonexistent)
171
+ end
172
+ end
173
+
174
+ def test_fetch_with_default_value
175
+ result = @params.fetch(:nonexistent, 'default')
176
+ assert_equal 'default', result
177
+ end
178
+
179
+ def test_fetch_with_block
180
+ result = @params.fetch(:nonexistent) { 'from block' }
181
+ assert_equal 'from block', result
182
+ end
183
+
184
+ def test_fetch_converts_hash_to_parameters
185
+ profile = @params.fetch(:profile)
186
+ assert profile.is_a?(ActionController::Parameters)
187
+ end
188
+
189
+ # Test slice method
190
+ def test_slice_returns_subset
191
+ sliced = @params.slice(:name, :email)
192
+ assert_equal 'John', sliced[:name]
193
+ assert_equal 'john@example.com', sliced[:email]
194
+ assert_nil sliced[:age]
195
+ end
196
+
197
+ def test_slice_preserves_permitted_flag
198
+ @params.permit!
199
+ sliced = @params.slice(:name)
200
+ assert sliced.permitted?
201
+ end
202
+
203
+ def test_slice_preserves_required_key
204
+ params = ActionController::Parameters.new(user: { name: 'John', email: 'john@example.com' })
205
+ user_params = params.require(:user)
206
+ sliced = user_params.slice(:name)
207
+ assert_equal :user, sliced.required_key
208
+ end
209
+
210
+ def test_slice_returns_new_instance
211
+ sliced = @params.slice(:name)
212
+ refute_equal @params.object_id, sliced.object_id
213
+ end
214
+
215
+ # Test dup method
216
+ def test_dup_creates_new_instance
217
+ duped = @params.dup
218
+ refute_equal @params.object_id, duped.object_id
219
+ end
220
+
221
+ def test_dup_preserves_data
222
+ duped = @params.dup
223
+ assert_equal 'John', duped[:name]
224
+ assert_equal 'john@example.com', duped[:email]
225
+ assert_equal 30, duped[:age]
226
+ end
227
+
228
+ def test_dup_preserves_permitted_flag
229
+ @params.permit!
230
+ duped = @params.dup
231
+ assert duped.permitted?
232
+ end
233
+
234
+ def test_dup_preserves_required_key
235
+ params = ActionController::Parameters.new(user: { name: 'John' })
236
+ user_params = params.require(:user)
237
+ duped = user_params.dup
238
+ assert_equal :user, duped.required_key
239
+ end
240
+
241
+ def test_dup_creates_shallow_copy
242
+ duped = @params.dup
243
+ duped[:new_key] = 'new_value'
244
+ assert_nil @params[:new_key]
245
+ end
246
+
247
+ # Test hash conversion
248
+ def test_to_h_returns_hash
249
+ hash = @params.permit(:name).to_h
250
+ assert hash.is_a?(Hash)
251
+ end
252
+
253
+ def test_to_h_with_permitted_params
254
+ permitted = @params.permit(:name, :email)
255
+ hash = permitted.to_h
256
+ assert_equal 'John', hash['name']
257
+ assert_equal 'john@example.com', hash['email']
258
+ end
259
+
260
+ # Test key checking
261
+ def test_has_key_with_symbol
262
+ assert @params.has_key?(:name)
263
+ end
264
+
265
+ def test_has_key_with_string
266
+ assert @params.has_key?('name')
267
+ end
268
+
269
+ def test_has_key_returns_false_for_missing
270
+ assert !@params.has_key?(:nonexistent)
271
+ end
272
+
273
+ def test_key_alias_works
274
+ assert @params.key?(:name)
275
+ end
276
+
277
+ # Test keys method
278
+ def test_keys_returns_all_keys
279
+ keys = @params.keys
280
+ assert_includes keys, 'name'
281
+ assert_includes keys, 'email'
282
+ assert_includes keys, 'age'
283
+ end
284
+
285
+ # Test values method
286
+ def test_values_returns_all_values
287
+ values = @params.values
288
+ assert_includes values, 'John'
289
+ assert_includes values, 'john@example.com'
290
+ assert_includes values, 30
291
+ end
292
+
293
+ # Test each methods
294
+ def test_each_iterates_over_pairs
295
+ result = {}
296
+ @params.each { |k, v| result[k] = v }
297
+ assert_equal 'John', result['name']
298
+ assert_equal 'john@example.com', result['email']
299
+ end
300
+
301
+ def test_each_pair_iterates_over_pairs
302
+ result = {}
303
+ @params.each_pair { |k, v| result[k] = v }
304
+ assert_equal 'John', result['name']
305
+ end
306
+
307
+ # Test merge
308
+ def test_merge_combines_params
309
+ other = ActionController::Parameters.new(country: 'USA')
310
+ merged = @params.permit(:name).merge(other.permit(:country))
311
+ assert_equal 'John', merged[:name]
312
+ assert_equal 'USA', merged[:country]
313
+ end
314
+
315
+ def test_merge_doesnt_modify_original
316
+ other = ActionController::Parameters.new(country: 'USA')
317
+ merged = @params.permit(:name).merge(other.permit(:country))
318
+ assert_nil @params[:country]
319
+ assert_equal 'USA', merged[:country]
320
+ end
321
+
322
+ # Test empty checks
323
+ def test_empty_returns_true_for_empty_params
324
+ params = ActionController::Parameters.new({})
325
+ assert params.empty?
326
+ end
327
+
328
+ def test_empty_returns_false_for_non_empty_params
329
+ assert !@params.empty?
330
+ end
331
+
332
+ # Test nested parameter conversion
333
+ def test_nested_hash_converted_to_parameters
334
+ profile = @params[:profile]
335
+ assert profile.is_a?(ActionController::Parameters)
336
+ assert !profile.permitted?
337
+ end
338
+
339
+ def test_array_elements_not_converted
340
+ tags = @params[:tags]
341
+ assert tags.is_a?(Array)
342
+ assert_equal 'ruby', tags[0]
343
+ end
344
+
345
+ def test_deeply_nested_hashes_converted
346
+ params = ActionController::Parameters.new(
347
+ level1: {
348
+ level2: {
349
+ level3: {
350
+ value: 'deep'
351
+ }
352
+ }
353
+ }
354
+ )
355
+
356
+ level1 = params[:level1]
357
+ level2 = level1[:level2]
358
+ level3 = level2[:level3]
359
+
360
+ assert level1.is_a?(ActionController::Parameters)
361
+ assert level2.is_a?(ActionController::Parameters)
362
+ assert level3.is_a?(ActionController::Parameters)
363
+ assert_equal 'deep', level3[:value]
364
+ end
365
+
366
+ # Test with indifferent access
367
+ def test_indifferent_access_symbol_and_string
368
+ params = ActionController::Parameters.new(key: 'value')
369
+ assert_equal 'value', params[:key]
370
+ assert_equal 'value', params['key']
371
+ end
372
+
373
+ # Test delete
374
+ def test_delete_removes_key
375
+ @params.delete(:name)
376
+ assert_nil @params[:name]
377
+ end
378
+
379
+ def test_delete_returns_value
380
+ result = @params.delete(:name)
381
+ assert_equal 'John', result
382
+ end
383
+
384
+ # Test select
385
+ def test_select_filters_params
386
+ selected = @params.select { |k, v| v.is_a?(String) }
387
+ assert_includes selected, 'name'
388
+ assert_includes selected, 'email'
389
+ end
390
+
391
+ # Test reject
392
+ def test_reject_filters_params
393
+ rejected = @params.reject { |k, v| v.is_a?(String) }
394
+ assert_includes rejected, 'age'
395
+ assert_includes rejected, 'admin'
396
+ end
397
+
398
+ # Test compact (if available)
399
+ def test_compact_removes_nil_values
400
+ params = ActionController::Parameters.new(
401
+ name: 'John',
402
+ email: nil,
403
+ age: 30
404
+ )
405
+
406
+ if params.respond_to?(:compact)
407
+ compacted = params.compact
408
+ assert_equal 'John', compacted[:name]
409
+ assert_equal 30, compacted[:age]
410
+ assert !compacted.has_key?(:email)
411
+ end
412
+ end
413
+
414
+ # Test transform_keys
415
+ def test_transform_keys_if_available
416
+ if @params.respond_to?(:transform_keys)
417
+ transformed = @params.transform_keys { |key| key.to_s.upcase }
418
+ assert_equal 'John', transformed['NAME']
419
+ end
420
+ end
421
+
422
+ # Test except
423
+ def test_except_excludes_keys
424
+ if @params.respond_to?(:except)
425
+ excepted = @params.except(:name, :email)
426
+ assert_nil excepted[:name]
427
+ assert_nil excepted[:email]
428
+ assert_equal 30, excepted[:age]
429
+ end
430
+ end
431
+
432
+ # Test with special parameter types
433
+ def test_with_file_upload
434
+ file = StringIO.new('file content')
435
+ params = ActionController::Parameters.new(
436
+ name: 'John',
437
+ avatar: file
438
+ )
439
+
440
+ permitted = params.permit(:name, :avatar)
441
+ assert_equal 'John', permitted[:name]
442
+ assert_equal file, permitted[:avatar]
443
+ end
444
+
445
+ def test_with_date_time
446
+ now = Time.now
447
+ today = Date.today
448
+
449
+ params = ActionController::Parameters.new(
450
+ name: 'John',
451
+ created_at: now,
452
+ birth_date: today
453
+ )
454
+
455
+ permitted = params.permit(:name, :created_at, :birth_date)
456
+ assert_equal 'John', permitted[:name]
457
+ assert_equal now, permitted[:created_at]
458
+ assert_equal today, permitted[:birth_date]
459
+ end
460
+
461
+ # Test parameter missing exception
462
+ def test_parameter_missing_exception_includes_param_name
463
+ begin
464
+ @params.require(:nonexistent)
465
+ flunk 'Should have raised ParameterMissing'
466
+ rescue ActionController::ParameterMissing => e
467
+ assert_equal :nonexistent, e.param
468
+ assert_includes e.message, 'nonexistent'
469
+ end
470
+ end
471
+
472
+ # Test unpermitted parameters exception
473
+ def test_unpermitted_parameters_exception_includes_params
474
+ params = ['field1', 'field2']
475
+ exception = ActionController::UnpermittedParameters.new(params)
476
+ assert_equal params, exception.params
477
+ assert_includes exception.message, 'field1'
478
+ assert_includes exception.message, 'field2'
479
+ end
480
+
481
+ # Test convert_value method behavior
482
+ def test_nested_array_of_hashes_converted
483
+ params = ActionController::Parameters.new(
484
+ items: [
485
+ { name: 'Item 1' },
486
+ { name: 'Item 2' }
487
+ ]
488
+ )
489
+
490
+ items = params[:items]
491
+ assert items.is_a?(Array)
492
+ assert items[0].is_a?(ActionController::Parameters)
493
+ assert items[1].is_a?(ActionController::Parameters)
494
+ assert_equal 'Item 1', items[0][:name]
495
+ assert_equal 'Item 2', items[1][:name]
496
+ end
497
+
498
+ # Test required_key initialization
499
+ def test_required_key_initially_nil
500
+ params = ActionController::Parameters.new(user: { name: 'John' })
501
+ assert_nil params.required_key
502
+ end
503
+ end