wor-simple_crud 0.6.0 → 0.8.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/README.md +116 -8
  4. data/lib/simple_crud/action_context.rb +16 -2
  5. data/lib/simple_crud/index_context.rb +21 -10
  6. data/lib/simple_crud/persistence_context.rb +16 -5
  7. data/lib/simple_crud/relation_source.rb +107 -0
  8. data/lib/simple_crud/rspec/config.rb +2 -0
  9. data/lib/simple_crud/rspec/helpers/controller_options.rb +1 -1
  10. data/lib/simple_crud/rspec/helpers/policies.rb +3 -3
  11. data/lib/simple_crud/rspec/helpers/requests.rb +4 -1
  12. data/lib/simple_crud/rspec/helpers/settings.rb +10 -1
  13. data/lib/simple_crud/rspec.rb +6 -0
  14. data/lib/simple_crud/serializer.rb +2 -7
  15. data/lib/simple_crud/simple_crud_controller.rb +28 -7
  16. data/lib/simple_crud/version.rb +1 -1
  17. data/lib/spec/matchers/render_or_redirect.rb +7 -0
  18. data/lib/spec/shared_examples/simple_crud_for_create.rb +1 -1
  19. data/lib/spec/shared_examples/simple_crud_for_create_with_block.rb +2 -2
  20. data/lib/spec/shared_examples/simple_crud_for_create_with_build.rb +8 -2
  21. data/lib/spec/shared_examples/simple_crud_for_destroy.rb +11 -9
  22. data/lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb +1 -1
  23. data/lib/spec/shared_examples/simple_crud_for_edit.rb +16 -14
  24. data/lib/spec/shared_examples/simple_crud_for_edit_with_block.rb +22 -0
  25. data/lib/spec/shared_examples/simple_crud_for_edit_with_finder.rb +34 -0
  26. data/lib/spec/shared_examples/simple_crud_for_index_with_scope.rb +15 -12
  27. data/lib/spec/shared_examples/simple_crud_for_nested_resource.rb +21 -0
  28. data/lib/spec/shared_examples/simple_crud_for_new_with_block.rb +1 -1
  29. data/lib/spec/shared_examples/simple_crud_for_new_with_build.rb +11 -5
  30. data/lib/spec/shared_examples/simple_crud_for_owned_resource.rb +45 -0
  31. data/lib/spec/shared_examples/simple_crud_for_show.rb +10 -8
  32. data/lib/spec/shared_examples/simple_crud_for_show_with_block.rb +1 -1
  33. data/lib/spec/shared_examples/simple_crud_for_update.rb +14 -12
  34. data/lib/spec/shared_examples/simple_crud_for_update_with_block.rb +40 -0
  35. data/lib/spec/shared_examples/simple_crud_for_update_with_finder.rb +7 -4
  36. data/lib/spec/shared_examples/simple_crud_not_found_with_finder.rb +9 -7
  37. data/spec/block_create_redirect/dummy_models_controller_spec.rb +11 -0
  38. data/spec/block_edit/dummy_models_controller_spec.rb +7 -0
  39. data/spec/block_finder/dummy_models_controller_spec.rb +2 -0
  40. data/spec/block_policy_show/dummy_models_controller_spec.rb +7 -0
  41. data/spec/block_redirect/dummy_models_controller_spec.rb +1 -0
  42. data/spec/block_update/dummy_models_controller_spec.rb +11 -0
  43. data/spec/dummy/app/controllers/block_create_redirect/dummy_models_controller.rb +13 -0
  44. data/spec/dummy/app/controllers/block_edit/dummy_models_controller.rb +9 -0
  45. data/spec/dummy/app/controllers/block_policy_show/dummy_models_controller.rb +12 -0
  46. data/spec/dummy/app/controllers/block_update/dummy_models_controller.rb +9 -0
  47. data/spec/dummy/app/controllers/html_notice/dummy_models_controller.rb +11 -0
  48. data/spec/dummy/app/controllers/owned/dummy_models_controller.rb +21 -0
  49. data/spec/dummy/app/controllers/parented/dummy_models_controller.rb +24 -0
  50. data/spec/dummy/app/controllers/scoped_order/dummy_models_controller.rb +11 -0
  51. data/spec/dummy/app/controllers/scoped_symbol/dummy_models_controller.rb +11 -0
  52. data/spec/dummy/app/controllers/singular/dummy_models_controller.rb +12 -0
  53. data/spec/dummy/app/models/dummy_model.rb +4 -0
  54. data/spec/dummy/app/serializers/dummy_model_plain_serializer.rb +8 -0
  55. data/spec/dummy/app/views/block_edit/dummy_models/edit.html.erb +1 -0
  56. data/spec/dummy/app/views/block_policy_show/dummy_models/show.html.erb +1 -0
  57. data/spec/dummy/app/views/html_notice/dummy_models/edit.html.erb +1 -0
  58. data/spec/dummy/config/routes.rb +40 -0
  59. data/spec/finder/dummy_models_controller_spec.rb +6 -0
  60. data/spec/html_finder/dummy_models_controller_spec.rb +1 -0
  61. data/spec/html_notice/dummy_models_controller_spec.rb +38 -0
  62. data/spec/owned/dummy_models_controller_spec.rb +12 -0
  63. data/spec/parented/dummy_models_controller_spec.rb +39 -0
  64. data/spec/scoped_order/dummy_models_controller_spec.rb +10 -0
  65. data/spec/scoped_symbol/dummy_models_controller_spec.rb +7 -0
  66. data/spec/simple_crud/action_context_spec.rb +15 -0
  67. data/spec/simple_crud/index_context_spec.rb +49 -0
  68. data/spec/simple_crud/relation_source_spec.rb +125 -0
  69. data/spec/simple_crud/serializer_spec.rb +21 -0
  70. data/spec/simple_crud/show_context_spec.rb +5 -0
  71. data/spec/simple_crud_controller_spec.rb +15 -0
  72. data/spec/singular/dummy_models_controller_spec.rb +10 -0
  73. metadata +34 -2
