wor-simple_crud 0.2.1 → 0.3.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/Gemfile +1 -0
  4. data/README.md +157 -2
  5. data/lib/simple_crud/authorization/adapter.rb +7 -0
  6. data/lib/simple_crud/authorization/pundit_adapter.rb +6 -0
  7. data/lib/simple_crud/config.rb +6 -1
  8. data/lib/simple_crud/controller_helpers.rb +119 -0
  9. data/lib/simple_crud/pagination/adapter.rb +7 -1
  10. data/lib/simple_crud/pagination/kaminari_adapter.rb +23 -2
  11. data/lib/simple_crud/pagination/pagy_adapter.rb +7 -2
  12. data/lib/simple_crud/pagination/will_paginate_adapter.rb +7 -3
  13. data/lib/simple_crud/pagination/wor_paginate_adapter.rb +7 -2
  14. data/lib/simple_crud/rspec/config.rb +61 -0
  15. data/lib/simple_crud/rspec/helpers.rb +147 -0
  16. data/lib/simple_crud/rspec.rb +34 -0
  17. data/lib/simple_crud/simple_crud_controller.rb +51 -36
  18. data/lib/simple_crud/version.rb +1 -1
  19. data/lib/spec/shared_contexts/authenticate_user.rb +1 -7
  20. data/lib/spec/shared_examples/authorization_adapter_authorize.rb +11 -0
  21. data/lib/spec/shared_examples/simple_crud_for_create.rb +43 -11
  22. data/lib/spec/shared_examples/simple_crud_for_create_with_block.rb +18 -0
  23. data/lib/spec/shared_examples/simple_crud_for_create_with_build.rb +15 -0
  24. data/lib/spec/shared_examples/simple_crud_for_destroy.rb +19 -8
  25. data/lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb +13 -0
  26. data/lib/spec/shared_examples/simple_crud_for_destroy_with_finder.rb +24 -0
  27. data/lib/spec/shared_examples/simple_crud_for_index.rb +8 -23
  28. data/lib/spec/shared_examples/simple_crud_for_index_with_block.rb +14 -0
  29. data/lib/spec/shared_examples/simple_crud_for_index_with_scope.rb +21 -0
  30. data/lib/spec/shared_examples/simple_crud_for_new.rb +29 -0
  31. data/lib/spec/shared_examples/simple_crud_for_new_with_block.rb +9 -0
  32. data/lib/spec/shared_examples/simple_crud_for_new_with_build.rb +13 -0
  33. data/lib/spec/shared_examples/simple_crud_for_show.rb +14 -10
  34. data/lib/spec/shared_examples/simple_crud_for_show_with_block.rb +11 -0
  35. data/lib/spec/shared_examples/simple_crud_for_show_with_finder.rb +24 -0
  36. data/lib/spec/shared_examples/simple_crud_for_update.rb +55 -15
  37. data/lib/spec/shared_examples/simple_crud_for_update_with_finder.rb +29 -0
  38. data/lib/spec/shared_examples/simple_crud_not_found_with_finder.rb +13 -0
  39. data/lib/spec/shared_examples/simple_crud_renders_template.rb +8 -0
  40. data/lib/spec/shared_examples/simple_crud_when_not_authorized.rb +18 -0
  41. data/lib/spec/shared_examples/simple_crud_without_authenticated_user.rb +9 -0
  42. data/lib/spec/shared_examples/unauthorized_when_not_logged_in.rb +2 -2
  43. data/spec/block/dummy_models_controller_spec.rb +8 -0
  44. data/spec/block_create/dummy_models_controller_spec.rb +7 -0
  45. data/spec/block_destroy/dummy_models_controller_spec.rb +7 -0
  46. data/spec/block_new/dummy_models_controller_spec.rb +7 -0
  47. data/spec/block_show/dummy_models_controller_spec.rb +7 -0
  48. data/spec/built/dummy_models_controller_spec.rb +8 -0
  49. data/spec/dummy/app/controllers/block/dummy_models_controller.rb +17 -0
  50. data/spec/dummy/app/controllers/block_create/dummy_models_controller.rb +13 -0
  51. data/spec/dummy/app/controllers/block_destroy/dummy_models_controller.rb +9 -0
  52. data/spec/dummy/app/controllers/block_modes/base_controller.rb +11 -0
  53. data/spec/dummy/app/controllers/block_new/dummy_models_controller.rb +9 -0
  54. data/spec/dummy/app/controllers/block_show/dummy_models_controller.rb +9 -0
  55. data/spec/dummy/app/controllers/built/dummy_models_controller.rb +21 -0
  56. data/spec/dummy/app/controllers/dummy_models_controller.rb +2 -1
  57. data/spec/dummy/app/controllers/finder/dummy_models_controller.rb +22 -0
  58. data/spec/dummy/app/controllers/html/dummy_models_controller.rb +7 -0
  59. data/spec/dummy/app/controllers/html_create/dummy_models_controller.rb +7 -0
  60. data/spec/dummy/app/controllers/html_destroy/dummy_models_controller.rb +7 -0
  61. data/spec/dummy/app/controllers/html_modes/base_controller.rb +17 -0
  62. data/spec/dummy/app/controllers/html_new/dummy_models_controller.rb +7 -0
  63. data/spec/dummy/app/controllers/html_scoped/dummy_models_controller.rb +8 -0
  64. data/spec/dummy/app/controllers/html_show/dummy_models_controller.rb +7 -0
  65. data/spec/dummy/app/controllers/html_update/dummy_models_controller.rb +7 -0
  66. data/spec/dummy/app/controllers/invalid_status/dummy_models_controller.rb +13 -0
  67. data/spec/dummy/app/controllers/nested/dummy_models_controller.rb +21 -0
  68. data/spec/dummy/app/controllers/nested_route/dummy_models_controller.rb +26 -0
  69. data/spec/dummy/app/controllers/redirect/dummy_models_controller.rb +10 -0
  70. data/spec/dummy/app/controllers/redirect_auth/dummy_models_controller.rb +28 -0
  71. data/spec/dummy/app/controllers/scoped/dummy_models_controller.rb +16 -0
  72. data/spec/dummy/app/controllers/scoped_params/dummy_models_controller.rb +16 -0
  73. data/spec/dummy/app/controllers/strict/dummy_models_controller.rb +21 -0
  74. data/spec/dummy/app/models/dummy_model.rb +4 -0
  75. data/spec/dummy/app/models/dummy_model_policy.rb +4 -0
  76. data/spec/dummy/app/views/block/dummy_models/index.html.erb +3 -0
  77. data/spec/dummy/app/views/block_new/dummy_models/new.html.erb +1 -0
  78. data/spec/dummy/app/views/block_show/dummy_models/show.html.erb +1 -0
  79. data/spec/dummy/app/views/html/dummy_models/index.html.erb +3 -0
  80. data/spec/dummy/app/views/html_create/dummy_models/new.html.erb +1 -0
  81. data/spec/dummy/app/views/html_destroy/dummy_models/show.html.erb +1 -0
  82. data/spec/dummy/app/views/html_new/dummy_models/new.html.erb +1 -0
  83. data/spec/dummy/app/views/html_scoped/dummy_models/index.html.erb +3 -0
  84. data/spec/dummy/app/views/html_show/dummy_models/show.html.erb +1 -0
  85. data/spec/dummy/app/views/html_update/dummy_models/edit.html.erb +1 -0
  86. data/spec/dummy/app/views/invalid_status/dummy_models/new.html.erb +1 -0
  87. data/spec/dummy/app/views/layouts/application.html.erb +4 -0
  88. data/spec/dummy/app/views/redirect/dummy_models/new.html.erb +1 -0
  89. data/spec/dummy/config/routes.rb +90 -0
  90. data/spec/dummy/db/migrate/20260806000000_add_slug_to_dummy_models.rb +7 -0
  91. data/spec/dummy/db/migrate/20260807000000_add_classroom_slug_to_dummy_models.rb +7 -0
  92. data/spec/dummy/db/schema.rb +3 -1
  93. data/spec/dummy/spec/factories/dummy_models.rb +1 -0
  94. data/spec/dummy_controller_spec.rb +1 -0
  95. data/spec/dummy_model_policy_spec.rb +8 -16
  96. data/spec/finder/dummy_models_controller_spec.rb +12 -0
  97. data/spec/html/dummy_models_controller_spec.rb +7 -0
  98. data/spec/html_create/dummy_models_controller_spec.rb +7 -0
  99. data/spec/html_destroy/dummy_models_controller_spec.rb +7 -0
  100. data/spec/html_new/dummy_models_controller_spec.rb +7 -0
  101. data/spec/html_scoped/dummy_models_controller_spec.rb +7 -0
  102. data/spec/html_show/dummy_models_controller_spec.rb +7 -0
  103. data/spec/html_update/dummy_models_controller_spec.rb +7 -0
  104. data/spec/invalid_status/dummy_models_controller_spec.rb +10 -0
  105. data/spec/nested/dummy_models_controller_spec.rb +12 -0
  106. data/spec/nested_route/dummy_models_controller_spec.rb +19 -0
  107. data/spec/redirect/dummy_models_controller_spec.rb +23 -0
  108. data/spec/redirect_auth/dummy_models_controller_spec.rb +11 -0
  109. data/spec/scoped/dummy_models_controller_spec.rb +7 -0
  110. data/spec/scoped_params/dummy_models_controller_spec.rb +11 -0
  111. data/spec/simple_crud/authorization/action_policy_adapter_spec.rb +1 -9
  112. data/spec/simple_crud/authorization/can_can_can_adapter_spec.rb +1 -7
  113. data/spec/simple_crud/authorization/pundit_adapter_spec.rb +41 -0
  114. data/spec/simple_crud/controller_helpers_spec.rb +49 -0
  115. data/spec/simple_crud/pagination/adapters_spec.rb +37 -13
  116. data/spec/simple_crud/rspec_config_spec.rb +67 -0
  117. data/spec/simple_crud_configuration_spec.rb +19 -16
  118. data/spec/simple_crud_controller_spec.rb +4 -4
  119. data/spec/spec_helper.rb +1 -0
  120. data/spec/strict/dummy_models_controller_spec.rb +23 -0
  121. metadata +87 -4
  122. data/lib/spec/shared_examples/helpers.rb +0 -50
