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,553 @@
1
+ require 'test_helper'
2
+
3
+ class ParametersIntegrationTest < Minitest::Test
4
+ def setup
5
+ ActionController::ParamsRegistry.clear!
6
+ end
7
+
8
+ def teardown
9
+ ActionController::ParamsRegistry.clear!
10
+ end
11
+
12
+ # Test complete workflow: define params class, register, use
13
+ def test_complete_workflow
14
+ # Step 1: Define params class
15
+ user_params = Class.new(ActionController::ApplicationParams) do
16
+ def self.name
17
+ 'UserParams'
18
+ end
19
+
20
+ allow :first_name
21
+ allow :last_name
22
+ allow :email
23
+ allow :role, only: :create
24
+ deny :password_digest
25
+ metadata :ip_address
26
+ end
27
+
28
+ # Step 2: Register it
29
+ ActionController::ParamsRegistry.register('User', user_params)
30
+
31
+ # Step 3: Use it
32
+ params = ActionController::Parameters.new(
33
+ user: {
34
+ first_name: 'John',
35
+ last_name: 'Doe',
36
+ email: 'john@example.com',
37
+ role: 'admin',
38
+ password_digest: 'secret_hash'
39
+ }
40
+ )
41
+
42
+ permitted = params.require(:user).transform_params(
43
+ action: :create,
44
+ current_user: Object.new,
45
+ ip_address: '127.0.0.1'
46
+ )
47
+
48
+ assert_equal 'John', permitted[:first_name]
49
+ assert_equal 'Doe', permitted[:last_name]
50
+ assert_equal 'john@example.com', permitted[:email]
51
+ assert_equal 'admin', permitted[:role]
52
+ assert_nil permitted[:password_digest]
53
+ end
54
+
55
+ # Test multiple models in same request
56
+ def test_multiple_models_in_same_request
57
+ user_params = Class.new(ActionController::ApplicationParams) do
58
+ def self.name
59
+ 'UserParams'
60
+ end
61
+ allow :name
62
+ end
63
+
64
+ account_params = Class.new(ActionController::ApplicationParams) do
65
+ def self.name
66
+ 'AccountParams'
67
+ end
68
+ allow :title
69
+ end
70
+
71
+ ActionController::ParamsRegistry.register('User', user_params)
72
+ ActionController::ParamsRegistry.register('Account', account_params)
73
+
74
+ params = ActionController::Parameters.new(
75
+ user: { name: 'John' },
76
+ account: { title: 'My Account' }
77
+ )
78
+
79
+ user_permitted = params.require(:user).transform_params()
80
+ account_permitted = params.require(:account).transform_params()
81
+
82
+ assert_equal 'John', user_permitted[:name]
83
+ assert_equal 'My Account', account_permitted[:title]
84
+ end
85
+
86
+ # Test combining permit and transform_params
87
+ def test_combining_permit_and_transform_params
88
+ user_params = Class.new(ActionController::ApplicationParams) do
89
+ def self.name
90
+ 'UserParams'
91
+ end
92
+ allow :name
93
+ allow :email
94
+ end
95
+
96
+ ActionController::ParamsRegistry.register('User', user_params)
97
+
98
+ params = ActionController::Parameters.new(
99
+ user: { name: 'John', email: 'john@example.com' },
100
+ extra: 'value'
101
+ )
102
+
103
+ # First permit top-level
104
+ top_level = params.permit(:extra)
105
+ assert_equal 'value', top_level[:extra]
106
+
107
+ # Then transform nested
108
+ user_permitted = params.require(:user).transform_params()
109
+ assert_equal 'John', user_permitted[:name]
110
+ assert_equal 'john@example.com', user_permitted[:email]
111
+ end
112
+
113
+ # Test params class with complex action filters
114
+ def test_complex_action_filtering_scenarios
115
+ params_class = Class.new(ActionController::ApplicationParams) do
116
+ def self.name
117
+ 'ComplexParams'
118
+ end
119
+
120
+ # Always allowed
121
+ allow :id
122
+ allow :name
123
+
124
+ # Only on create
125
+ allow :created_by, only: :create
126
+
127
+ # Only on create and update
128
+ allow :status, only: [:create, :update]
129
+
130
+ # Never on destroy
131
+ allow :notes, except: :destroy
132
+
133
+ # Never on destroy or delete
134
+ allow :metadata, except: [:destroy, :delete]
135
+ end
136
+
137
+ ActionController::ParamsRegistry.register('Complex', params_class)
138
+
139
+ # Test create action
140
+ create_params = ActionController::Parameters.new(
141
+ complex: {
142
+ id: 1,
143
+ name: 'Test',
144
+ created_by: 'admin',
145
+ status: 'active',
146
+ notes: 'Some notes',
147
+ metadata: { key: 'value' }
148
+ }
149
+ )
150
+ create_permitted = create_params.require(:complex).transform_params(action: :create)
151
+ assert_equal 1, create_permitted[:id]
152
+ assert_equal 'Test', create_permitted[:name]
153
+ assert_equal 'admin', create_permitted[:created_by]
154
+ assert_equal 'active', create_permitted[:status]
155
+ assert_equal 'Some notes', create_permitted[:notes]
156
+
157
+ # Test update action
158
+ update_params = ActionController::Parameters.new(
159
+ complex: {
160
+ id: 1,
161
+ name: 'Test',
162
+ created_by: 'admin',
163
+ status: 'active',
164
+ notes: 'Some notes',
165
+ metadata: { key: 'value' }
166
+ }
167
+ )
168
+ update_permitted = update_params.require(:complex).transform_params(action: :update)
169
+ assert_equal 1, update_permitted[:id]
170
+ assert_equal 'Test', update_permitted[:name]
171
+ assert_nil update_permitted[:created_by] # only on create
172
+ assert_equal 'active', update_permitted[:status]
173
+ assert_equal 'Some notes', update_permitted[:notes]
174
+
175
+ # Test destroy action
176
+ destroy_params = ActionController::Parameters.new(
177
+ complex: {
178
+ id: 1,
179
+ name: 'Test',
180
+ created_by: 'admin',
181
+ status: 'active',
182
+ notes: 'Some notes',
183
+ metadata: { key: 'value' }
184
+ }
185
+ )
186
+ destroy_permitted = destroy_params.require(:complex).transform_params(action: :destroy)
187
+ assert_equal 1, destroy_permitted[:id]
188
+ assert_equal 'Test', destroy_permitted[:name]
189
+ assert_nil destroy_permitted[:created_by]
190
+ assert_nil destroy_permitted[:status]
191
+ assert_nil destroy_permitted[:notes] # except destroy
192
+ assert_nil destroy_permitted[:metadata] # except destroy
193
+
194
+ # Test show action
195
+ show_params = ActionController::Parameters.new(
196
+ complex: {
197
+ id: 1,
198
+ name: 'Test',
199
+ created_by: 'admin',
200
+ status: 'active',
201
+ notes: 'Some notes',
202
+ metadata: { key: 'value' }
203
+ }
204
+ )
205
+ show_permitted = show_params.require(:complex).transform_params(action: :show)
206
+ assert_equal 1, show_permitted[:id]
207
+ assert_equal 'Test', show_permitted[:name]
208
+ assert_nil show_permitted[:created_by]
209
+ assert_nil show_permitted[:status]
210
+ assert_equal 'Some notes', show_permitted[:notes]
211
+ end
212
+
213
+ # Test inheritance chain in real usage
214
+ def test_inheritance_chain_in_usage
215
+ base_params = Class.new(ActionController::ApplicationParams) do
216
+ def self.name
217
+ 'BaseParams'
218
+ end
219
+ allow :id
220
+ allow :created_at
221
+ end
222
+
223
+ user_params = Class.new(base_params) do
224
+ def self.name
225
+ 'UserParams'
226
+ end
227
+ allow :name
228
+ allow :email
229
+ deny :created_at # Override parent
230
+ end
231
+
232
+ admin_params = Class.new(user_params) do
233
+ def self.name
234
+ 'AdminParams'
235
+ end
236
+ allow :role
237
+ allow :permissions, array: true
238
+ end
239
+
240
+ ActionController::ParamsRegistry.register('Admin', admin_params)
241
+
242
+ params = ActionController::Parameters.new(
243
+ admin: {
244
+ id: 1,
245
+ name: 'Admin User',
246
+ email: 'admin@example.com',
247
+ role: 'super_admin',
248
+ permissions: ['all'],
249
+ created_at: Time.now
250
+ }
251
+ )
252
+
253
+ permitted = params.require(:admin).transform_params()
254
+
255
+ assert_equal 1, permitted[:id]
256
+ assert_equal 'Admin User', permitted[:name]
257
+ assert_equal 'admin@example.com', permitted[:email]
258
+ assert_equal 'super_admin', permitted[:role]
259
+ assert_equal ['all'], permitted[:permissions]
260
+ assert_nil permitted[:created_at] # denied in child
261
+ end
262
+
263
+ # Test dynamic additional_attrs based on conditions
264
+ def test_dynamic_additional_attrs
265
+ user_params = Class.new(ActionController::ApplicationParams) do
266
+ def self.name
267
+ 'UserParams'
268
+ end
269
+ allow :name
270
+ allow :email
271
+ end
272
+
273
+ ActionController::ParamsRegistry.register('User', user_params)
274
+
275
+ # Simulate allowing different fields based on user role
276
+ is_admin = true
277
+ additional = is_admin ? [:role, :status] : []
278
+
279
+ params = ActionController::Parameters.new(
280
+ user: {
281
+ name: 'Test',
282
+ email: 'test@example.com',
283
+ role: 'admin',
284
+ status: 'active'
285
+ }
286
+ )
287
+
288
+ permitted = params.require(:user).transform_params(
289
+ additional_attrs: additional
290
+ )
291
+
292
+ assert_equal 'Test', permitted[:name]
293
+ assert_equal 'test@example.com', permitted[:email]
294
+ assert_equal 'admin', permitted[:role]
295
+ assert_equal 'active', permitted[:status]
296
+ end
297
+
298
+ # Test registry lookup with different naming conventions
299
+ def test_registry_with_different_naming_conventions
300
+ params_class = Class.new(ActionController::ApplicationParams) do
301
+ def self.name
302
+ 'UserAccountParams'
303
+ end
304
+ allow :field
305
+ end
306
+
307
+ # Test various registration keys
308
+ ActionController::ParamsRegistry.register('UserAccount', params_class)
309
+
310
+ params1 = ActionController::Parameters.new(
311
+ user_account: { field: 'value1' }
312
+ )
313
+ permitted1 = params1.require(:user_account).transform_params()
314
+ assert_equal 'value1', permitted1[:field]
315
+
316
+ # Clear and try with different registration
317
+ ActionController::ParamsRegistry.clear!
318
+ ActionController::ParamsRegistry.register('user_account', params_class)
319
+
320
+ params2 = ActionController::Parameters.new(
321
+ user_account: { field: 'value2' }
322
+ )
323
+ permitted2 = params2.require(:user_account).transform_params()
324
+ assert_equal 'value2', permitted2[:field]
325
+ end
326
+
327
+ # Test backward compatibility with permit_by_model
328
+ def test_backward_compatibility_permit_by_model
329
+ user_params = Class.new(ActionController::ApplicationParams) do
330
+ def self.name
331
+ 'UserParams'
332
+ end
333
+ allow :name
334
+ allow :email
335
+ allow :status, only: :create
336
+ end
337
+
338
+ ActionController::ParamsRegistry.register('User', user_params)
339
+
340
+ params = ActionController::Parameters.new(
341
+ name: 'John',
342
+ email: 'john@example.com',
343
+ status: 'active',
344
+ admin: true
345
+ )
346
+
347
+ # Old API
348
+ permitted = params.permit_by_model(:user, action: :create)
349
+
350
+ assert_equal 'John', permitted[:name]
351
+ assert_equal 'john@example.com', permitted[:email]
352
+ assert_equal 'active', permitted[:status]
353
+ assert_nil permitted[:admin]
354
+ end
355
+
356
+ # Test chaining with slice
357
+ def test_chaining_with_slice
358
+ user_params = Class.new(ActionController::ApplicationParams) do
359
+ def self.name
360
+ 'UserParams'
361
+ end
362
+ allow :name
363
+ allow :email
364
+ end
365
+
366
+ ActionController::ParamsRegistry.register('User', user_params)
367
+
368
+ params = ActionController::Parameters.new(
369
+ user: {
370
+ name: 'John',
371
+ email: 'john@example.com',
372
+ age: 30
373
+ }
374
+ )
375
+
376
+ # Slice then transform
377
+ sliced = params.require(:user).slice(:name, :email, :age)
378
+ permitted = sliced.transform_params()
379
+
380
+ assert_equal 'John', permitted[:name]
381
+ assert_equal 'john@example.com', permitted[:email]
382
+ assert_nil permitted[:age] # not allowed
383
+ end
384
+
385
+ # Test with dup
386
+ def test_transform_params_with_dup
387
+ user_params = Class.new(ActionController::ApplicationParams) do
388
+ def self.name
389
+ 'UserParams'
390
+ end
391
+ allow :name
392
+ end
393
+
394
+ ActionController::ParamsRegistry.register('User', user_params)
395
+
396
+ params = ActionController::Parameters.new(
397
+ user: { name: 'John' }
398
+ )
399
+
400
+ user_params = params.require(:user)
401
+ duped = user_params.dup
402
+
403
+ # Both should work
404
+ permitted1 = user_params.transform_params()
405
+ permitted2 = duped.transform_params()
406
+
407
+ assert_equal 'John', permitted1[:name]
408
+ assert_equal 'John', permitted2[:name]
409
+ end
410
+
411
+ # Test multiple registrations for same model (last wins)
412
+ def test_multiple_registrations_last_wins
413
+ first_params = Class.new(ActionController::ApplicationParams) do
414
+ def self.name
415
+ 'FirstParams'
416
+ end
417
+ allow :field1
418
+ end
419
+
420
+ second_params = Class.new(ActionController::ApplicationParams) do
421
+ def self.name
422
+ 'SecondParams'
423
+ end
424
+ allow :field2
425
+ end
426
+
427
+ ActionController::ParamsRegistry.register('Test', first_params)
428
+ ActionController::ParamsRegistry.register('Test', second_params)
429
+
430
+ params = ActionController::Parameters.new(
431
+ test: {
432
+ field1: 'value1',
433
+ field2: 'value2'
434
+ }
435
+ )
436
+
437
+ permitted = params.require(:test).transform_params()
438
+
439
+ # Should use second registration
440
+ assert_nil permitted[:field1]
441
+ assert_equal 'value2', permitted[:field2]
442
+ end
443
+
444
+ # Test metadata in inheritance
445
+ def test_metadata_in_inheritance_chain
446
+ parent_params = Class.new(ActionController::ApplicationParams) do
447
+ def self.name
448
+ 'ParentParams'
449
+ end
450
+ allow :name
451
+ metadata :parent_meta
452
+ end
453
+
454
+ child_params = Class.new(parent_params) do
455
+ def self.name
456
+ 'ChildParams'
457
+ end
458
+ allow :email
459
+ metadata :child_meta
460
+ end
461
+
462
+ ActionController::ParamsRegistry.register('Child', child_params)
463
+
464
+ params = ActionController::Parameters.new(
465
+ child: {
466
+ name: 'Test',
467
+ email: 'test@example.com'
468
+ }
469
+ )
470
+
471
+ # Should accept both parent and child metadata
472
+ permitted = params.require(:child).transform_params(
473
+ parent_meta: 'parent_value',
474
+ child_meta: 'child_value',
475
+ current_user: Object.new
476
+ )
477
+
478
+ assert_equal 'Test', permitted[:name]
479
+ assert_equal 'test@example.com', permitted[:email]
480
+ end
481
+
482
+ # Test flags don't affect filtering
483
+ def test_flags_dont_affect_filtering
484
+ params_class = Class.new(ActionController::ApplicationParams) do
485
+ def self.name
486
+ 'FlaggedParams'
487
+ end
488
+ allow :name
489
+ flag :some_flag, true
490
+ flag :another_flag, 'value'
491
+ end
492
+
493
+ ActionController::ParamsRegistry.register('Flagged', params_class)
494
+
495
+ params = ActionController::Parameters.new(
496
+ flagged: {
497
+ name: 'Test',
498
+ some_flag: 'should not appear',
499
+ another_flag: 'should not appear'
500
+ }
501
+ )
502
+
503
+ permitted = params.require(:flagged).transform_params()
504
+
505
+ assert_equal 'Test', permitted[:name]
506
+ assert_nil permitted[:some_flag]
507
+ assert_nil permitted[:another_flag]
508
+ end
509
+
510
+ # Test empty registry
511
+ def test_empty_registry_all_transforms_return_empty
512
+ params = ActionController::Parameters.new(
513
+ anything: { field: 'value' }
514
+ )
515
+
516
+ permitted = params.require(:anything).transform_params()
517
+
518
+ assert permitted.permitted?
519
+ assert_nil permitted[:field]
520
+ end
521
+
522
+ # Test registry.registered_models
523
+ def test_registry_tracking
524
+ user_params = Class.new(ActionController::ApplicationParams) do
525
+ def self.name
526
+ 'UserParams'
527
+ end
528
+ allow :name
529
+ end
530
+
531
+ account_params = Class.new(ActionController::ApplicationParams) do
532
+ def self.name
533
+ 'AccountParams'
534
+ end
535
+ allow :title
536
+ end
537
+
538
+ ActionController::ParamsRegistry.register('User', user_params)
539
+ ActionController::ParamsRegistry.register('Account', account_params)
540
+
541
+ models = ActionController::ParamsRegistry.registered_models
542
+ assert_equal 2, models.length
543
+ assert_includes models, 'user'
544
+ assert_includes models, 'account'
545
+
546
+ # Both should work
547
+ user_p = ActionController::Parameters.new(user: { name: 'John' })
548
+ account_p = ActionController::Parameters.new(account: { title: 'My Account' })
549
+
550
+ assert_equal 'John', user_p.require(:user).transform_params()[:name]
551
+ assert_equal 'My Account', account_p.require(:account).transform_params()[:title]
552
+ end
553
+ end