@@ -4,6 +4,7 @@ require 'active_support/all'
4
4
  require_relative 'config'
5
5
  require_relative 'serializer'
6
6
  require_relative 'cache_helpers'
7
+ require_relative 'relation_source'
7
8
  require_relative 'action_context'
8
9
  require_relative 'persistence_context'
9
10
  require_relative 'show_context'
@@ -32,16 +33,26 @@ module SimpleCrudController
32
33
  ### serializer: use a particular serializer (both each_serializer and serializer)
33
34
  ### html: render the action's ERB template instead of JSON (index/show/new/edit/create/update/destroy)
34
35
  ### finder: custom record lookup (Proc/lambda or Symbol) for :show/:update/:destroy/:edit
35
- ### scope: custom index scope (Proc/lambda taking current_user and optional params), overrides policy_scope
36
+ ### scope: custom index scope for :index, overriding policy_scope. A Proc/lambda run with the
37
+ ### controller as self (arity 0 no args, arity 1 the user, else user and params), or a Symbol
38
+ ### naming a model class method taking (current_user, params)
36
39
  ### build: custom record builder (Proc/lambda) for :new/:create, invoked with the controller as self
40
+ ### owned_by: association on Config.user_method for the default finder/build/index-scope
41
+ ### parent: nested-route parent for the same defaults; exclusive with owned_by:
42
+ ### parent_association: parent association when it differs from the controller model's plural
43
+ ### notice:/alert: flash on the html: create/update/destroy success redirect / failure re-render
37
44
  ### raise_on_invalid: use strict create!/update! semantics instead of returning 422
38
45
  ### A block given to simple_crud_for renders explicitly: it receives the records for :index,