@@ -35,15 +35,7 @@ describe SimpleCrud::Authorization::ActionPolicyAdapter do
35
35
  end
36
36
 
37
37
  describe '#authorize' do
38
- it 'passes for a record the policy allows' do
39
- expect { adapter.authorize(controller, ActionPolicyTestRecord.new(1)) }.not_to raise_error
40
- end
41
-
42
- it 'raises for a record the policy denies' do
43
- expect do
44
- adapter.authorize(controller, ActionPolicyTestRecord.new(2))
45
- end.to raise_error(ActionPolicy::Unauthorized)
46
- end
38
+ include_examples 'authorization adapter #authorize', ActionPolicyTestRecord, ActionPolicy::Unauthorized
47
39
  end
48
40
 
49
41
  describe '#policy_defined?' do
@@ -36,13 +36,7 @@ describe SimpleCrud::Authorization::CanCanCanAdapter do
36
36
  end
37
37
 
38
38
  describe '#authorize' do
39
- it 'passes for a record the ability grants' do
40
- expect { adapter.authorize(controller, CanCanCanTestRecord.new(1)) }.not_to raise_error
41
- end
42
-
43
- it 'raises for a record the ability denies' do
44
- expect { adapter.authorize(controller, CanCanCanTestRecord.new(2)) }.to raise_error(CanCan::AccessDenied)
45
- end
39
+ include_examples 'authorization adapter #authorize', CanCanCanTestRecord, CanCan::AccessDenied
46
40
  end
47
41
 
48
42
  describe '#policy_defined?' do
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'simple_crud/authorization/pundit_adapter'
5
+
6
+ class PolicyScopeTestModel; end
7
+
8
+ class PolicyScopeTestModelPolicy
9
+ class Scope
10
+ def initialize(user, scope)
11
+ @user = user
12
+ @scope = scope
13
+ end
14
+
15
+ def resolve
16
+ :scoped
17
+ end
18
+ end
19
+ end
20
+
21
+ class PolicyScopeTestController
22
+ include Pundit::Authorization
23
+
24
+ attr_accessor :current_user
25
+ end
26
+
27
+ describe SimpleCrud::Authorization::PunditAdapter do
28
+ subject(:adapter) { described_class.new }
29
+
30
+ let(:controller) { PolicyScopeTestController.new.tap { |c| c.current_user = :user } }
31
+
32
+ describe '#policy_scope' do
33
+ it 'returns the resolved policy scope when one is defined' do
34
+ expect(adapter.policy_scope(controller, PolicyScopeTestModel)).to eq(:scoped)
35
+ end
36
+
37
+ it 'falls back to the full relation when no scope is defined' do
38
+ expect(adapter.policy_scope(controller, DummyModel).to_sql).to eq(DummyModel.all.to_sql)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'simple_crud/controller_helpers'
5
+
6
+ describe SimpleCrud::ControllerHelpers do
7
+ subject(:helpers) do
8
+ Class.new do
9
+ extend SimpleCrud::ControllerHelpers
10
+ end
11
+ end
12
+
13
+ describe '.call_scope' do
14
+ after { SimpleCrud::Config.user_method = :current_user }
15
+
16
+ it 'passes nil as the user when the controller has no current_user' do
17
+ controller = double(params: { status: 'active' })
18
+ scope = ->(user) { user ? :scoped_to_user : :unscoped }
19
+
20
+ expect(helpers.call_scope(scope, controller)).to eq(:unscoped)
21
+ end
22
+
23
+ it 'still passes params as the second argument when the scope takes two' do
24
+ controller = double(params: { status: 'active' })
25
+ scope = ->(user, params) { [user, params[:status]] }
26
+
27
+ expect(helpers.call_scope(scope, controller)).to eq([nil, 'active'])
28
+ end
29
+
30
+ it 'resolves the user via Config.user_method when overridden' do
31
+ SimpleCrud::Config.user_method = :current_admin
32
+ admin = double
33
+ controller = double(params: {}, current_admin: admin)
34
+ scope = ->(user) { user }
35
+
36
+ expect(helpers.call_scope(scope, controller)).to eq(admin)
37
+ end
38
+ end
39
+
40
+ describe '.find_record' do
41
+ it 'raises when a custom finder returns something other than a record' do
42
+ controller = double(params: {})
43
+ parameters = { finder: ->(_params) { DummyModel.where(name: 'x') } }
44
+
45
+ expect { helpers.find_record(DummyModel, controller, parameters) }
46
+ .to raise_error(ActiveRecord::RecordNotFound, /must return a single record/)
47
+ end
48
+ end
49
+ end
@@ -22,27 +22,51 @@ describe 'pagination adapters', type: :request do
22
22
  SimpleCrud::Config.pagination_adapter = original_adapter