39
46
  ### the record for :show/:new/:edit, or the record and a saved flag for :create/:update/:destroy
47
+ ### An Array of actions declares several at once with the same options
40
48
  def simple_crud_for(method, parameters = {}, &block)
49
+ return method.each { |name| simple_crud_for(name, parameters.dup, &block) } if method.is_a?(Array)
50
+
41
51
  parameters[:block] = true if block
42
52
  parameters = parameters_with_defaults(parameters)
43
53
  klass = simple_crud_controller_model
44
54
  check_valid_method(method)
55
+ check_relation_source(parameters)
45
56
  check_policies(parameters)
46
57
  check_serializer(parameters)
47
58
  define_method(method, send("crud_lambda_for_#{method}", klass, parameters, &block))
@@ -62,18 +73,22 @@ module SimpleCrudController
62
73
  end
63
74
 
64
75
  def parameters_with_defaults(parameters)
65
- defaults = {
66
- authorize: true, paginate: true, authenticate: true, authenticate_headers: nil,
67
- serializer: nil, serializer_options: nil, status: nil, after_persist: nil, html: false,
68
- finder: nil, scope: nil, build: nil, raise_on_invalid: false
69
- }
70
76
  defaults.merge(simple_crud_inherited_defaults).each do |key, value|
71
77
  parameters[key] = value unless parameters.key?(key)
72
78
  end
73
- parameters[:authenticate_headers] = parameters[:authenticate] if parameters[:authenticate_headers].nil?
79
+ parameters[:authenticate_headers] ||= parameters[:authenticate]
74
80
  parameters
75
81
  end
76
82
 
83
+ def defaults
84
+ {
85
+ authorize: true, paginate: true, authenticate: true, authenticate_headers: nil,
86
+ serializer: nil, serializer_options: nil, status: nil, after_persist: nil, html: false,
87
+ finder: nil, scope: nil, build: nil, owned_by: nil, parent: nil, parent_association: nil,
88
+ notice: nil, alert: nil, raise_on_invalid: false
89
+ }
90
+ end
91
+
77
92
  def write_metadata(method, parameters)
78
93
  @simple_crud_metadata ||= {}
79
94
  @simple_crud_metadata[method] = parameters
@@ -87,6 +102,12 @@ module SimpleCrudController
87
102
  raise ArgumentError, 'invalid method' unless %i[show index create update destroy new edit].include? method
88
103
  end
89
104
 
105
+ def check_relation_source(parameters)
106
+ return unless parameters[:owned_by] && parameters[:parent]
107
+
108
+ raise ArgumentError, 'owned_by: and parent: are mutually exclusive, they both scope the same action'
109
+ end
110
+
90
111
  def check_policies(parameters)
91
112
  return if !parameters.key?(:authorize) || !parameters[:authorize]
92
113
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCrud
4
- VERSION = '0.6.0'
4
+ VERSION = '0.8.0'
5
5
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :render_or_redirect do
4
+ match { |response| response.successful? || response.redirect? }
5
+
6
+ failure_message { |response| "expected a 2xx render or a redirect, got #{response.status}" }
7
+ end
@@ -53,7 +53,7 @@ RSpec.shared_examples 'simple crud for create' do
53
53
  end
54
54
  end
55
55
 
56
- unless check_raise_on_invalid(:create)
56
+ if invalid_case?(:create)
57
57
  context 'when creating with invalid attributes' do
58
58
  include_context 'with authenticated user' if check_authenticate(:create)
59
59
 
@@ -11,7 +11,7 @@ RSpec.shared_examples 'simple crud for create with block' do
11
11
  end
12
12
 
13
13
  it 'passes the saved record to the block' do
14
- expect(response).to be_successful
14
+ expect(response).to render_or_redirect
15
15
  end
16
16
 
17
17
  it 'creates the record' do