23
23
  end
24
24
 
25
- it 'paginates with Kaminari' do
26
- SimpleCrud::Config.pagination_adapter = SimpleCrud::Pagination::KaminariAdapter.new
25
+ {
26
+ 'Kaminari' => [SimpleCrud::Pagination::KaminariAdapter, { per_page: 5 }],
27
+ 'will_paginate' => [SimpleCrud::Pagination::WillPaginateAdapter, { per_page: 5 }],
28
+ 'Pagy' => [SimpleCrud::Pagination::PagyAdapter, { limit: 5 }]
29
+ }.each do |name, (adapter_class, pagination_params)|
30
+ it "paginates with #{name}" do
31
+ SimpleCrud::Config.pagination_adapter = adapter_class.new
27
32
 
28
- get '/dummy_models', params: { page: 2, per_page: 5 }, headers: auth_headers
33
+ get '/dummy_models', params: { page: 2 }.merge(pagination_params), headers: auth_headers
29
34
 
30
- expect(response.parsed_body.size).to eq(5)
35
+ expect(response.parsed_body.size).to eq(5)
36
+ end
31
37
  end
32
38
 
33
- it 'paginates with will_paginate' do
34
- SimpleCrud::Config.pagination_adapter = SimpleCrud::Pagination::WillPaginateAdapter.new
39
+ describe 'Kaminari edge cases' do
40
+ before { SimpleCrud::Config.pagination_adapter = SimpleCrud::Pagination::KaminariAdapter.new }
35
41
 
36
- get '/dummy_models', params: { page: 2, per_page: 5 }, headers: auth_headers
42
+ # Offset math must not trust string params: "5" * 2 == "55".
43
+ it 'paginates past page 2 when params arrive as strings' do
44
+ get '/dummy_models', params: { page: '3', per_page: '5' }, headers: auth_headers
37
45
 
38
- expect(response.parsed_body.size).to eq(5)
39
- end
46
+ expected_ids = DummyModel.offset(10).limit(5).pluck(:id)
47
+ expect(response.parsed_body.map { |record| record['id'] }).to match_array(expected_ids)
48
+ end
49
+
50
+ context 'with Kaminari.config.max_per_page set' do
51
+ let!(:original_max_per_page) { Kaminari.config.max_per_page }
52
+
53
+ before do
54
+ Kaminari.config.max_per_page = 8
55
+ end
56
+
57
+ after { Kaminari.config.max_per_page = original_max_per_page }
58
+
59
+ it 'caps per_page at the configured ceiling' do
60
+ get '/dummy_models', params: { page: 1, per_page: '100' }, headers: auth_headers
40
61
 
41
- it 'paginates with Pagy' do
42
- SimpleCrud::Config.pagination_adapter = SimpleCrud::Pagination::PagyAdapter.new
62
+ expect(response.parsed_body.size).to eq(8)
63
+ end
64
+ end
43
65
 
44
- get '/dummy_models', params: { page: 2, limit: 5 }, headers: auth_headers
66
+ it 'falls back to page 1 for non-numeric page params' do
67
+ get '/dummy_models', params: { page: 'abc', per_page: '5' }, headers: auth_headers
45
68
 
46
- expect(response.parsed_body.size).to eq(5)
69
+ expect(response.parsed_body.size).to eq(5)
70
+ end
47
71
  end
48
72
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SimpleCrud::RSpec do
6
+ let(:config) { SimpleCrud::RSpec::Config.instance }
7
+
8
+ describe '.configure' do
9
+ it 'yields the config instance' do
10
+ expect { |b| described_class.configure(&b) }.to yield_with_args(config)
11
+ end
12
+ end
13
+
14
+ describe SimpleCrud::RSpec::Config do
15
+ it 'defaults the owner and finder settings', :aggregate_failures do
16
+ expect(config.owner_association).to eq(:user)
17
+ expect(config.finder_key).to eq(:slug)
18
+ end
19
+
20
+ it 'defaults the model validation settings', :aggregate_failures do
21
+ expect(config.required_attribute).to eq(:name)
22
+ expect(config.required_error).to eq("Name can't be blank")
23
+ end
24
+
25
+ it 'defaults the html and status settings', :aggregate_failures do
26
+ expect(config.unauthenticated_status).to eq(:unauthorized)
27
+ expect(config.assert_html_template).to be true
28
+ end
29
+
30
+ it 'defaults to flat params', :aggregate_failures do
31
+ expect(config.params_key).to be_nil
32
+ expect(body_params(name: 'x')).to eq(name: 'x')
33
+ end
34
+
35
+ it 'omits owner params when there is no owner association', :aggregate_failures do
36
+ with_config_override(:owner_association, nil) do
37
+ expect(owner_params).to eq({})
38
+ expect(model_attributes).to eq({})
39
+ end
40
+ end
41
+
42
+ it 'lets settings be overridden and restored', :aggregate_failures do
43
+ original = config.required_attribute
44
+
45
+ SimpleCrud::RSpec.configure { |c| c.required_attribute = :title }
46
+ expect(config.required_attribute).to eq(:title)
47
+
48
+ SimpleCrud::RSpec.configure { |c| c.required_attribute = original }
49
+ expect(config.required_attribute).to eq(:name)
50
+ end
51
+ end
52
+
53
+ describe SimpleCrud::RSpec::Helpers do
54
+ it 'resolves plain settings as-is' do
55
+ expect(resolve(config.owner_association)).to eq(:user)
56
+ end
57
+
58
+ it 'resolves callable settings in the example context' do
59
+ original = config.required_attribute
60
+
61
+ SimpleCrud::RSpec.configure { |c| c.required_attribute = -> { :title } }
62
+ expect(required_attribute).to eq(:title)
63
+
64
+ SimpleCrud::RSpec.configure { |c| c.required_attribute = original }
65
+ end
66
+ end
67
+ end
@@ -18,24 +18,19 @@ describe SimpleCrud do
18
18
  expect { |b| described_class.configure(&b) }.to yield_with_args(SimpleCrud::Config)
19
19
  end
20
20
 
21
- it 'lets a custom authorization adapter replace the Pundit default' do
22
- original_adapter = SimpleCrud::Config.authorization_adapter
23
- custom_adapter = instance_double(SimpleCrud::Authorization::PunditAdapter)
21
+ {
22
+ authorization_adapter: [SimpleCrud::Authorization::PunditAdapter, 'the Pundit default'],
23
+ pagination_adapter: [SimpleCrud::Pagination::WorPaginateAdapter, 'the wor-paginate default']
24
+ }.each do |adapter_name, (adapter_class, default_description)|
25
+ it "lets a custom #{adapter_name.to_s.sub('_adapter', '')} adapter replace #{default_description}" do
26
+ original_adapter = SimpleCrud::Config.public_send(adapter_name)
27
+ custom_adapter = instance_double(adapter_class)
24
28
 
25
- described_class.configure { |config| config.authorization_adapter = custom_adapter }
26
- expect(SimpleCrud::Config.authorization_adapter).to eq(custom_adapter)
29
+ described_class.configure { |config| config.public_send("#{adapter_name}=", custom_adapter) }
30
+ expect(SimpleCrud::Config.public_send(adapter_name)).to eq(custom_adapter)
27
31
 
28
- SimpleCrud::Config.authorization_adapter = original_adapter
29
- end
30
-
31
- it 'lets a custom pagination adapter replace the wor-paginate default' do
32
- original_adapter = SimpleCrud::Config.pagination_adapter
33
- custom_adapter = instance_double(SimpleCrud::Pagination::WorPaginateAdapter)
34
-
35
- described_class.configure { |config| config.pagination_adapter = custom_adapter }
36
- expect(SimpleCrud::Config.pagination_adapter).to eq(custom_adapter)
37
-
38
- SimpleCrud::Config.pagination_adapter = original_adapter
32
+ SimpleCrud::Config.public_send("#{adapter_name}=", original_adapter)
33
+ end
39
34
  end
40
35
  end
41
36
 
@@ -49,6 +44,10 @@ describe SimpleCrud do
49
44
  it 'requires #policy_defined? to be implemented' do
50
45
  expect { adapter.policy_defined?(nil) }.to raise_error(NotImplementedError)
51
46
  end
47
+
48
+ it 'defaults #policy_scope to the full relation' do
49
+ expect(adapter.policy_scope(nil, DummyModel).to_sql).to eq(DummyModel.all.to_sql)
50
+ end
52
51
  end
53
52
 
54
53
  describe SimpleCrud::Pagination::Adapter do
@@ -57,5 +56,9 @@ describe SimpleCrud do
57
56
  it 'requires #paginate to be implemented' do
58
57
  expect { adapter.paginate(nil, nil, nil) }.to raise_error(NotImplementedError)
59
58
  end
59
+
60
+ it 'requires #paginated_records to be implemented' do
61
+ expect { adapter.paginated_records(nil, nil, nil) }.to raise_error(NotImplementedError)
62
+ end
60
63
  end
61
64
  end