@@ -19,7 +19,7 @@ RSpec.shared_examples 'simple crud for create with block' do
19
19
  end
20
20
  end
21
21
 
22
- unless check_raise_on_invalid(:create)
22
+ if invalid_case?(:create)
23
23
  context 'when creating with invalid attributes' do
24
24
  include_context 'with authenticated user' if check_authenticate(:create)
25
25
 
@@ -8,8 +8,14 @@ RSpec.shared_examples 'simple crud for create with build' do
8
8
  post :create, params: with_route_params(body_params(params_for(model_class))), format: format_param(:create)
9
9
  end
10
10
 
11
- it 'creates the record owned by the current user' do
12
- expect(model_class_object.last.public_send(owner_association)).to eq(current_user)
11
+ if owner_association
12
+ it 'creates the record owned by the current user' do
13
+ expect(model_class_object.last.public_send(owner_association)).to eq(current_user)
14
+ end
15
+ else
16
+ it 'creates the record through the hook' do
17
+ expect(model_class_object.count).to eq(1)
18
+ end
13
19
  end
14
20
  end
15
21
  end
@@ -71,17 +71,19 @@ RSpec.shared_examples 'simple crud for destroy' do
71
71
  end
72
72
  end
73
73
 
74
- context 'when ID is invalid' do
75
- include_context 'with authenticated user' if check_authenticate(:destroy)
74
+ unless singular
75
+ context 'when ID is invalid' do
76
+ include_context 'with authenticated user' if check_authenticate(:destroy)
76
77
 
77
- before do
78
- model
79
- delete :destroy, params: with_route_params(record_param(:destroy, nil, not_found: true)),
80
- format: format_param(:destroy)
81
- end
78
+ before do
79
+ model
80
+ delete :destroy, params: with_route_params(record_param(:destroy, nil, not_found: true)),
81
+ format: format_param(:destroy)
82
+ end
82
83
 
83
- it 'responds with not found status' do
84
- expect(response).to have_http_status(:not_found)
84
+ it 'responds with not found status' do
85
+ expect(response).to have_http_status(:not_found)
86
+ end
85
87
  end
86
88
  end
87
89
 
@@ -11,7 +11,7 @@ RSpec.shared_examples 'simple crud for destroy with block' do
11
11
  end
12
12
 
13
13
  it 'passes the destroyed record to the block' do
14
- expect(response).to be_successful
14
+ expect(response).to render_or_redirect
15
15
  end
16
16
 
17
17
  it 'destroys the record' do
@@ -41,38 +41,40 @@ RSpec.shared_examples 'simple crud for edit' do
41
41
 
42
42
  before do
43
43
  model
44
- make_policies_succeed(:edit) if check_authorize(:edit)
44
+ make_policies_succeed(:edit, permissive: check_block(:edit)) if check_authorize(:edit)
45
45
  edit_request
46
46
  end
47
47
 
48
- it 'responds with ok status' do
49
- expect(response).to have_http_status(:ok)
50
- end
51
-
52
48
  if check_block(:edit)
53
49
  it 'renders the asked model through the block' do
54
- expect(response).to be_successful
50
+ expect(response).to render_or_redirect
55
51
  end
56
52
  elsif check_html(:edit)
57
53
  include_examples 'simple crud renders template', :edit
58
54
  else
55
+ it 'responds with ok status' do
56
+ expect(response).to have_http_status(:ok)
57
+ end
58
+
59
59
  it 'returns the asked model' do
60
60
  expect(response_body['id']).to eq(model.id)
61
61
  end
62
62
  end
63
63
  end
64
64
 
65
- context 'when the model does not exist' do
66
- include_context 'with authenticated user'
65
+ unless singular
66
+ context 'when the model does not exist' do
67
+ include_context 'with authenticated user'
67
68
 
68
- let(:edit_params) { record_param(:edit, nil, not_found: true) }
69
+ let(:edit_params) { record_param(:edit, nil, not_found: true) }
69
70
 