@@ -14,10 +14,10 @@ describe SimpleCrudController do
14
14
  expect { DummyModelsController.check_policies(authorize: true) }.not_to raise_error
15
15
  end
16
16
 
17
- it 'throws when no matching policy exists' do
17
+ it 'raises when no matching policy exists' do
18
18
  expect do
19
19
  UnpoliceableThingsController.check_policies(authorize: true)
20
- end.to raise_error(UncaughtThrowError)
20
+ end.to raise_error(ArgumentError)
21
21
  end
22
22
  end
23
23
 
@@ -32,10 +32,10 @@ describe SimpleCrudController do
32
32
  end.not_to raise_error
33
33
  end
34
34
 
35
- it 'throws when no matching serializer exists' do
35
+ it 'raises when no matching serializer exists' do
36
36
  expect do
37
37
  DummyModelsController.check_serializer(serializer: 'NonExistentSerializer')
38
- end.to raise_error(UncaughtThrowError)
38
+ end.to raise_error(ArgumentError)
39
39
  end
40
40
  end
41
41
  end
data/spec/spec_helper.rb CHANGED
@@ -13,6 +13,7 @@ require 'rspec/rails'
13
13
  require 'pundit/rspec'
14
14
  require 'wor/paginate/rspec'
15
15
  require 'simple_crud/rspec'
16
+ SimpleCrud::RSpec.install!
16
17
  require 'devise'
17
18
  require 'devise/jwt/test_helpers'
18
19
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Strict::DummyModelsController, type: :controller do
6
+ include_examples 'simple crud for create'
7
+ include_examples 'simple crud for update'
8
+
9
+ describe 'with raise_on_invalid' do
10
+ include_context 'with authenticated user'
11
+
12
+ it 'raises on invalid create, rescued to unprocessable' do
13
+ post :create, params: { user_id: current_user.id }
14
+ expect(response).to have_http_status(ApplicationController::UNPROCESSABLE_STATUS)
15
+ end
16
+
17
+ it 'raises on invalid update, rescued to unprocessable' do
18
+ record = create(:dummy_model, user: current_user)
19
+ put :update, params: { id: record.id, name: nil, user_id: current_user.id }
20
+ expect(response).to have_http_status(ApplicationController::UNPROCESSABLE_STATUS)
21
+ end
22
+ end
23
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wor-simple_crud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - icoluccio
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-08-04 00:00:00.000000000 Z
10
+ date: 2026-08-21 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -53,29 +53,78 @@ files:
53
53
  - lib/simple_crud/authorization/can_can_can_adapter.rb
54
54
  - lib/simple_crud/authorization/pundit_adapter.rb
55
55
  - lib/simple_crud/config.rb
56
+ - lib/simple_crud/controller_helpers.rb
56
57
  - lib/simple_crud/pagination/adapter.rb
57
58
  - lib/simple_crud/pagination/kaminari_adapter.rb
58
59
  - lib/simple_crud/pagination/pagy_adapter.rb
59
60
  - lib/simple_crud/pagination/will_paginate_adapter.rb
60
61
  - lib/simple_crud/pagination/wor_paginate_adapter.rb
61
62
  - lib/simple_crud/rspec.rb
63
+ - lib/simple_crud/rspec/config.rb
64
+ - lib/simple_crud/rspec/helpers.rb
62
65
  - lib/simple_crud/simple_crud_controller.rb
63
66
  - lib/simple_crud/version.rb
64
67
  - lib/spec/matchers/have_been_serialized_with.rb
65
68
  - lib/spec/response_helper.rb
66
69
  - lib/spec/shared_contexts/authenticate_user.rb
67
- - lib/spec/shared_examples/helpers.rb
70
+ - lib/spec/shared_examples/authorization_adapter_authorize.rb
68
71
  - lib/spec/shared_examples/simple_crud_for_create.rb
72
+ - lib/spec/shared_examples/simple_crud_for_create_with_block.rb
73
+ - lib/spec/shared_examples/simple_crud_for_create_with_build.rb
69
74
  - lib/spec/shared_examples/simple_crud_for_destroy.rb
75
+ - lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb
76
+ - lib/spec/shared_examples/simple_crud_for_destroy_with_finder.rb
70
77
  - lib/spec/shared_examples/simple_crud_for_index.rb
78
+ - lib/spec/shared_examples/simple_crud_for_index_with_block.rb
79
+ - lib/spec/shared_examples/simple_crud_for_index_with_scope.rb
80
+ - lib/spec/shared_examples/simple_crud_for_new.rb
81
+ - lib/spec/shared_examples/simple_crud_for_new_with_block.rb
82
+ - lib/spec/shared_examples/simple_crud_for_new_with_build.rb
71
83
  - lib/spec/shared_examples/simple_crud_for_show.rb
84
+ - lib/spec/shared_examples/simple_crud_for_show_with_block.rb
85
+ - lib/spec/shared_examples/simple_crud_for_show_with_finder.rb
72
86
  - lib/spec/shared_examples/simple_crud_for_update.rb
87
+ - lib/spec/shared_examples/simple_crud_for_update_with_finder.rb
88
+ - lib/spec/shared_examples/simple_crud_not_found_with_finder.rb
89
+ - lib/spec/shared_examples/simple_crud_renders_template.rb
90
+ - lib/spec/shared_examples/simple_crud_when_not_authorized.rb
91
+ - lib/spec/shared_examples/simple_crud_without_authenticated_user.rb
73
92
  - lib/spec/shared_examples/unauthorized_when_not_logged_in.rb
74
93
  - pull_request_template.md
94
+ - spec/block/dummy_models_controller_spec.rb
95
+ - spec/block_create/dummy_models_controller_spec.rb
96
+ - spec/block_destroy/dummy_models_controller_spec.rb
97
+ - spec/block_new/dummy_models_controller_spec.rb
98
+ - spec/block_show/dummy_models_controller_spec.rb
99
+ - spec/built/dummy_models_controller_spec.rb
75
100
  - spec/dummy/Rakefile
76
101
  - spec/dummy/app/assets/config/manifest.js
77
102
  - spec/dummy/app/controllers/application_controller.rb
103
+ - spec/dummy/app/controllers/block/dummy_models_controller.rb
104
+ - spec/dummy/app/controllers/block_create/dummy_models_controller.rb
105
+ - spec/dummy/app/controllers/block_destroy/dummy_models_controller.rb
106
+ - spec/dummy/app/controllers/block_modes/base_controller.rb
107
+ - spec/dummy/app/controllers/block_new/dummy_models_controller.rb
108
+ - spec/dummy/app/controllers/block_show/dummy_models_controller.rb
109
+ - spec/dummy/app/controllers/built/dummy_models_controller.rb
78
110
  - spec/dummy/app/controllers/dummy_models_controller.rb
111
+ - spec/dummy/app/controllers/finder/dummy_models_controller.rb
112
+ - spec/dummy/app/controllers/html/dummy_models_controller.rb
113
+ - spec/dummy/app/controllers/html_create/dummy_models_controller.rb
114
+ - spec/dummy/app/controllers/html_destroy/dummy_models_controller.rb
115
+ - spec/dummy/app/controllers/html_modes/base_controller.rb
116
+ - spec/dummy/app/controllers/html_new/dummy_models_controller.rb
117
+ - spec/dummy/app/controllers/html_scoped/dummy_models_controller.rb
118
+ - spec/dummy/app/controllers/html_show/dummy_models_controller.rb
119
+ - spec/dummy/app/controllers/html_update/dummy_models_controller.rb
120
+ - spec/dummy/app/controllers/invalid_status/dummy_models_controller.rb
121
+ - spec/dummy/app/controllers/nested/dummy_models_controller.rb
122
+ - spec/dummy/app/controllers/nested_route/dummy_models_controller.rb
123
+ - spec/dummy/app/controllers/redirect/dummy_models_controller.rb
124
+ - spec/dummy/app/controllers/redirect_auth/dummy_models_controller.rb
125
+ - spec/dummy/app/controllers/scoped/dummy_models_controller.rb
126
+ - spec/dummy/app/controllers/scoped_params/dummy_models_controller.rb
127
+ - spec/dummy/app/controllers/strict/dummy_models_controller.rb
79
128
  - spec/dummy/app/controllers/without_pagination/dummy_models_controller.rb
80
129
  - spec/dummy/app/models/application_record.rb
81
130
  - spec/dummy/app/models/concerns/.keep
@@ -83,6 +132,19 @@ files:
83
132
  - spec/dummy/app/models/dummy_model_policy.rb
84
133
  - spec/dummy/app/models/user.rb
85
134
  - spec/dummy/app/serializers/dummy_model_serializer.rb
135
+ - spec/dummy/app/views/block/dummy_models/index.html.erb
136
+ - spec/dummy/app/views/block_new/dummy_models/new.html.erb
137
+ - spec/dummy/app/views/block_show/dummy_models/show.html.erb
138
+ - spec/dummy/app/views/html/dummy_models/index.html.erb
139
+ - spec/dummy/app/views/html_create/dummy_models/new.html.erb
140
+ - spec/dummy/app/views/html_destroy/dummy_models/show.html.erb
141
+ - spec/dummy/app/views/html_new/dummy_models/new.html.erb
142
+ - spec/dummy/app/views/html_scoped/dummy_models/index.html.erb
143
+ - spec/dummy/app/views/html_show/dummy_models/show.html.erb
144
+ - spec/dummy/app/views/html_update/dummy_models/edit.html.erb
145
+ - spec/dummy/app/views/invalid_status/dummy_models/new.html.erb
146
+ - spec/dummy/app/views/layouts/application.html.erb
147
+ - spec/dummy/app/views/redirect/dummy_models/new.html.erb
86
148
  - spec/dummy/bin/bundle