70
- before do
71
- edit_request
72
- end
71
+ before do
72
+ edit_request
73
+ end
73
74
 
74
- it 'responds with not found status' do
75
- expect(response).to have_http_status(:not_found)
75
+ it 'responds with not found status' do
76
+ expect(response).to have_http_status(:not_found)
77
+ end
76
78
  end
77
79
  end
78
80
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'simple crud for edit with block' do
4
+ describe 'GET #edit with a render block' do
5
+ include_context 'with authenticated user' if check_authenticate(:edit)
6
+
7
+ let!(:record) { model }
8
+
9
+ before do
10
+ make_policies_succeed(:edit, permissive: true) if check_authorize(:edit)
11
+ get :edit, params: with_route_params(record_param(:edit, record)), format: format_param(:edit)
12
+ end
13
+
14
+ if check_html(:edit)
15
+ include_examples 'simple crud renders template', :edit
16
+ else
17
+ it 'renders the record through the block' do
18
+ expect(response).to render_or_redirect
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'simple crud for edit with finder' do
4
+ describe 'GET #edit with a custom finder' do
5
+ include_context 'with authenticated user' if check_authenticate(:edit)
6
+
7
+ context 'when the model exists' do
8
+ before do
9
+ model
10
+ make_policies_succeed(:edit, permissive: check_block(:edit)) if check_authorize(:edit)
11
+ get :edit, params: with_route_params(finder_key => model.public_send(finder_key)),
12
+ format: format_param(:edit)
13
+ end
14
+
15
+ if check_block(:edit)
16
+ it 'finds the asked model' do
17
+ expect(rendered_record.id).to eq(model.id)
18
+ end
19
+ elsif check_html(:edit)
20
+ include_examples 'simple crud renders template', :edit
21
+ else
22
+ it 'responds with ok status' do
23
+ expect(response).to have_http_status(:ok)
24
+ end
25
+
26
+ it 'returns the asked model' do
27
+ expect(response_body['id']).to eq(model.id)
28
+ end
29
+ end
30
+ end
31
+
32
+ include_examples 'simple crud not found with finder', :get, :edit
33
+ end
34
+ end
@@ -3,7 +3,14 @@
3
3
  RSpec.shared_examples 'simple crud for index with scope' do
4
4
  let(:request_params) { {} }
5
5
  let(:my_models) { create_records(model_class, 2, scoped_attributes) }
6
- let(:other_models) { create_records(model_class, 3, other_scoped_attributes) }
6
+ let(:other_models) { create_records(model_class, 3, other_scoped_attributes) if other_scoped_attributes }
7
+ let(:listed_ids) do
8
+ if check_html(:index)
9
+ rendered_records.map(&:id)
10
+ else
11
+ (check_paginate(:index) ? response_body['page'] : response_body).map { |m| m['id'] }
12
+ end
13
+ end
7
14
 
8
15
  # Hooks stay inside this context so co-including the example never
9
16
  # authenticates or stubs policies for sibling examples.
@@ -16,17 +23,13 @@ RSpec.shared_examples 'simple crud for index with scope' do
16
23
  get :index, params: with_route_params(request_params), format: format_param(:index)
17
24
  end
18
25
 
19
- if check_html(:index)
20
- it 'returns only the assigned scoped records' do
21
- expect(rendered_records.map(&:id)).to match_array(my_models.map(&:id))
22
- end
23
- elsif check_paginate(:index)
24
- it 'returns only the paginated scoped records' do
25
- expect(response_body['page'].map { |m| m['id'] }).to match_array(my_models.map(&:id))
26
- end
27
- else
28
- it 'returns only the unpaginated scoped records' do
29
- expect(response_body.map { |m| m['id'] }).to match_array(my_models.map(&:id))
26
+ it 'returns the scoped records' do
27
+ expect(listed_ids).to match_array(my_models.map(&:id))
28
+ end
29
+
30
+ if scoped_order
31
+ it 'returns the scoped records in order' do
32
+ expect(listed_ids).to eq(instance_exec(my_models, &scoped_order).map(&:id))
30
33
  end