87
149
  - spec/dummy/bin/rails
88
150
  - spec/dummy/bin/rake
@@ -104,6 +166,8 @@ files:
104
166
  - spec/dummy/db/migrate/20170626184159_create_dummy_models.rb
105
167
  - spec/dummy/db/migrate/20200110191019_devise_create_users.rb
106
168
  - spec/dummy/db/migrate/20200120165657_add_user_id_to_dummy_models.rb
169
+ - spec/dummy/db/migrate/20260806000000_add_slug_to_dummy_models.rb
170
+ - spec/dummy/db/migrate/20260807000000_add_classroom_slug_to_dummy_models.rb
107
171
  - spec/dummy/db/schema.rb
108
172
  - spec/dummy/lib/assets/.keep
109
173
  - spec/dummy/log/.keep
@@ -112,13 +176,32 @@ files:
112
176
  - spec/dummy_controller_spec.rb
113
177
  - spec/dummy_model_policy_spec.rb
114
178
  - spec/dummy_model_serializer_spec.rb
179
+ - spec/finder/dummy_models_controller_spec.rb
180
+ - spec/html/dummy_models_controller_spec.rb
181
+ - spec/html_create/dummy_models_controller_spec.rb
182
+ - spec/html_destroy/dummy_models_controller_spec.rb
183
+ - spec/html_new/dummy_models_controller_spec.rb
184
+ - spec/html_scoped/dummy_models_controller_spec.rb
185
+ - spec/html_show/dummy_models_controller_spec.rb
186
+ - spec/html_update/dummy_models_controller_spec.rb
187
+ - spec/invalid_status/dummy_models_controller_spec.rb
188
+ - spec/nested/dummy_models_controller_spec.rb
189
+ - spec/nested_route/dummy_models_controller_spec.rb
190
+ - spec/redirect/dummy_models_controller_spec.rb
191
+ - spec/redirect_auth/dummy_models_controller_spec.rb
192
+ - spec/scoped/dummy_models_controller_spec.rb
193
+ - spec/scoped_params/dummy_models_controller_spec.rb
115
194
  - spec/simple_crud/authorization/action_policy_adapter_spec.rb
116
195
  - spec/simple_crud/authorization/can_can_can_adapter_spec.rb
196
+ - spec/simple_crud/authorization/pundit_adapter_spec.rb
197
+ - spec/simple_crud/controller_helpers_spec.rb
117
198
  - spec/simple_crud/pagination/adapters_spec.rb
199
+ - spec/simple_crud/rspec_config_spec.rb
118
200
  - spec/simple_crud_configuration_spec.rb
119
201
  - spec/simple_crud_controller_spec.rb
120
202
  - spec/spec_helper.rb
121
203
  - spec/spy.rb
204
+ - spec/strict/dummy_models_controller_spec.rb
122
205
  - spec/without_pagination/dummy_models_controller_spec.rb
123
206
  - wor-simple_crud.gemspec
124
207
  homepage: https://github.com/icoluccio/simple-crud
@@ -139,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
222
  - !ruby/object:Gem::Version
140
223
  version: '0'
141
224
  requirements: []
142
- rubygems_version: 4.0.6
225
+ rubygems_version: 3.6.9
143
226
  specification_version: 4
144
227
  summary: Simplified CRUD endpoints for Rails API controllers
145
228
  test_files: []
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- def get_option(method, option)
4
- described_class.instance_variable_get(:@simple_crud_metadata)[method][option]
5
- end
6
-
7
- %i[paginate authorize authenticate serializer].each do |option|
8
- define_method("check_#{option}") do |method|
9
- get_option(method, option)
10
- end
11
- end
12
-
13
- def model_class
14
- described_class.to_s.split('::')
15
- .last.sub('Controller', '').singularize.underscore
16
- end
17
-
18
- def model_serializer
19
- defined?(serializer) ? serializer : "#{model.class}_serializer".classify.constantize
20
- end
21
-
22
- def model
23
- @model ||= create(model_class, **model_attributes)
24
- end
25
-
26
- def model_attributes
27
- respond_to?(:current_user) ? { user: current_user } : {}
28
- end
29
-
30
- def model_class_object
31
- model_class.classify.constantize
32
- end
33
-
34
- def policy_class_object
35
- "#{model_class_object}Policy".classify.constantize
36
- end
37
-
38
- def make_policies_fail(method)
39
- allow(policy_class_object).to receive(:new)
40
- .and_return(instance_double(policy_class_object, "#{method}?" => false))
41
- end
42
-
43
- def make_policies_succeed(method)
44
- allow(policy_class_object).to receive(:new)
45
- .and_return(instance_double(policy_class_object, "#{method}?" => true))
46
- end
47
-
48
- def model_params
49
- @model_params ||= attributes_for(model_class)
50
- end