31
34
  end
32
35
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # For a parent: controller. The parent in route_params must match the record's owner_association.
4
+ RSpec.shared_examples 'simple crud for nested resource' do
5
+ describe 'parent: scoping' do
6
+ include_context 'with authenticated user' if check_authenticate(:show)
7
+
8
+ it 'finds a record inside the parent' do
9
+ get :show, params: with_route_params(record_param(:show, model)), format: format_param(:show)
10
+
11
+ expect(response).to have_http_status(:ok)
12
+ end
13
+
14
+ it 'returns not found for a record outside the parent' do
15
+ outside = create_record(model_class, model_attributes.merge(owner_association => other_user))
16
+ get :show, params: with_route_params(record_param(:show, outside)), format: format_param(:show)
17
+
18
+ expect(response).to have_http_status(:not_found)
19
+ end
20
+ end
21
+ end
@@ -5,7 +5,7 @@ RSpec.shared_examples 'simple crud for new with block' do
5
5
  include_context 'with authenticated user' if check_authenticate(:new)
6
6
 
7
7
  before do
8
- make_policies_succeed(:new) if check_authorize(:new)
8
+ make_policies_succeed(:new, permissive: true) if check_authorize(:new)
9
9
  get :new, params: with_route_params({}), format: format_param(:new)
10
10
  end
11
11
 
@@ -8,11 +8,17 @@ RSpec.shared_examples 'simple crud for new with build' do
8
8
  get :new, params: with_route_params({}), format: format_param(:new)
9
9
  end
10
10
 
11
- it 'builds the record owned by the current user', :aggregate_failures do
12
- if check_html(:new) || check_block(:new)
13
- expect(rendered_record.public_send(owner_foreign_key)).to eq(current_user.id)
14
- else
15
- expect(response_body[owner_foreign_key.to_s]).to eq(current_user.id)
11
+ if owner_association
12
+ it 'builds the record owned by the current user', :aggregate_failures do
13
+ if check_html(:new) || check_block(:new)
14
+ expect(rendered_record.public_send(owner_foreign_key)).to eq(current_user.id)
15
+ else
16
+ expect(response_body[owner_foreign_key.to_s]).to eq(current_user.id)
17
+ end
18
+ end
19
+ else
20
+ it 'builds the record through the hook' do
21
+ expect(response).to be_successful
16
22
  end
17
23
  end
18
24
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Include alongside the per-action examples for a controller using owned_by:.
4
+ RSpec.shared_examples 'simple crud for owned resource' do
5
+ describe 'owned_by: scoping' do
6
+ include_context 'with authenticated user'
7
+
8
+ let!(:someone_elses_model) do
9
+ create_record(model_class, model_attributes.merge(owner_association => other_user))
10
+ end
11
+
12
+ it 'returns not found for a record owned by another user' do
13
+ get :show, params: with_route_params(record_param(:show, someone_elses_model)), format: format_param(:show)
14
+
15
+ expect(response).to have_http_status(:not_found)
16
+ end
17
+
18
+ context 'when updating a record owned by another user' do
19
+ let!(:original_value) { required_attribute && someone_elses_model.public_send(required_attribute) }
20
+
21
+ before do
22
+ params = with_route_params(record_param(:update, someone_elses_model)).merge(body_params(model_params))
23
+ patch :update, params: params, format: format_param(:update)
24
+ end
25
+
26
+ it 'responds with not found' do
27
+ expect(response).to have_http_status(:not_found)
28
+ end
29
+
30
+ if required_attribute
31
+ it 'leaves the record untouched' do
32
+ expect(someone_elses_model.reload.public_send(required_attribute)).to eq(original_value)
33
+ end
34
+ end
35
+ end
36
+
37
+ it 'attributes a created record to the current user regardless of the request', :aggregate_failures do
38
+ body = body_params(model_params.merge(owner_association => other_user.id))
39
+ post :create, params: with_route_params(body), format: format_param(:create)
40
+
41
+ expect(response).to have_http_status(check_status(:create) || :created)
42
+ expect(model_class_object.last.public_send(owner_association)).to eq(current_user)
43
+ end
44
+ end
45
+ end
@@ -39,16 +39,18 @@ RSpec.shared_examples 'simple crud for show' do
39
39
  end
40
40
  end
41
41
 
42
- describe 'when the model does not exist' do
43
- include_context 'with authenticated user'
44
- let(:show_params) { record_param(:show, nil, not_found: true) }
42
+ unless singular
43
+ describe 'when the model does not exist' do
44
+ include_context 'with authenticated user'
45
+ let(:show_params) { record_param(:show, nil, not_found: true) }
45
46
 
46
- before do
47
- show_request
48
- end
47
+ before do
48
+ show_request
49
+ end
49
50
 
50
- it 'responds with not found status' do
51
- expect(response).to have_http_status(:not_found)
51
+ it 'responds with not found status' do
52
+ expect(response).to have_http_status(:not_found)
53
+ end
52
54
  end
53
55
  end
54
56
  end
@@ -7,7 +7,7 @@ RSpec.shared_examples 'simple crud for show with block' do
7
7
  let!(:record) { model }
8
8
 
9
9
  before do
10
- make_policies_succeed(:show) if check_authorize(:show)
10
+ make_policies_succeed(:show, permissive: true) if check_authorize(:show)
11
11
  get :show, params: with_route_params(record_param(:show, record)), format: format_param(:show)
12
12
  end
13
13
 
@@ -61,24 +61,26 @@ RSpec.shared_examples 'simple crud for update' do
61
61
  end
62
62
  end
63
63
 
64
- context 'when updating a model that doesn\'t exist' do
65
- include_context 'with authenticated user'
64
+ unless singular
65
+ context 'when updating a model that doesn\'t exist' do
66
+ include_context 'with authenticated user'
66
67
 
67
- before do
68
- put :update, params: with_route_params(record_param(:update, nil, not_found: true)),
69
- format: format_param(:update)
70
- end
68
+ before do
69
+ put :update, params: with_route_params(record_param(:update, nil, not_found: true)),
70
+ format: format_param(:update)
71
+ end
71
72
 
72
- it 'response with 404 status code' do
73
- expect(response).to have_http_status(:not_found)
74
- end
73
+ it 'response with 404 status code' do
74
+ expect(response).to have_http_status(:not_found)
75
+ end
75
76
 
76
- it 'doesn\'t create an model' do
77
- expect(model_class_object.count).to be 0
77
+ it 'doesn\'t create an model' do
78
+ expect(model_class_object.count).to be 0
79
+ end
78
80
  end
79
81
  end
80
82
 
81
- unless check_raise_on_invalid(:update)
83
+ if invalid_case?(:update)
82
84
  context 'when updating with invalid attributes' do
83
85
  include_context 'with authenticated user' if check_authenticate(:update)
84
86
 
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'simple crud for update with block' do
4
+ describe 'PUT #update with a render block' do
5
+ include_context 'with authenticated user' if check_authenticate(:update)
6
+
7
+ let!(:record) { model }
8
+
9
+ context 'when successfully updating a record' do
10
+ before do
11
+ make_policies_succeed(:update, permissive: true) if check_authorize(:update)
12
+ put :update, params: with_route_params(body_params(model_params).merge(record_param(:update, record))),
13
+ format: format_param(:update)
14
+ end
15
+
16
+ it 'passes the saved record to the block' do
17
+ expect(response).to render_or_redirect
18
+ end
19
+
20
+ it 'updates the record' do
21
+ expect(record.reload).to have_attributes(model_params)
22
+ end
23
+ end
24
+
25
+ if invalid_case?(:update)
26
+ context 'when updating with invalid attributes' do
27
+ before do
28
+ make_policies_succeed(:update, permissive: true) if check_authorize(:update)
29
+ put :update, params: with_route_params(body_params({ required_attribute => nil }.merge(owner_params))
30
+ .merge(record_param(:update, record))),
31
+ format: format_param(:update)
32
+ end
33
+
34
+ it 'does not change the record' do
35
+ expect(record.reload.public_send(required_attribute)).not_to be_nil
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -5,8 +5,9 @@ RSpec.shared_examples 'simple crud for update with finder' do
5
5
  include_context 'with authenticated user' if check_authenticate(:update)
6
6
 
7
7
  context 'when successfully updating a model' do
8
+ let(:updated_attributes) { required_attribute ? { required_attribute => 'Updated' } : model_params }
8
9
  let(:update_params) do
9
- with_route_params(body_params(required_attribute => 'Updated').merge(owner_params))
10
+ with_route_params(body_params(updated_attributes).merge(owner_params))
10
11
  .merge(finder_key => model.public_send(finder_key))
11
12
  end
12
13
 
@@ -15,13 +16,15 @@ RSpec.shared_examples 'simple crud for update with finder' do
15
16
  put :update, params: update_params, format: format_param(:update)
16
17
  end
17
18
 
18
- it 'updates the model' do
19
- expect(model.reload.public_send(required_attribute)).to eq('Updated')
19
+ if required_attribute
20
+ it 'updates the model' do
21
+ expect(model.reload.public_send(required_attribute)).to eq('Updated')
22
+ end
20
23
  end
21
24
 
22
25
  if check_block(:update)
23
26
  it 'passes the updated model to the block' do
24
- expect(rendered_record.id).to eq(model.id)
27
+ expect(response).to render_or_redirect
25
28
  end
26
29
  elsif check_html(:update)
27
30
  it 'redirects to the updated record' do
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.shared_examples 'simple crud not found with finder' do |http_method, action|
4
- context 'when the model does not exist' do
5
- before do
6
- send(http_method, action, params: with_route_params(finder_key => "nonexistent-#{finder_key}"),
7
- format: format_param(action))
8
- end
4
+ unless singular
5
+ context 'when the model does not exist' do
6
+ before do
7
+ send(http_method, action, params: with_route_params(finder_key => "nonexistent-#{finder_key}"),
8
+ format: format_param(action))
9
+ end
9
10
 
10
- it 'responds with not found status' do
11
- expect(response).to have_http_status(:not_found)
11
+ it 'responds with not found status' do
12
+ expect(response).to have_http_status(:not_found)
13
+ end
12
14
  end
13
15
  end
14
16
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe BlockCreateRedirect::DummyModelsController, type: :controller do
6
+ include_examples 'simple crud for create with block'
7
+
8
+ describe 'without a validated attribute', simple_crud: { required_attribute: nil } do
9
+ include_examples 'simple crud for create with block'
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe BlockEdit::DummyModelsController, type: :controller do
6
+ include_examples 'simple crud for edit with block'
7
+ end
@@ -4,6 +4,8 @@ require 'spec_helper'
4
4
 
5
5
  describe BlockFinder::DummyModelsController, type: :controller do
6
6
  include_examples 'simple crud for edit'
7
+ include_examples 'simple crud for edit with finder'
8
+ include_examples 'simple crud for edit with block'
7
9
  include_examples 'simple crud for show with finder'
8
10
  include_examples 'simple crud for update with finder'
9
11
  include_examples 'simple crud for destroy with finder'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe BlockPolicyShow::DummyModelsController, type: :controller do
6
+ include_examples 'simple crud for show with block'
7
+ end