wor-simple_crud 0.3.0 → 0.3.1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/CHANGELOG.md +16 -2
  4. data/README.md +57 -14
  5. data/lib/simple_crud/action_lambdas.rb +81 -0
  6. data/lib/simple_crud/controller_helpers.rb +11 -2
  7. data/lib/simple_crud/rspec/config.rb +18 -5
  8. data/lib/simple_crud/rspec/helpers/authentication.rb +32 -0
  9. data/lib/simple_crud/rspec/helpers/controller_options.rb +19 -0
  10. data/lib/simple_crud/rspec/helpers/models.rb +55 -0
  11. data/lib/simple_crud/rspec/helpers/policies.rb +25 -0
  12. data/lib/simple_crud/rspec/helpers/requests.rb +45 -0
  13. data/lib/simple_crud/rspec/helpers/settings.rb +49 -0
  14. data/lib/simple_crud/rspec/helpers.rb +16 -138
  15. data/lib/simple_crud/rspec.rb +1 -0
  16. data/lib/simple_crud/simple_crud_controller.rb +11 -74
  17. data/lib/simple_crud/version.rb +1 -1
  18. data/lib/spec/shared_examples/simple_crud_for_create.rb +15 -12
  19. data/lib/spec/shared_examples/simple_crud_for_create_with_block.rb +25 -7
  20. data/lib/spec/shared_examples/simple_crud_for_create_with_build.rb +2 -2
  21. data/lib/spec/shared_examples/simple_crud_for_destroy.rb +25 -0
  22. data/lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb +10 -2
  23. data/lib/spec/shared_examples/simple_crud_for_destroy_with_finder.rb +17 -6
  24. data/lib/spec/shared_examples/simple_crud_for_edit.rb +79 -0
  25. data/lib/spec/shared_examples/simple_crud_for_index.rb +1 -1
  26. data/lib/spec/shared_examples/simple_crud_for_index_with_block.rb +11 -3
  27. data/lib/spec/shared_examples/simple_crud_for_index_with_scope.rb +25 -13
  28. data/lib/spec/shared_examples/simple_crud_for_new.rb +1 -1
  29. data/lib/spec/shared_examples/simple_crud_for_new_with_block.rb +13 -2
  30. data/lib/spec/shared_examples/simple_crud_for_new_with_build.rb +10 -4
  31. data/lib/spec/shared_examples/simple_crud_for_show_with_block.rb +13 -2
  32. data/lib/spec/shared_examples/simple_crud_for_show_with_finder.rb +16 -7
  33. data/lib/spec/shared_examples/simple_crud_for_update.rb +16 -9
  34. data/lib/spec/shared_examples/simple_crud_for_update_with_finder.rb +16 -6
  35. data/lib/spec/shared_examples/simple_crud_not_found_with_finder.rb +2 -1
  36. data/spec/block_finder/dummy_models_controller_spec.rb +10 -0
  37. data/spec/block_json/dummy_models_controller_spec.rb +9 -0
  38. data/spec/block_redirect/dummy_models_controller_spec.rb +7 -0
  39. data/spec/dummy/app/controllers/block_finder/dummy_models_controller.rb +25 -0
  40. data/spec/dummy/app/controllers/block_json/dummy_models_controller.rb +17 -0
  41. data/spec/dummy/app/controllers/block_redirect/dummy_models_controller.rb +13 -0
  42. data/spec/dummy/app/controllers/finder/dummy_models_controller.rb +1 -0
  43. data/spec/dummy/app/controllers/html_finder/dummy_models_controller.rb +17 -0
  44. data/spec/dummy/app/controllers/html_scoped/dummy_models_controller.rb +1 -1
  45. data/spec/dummy/app/controllers/unpaginated_scoped/dummy_models_controller.rb +16 -0
  46. data/spec/dummy/app/models/dummy_model_policy.rb +5 -1
  47. data/spec/dummy/app/views/html_finder/dummy_models/edit.html.erb +1 -0
  48. data/spec/dummy/app/views/html_finder/dummy_models/new.html.erb +1 -0
  49. data/spec/dummy/app/views/html_finder/dummy_models/show.html.erb +1 -0
  50. data/spec/dummy/config/routes.rb +21 -1
  51. data/spec/dummy_model_policy_spec.rb +18 -8
  52. data/spec/finder/dummy_models_controller_spec.rb +1 -0
  53. data/spec/html_finder/dummy_models_controller_spec.rb +11 -0
  54. data/spec/html_scoped/dummy_models_controller_spec.rb +1 -1
  55. data/spec/scoped_params/dummy_models_controller_spec.rb +4 -3
  56. data/spec/simple_crud/rspec_config_spec.rb +33 -0
  57. data/spec/unpaginated_scoped/dummy_models_controller_spec.rb +7 -0
  58. metadata +23 -2
@@ -1,147 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'helpers/settings'
4
+ require_relative 'helpers/controller_options'
5
+ require_relative 'helpers/models'
6
+ require_relative 'helpers/authentication'
7
+ require_relative 'helpers/requests'
8
+ require_relative 'helpers/policies'
9
+
3
10
  module SimpleCrud
4
11
  module RSpec
5
12
  # Helpers available to the shared examples (and to apps that include
6
- # them). Everything app-specific is delegated to SimpleCrud::RSpec::Config
7
- # so it can be overridden without editing the examples.
13
+ # them), composed from focused sub-modules: settings resolution,
14
+ # controller-option introspection, record building, authentication,
15
+ # request params and policy access.
8
16
  module Helpers
9
- def config
10
- SimpleCrud::RSpec::Config.instance
11
- end
12
-
13
- def get_option(method, option)
14
- described_class.instance_variable_get(:@simple_crud_metadata)[method][option]
15
- end
16
-
17
- # :unprocessable_entity was renamed :unprocessable_content in Rack 3.1.
18
- # Resolve the current Rack's canonical symbol for 422.
19
- def unprocessable_status
20
- Rack::Utils::SYMBOL_TO_STATUS_CODE.invert[422]
21
- end
22
-
23
- %i[paginate authorize authenticate serializer html finder scope build raise_on_invalid].each do |option|
24
- define_method("check_#{option}") { |method| get_option(method, option) }
25
- end
26
-
27
- %i[owner_association required_attribute required_error finder_key params_key invalid_status
28
- unauthenticated_status assert_html_template].each do |setting|
29
- define_method(setting) { resolve(config.public_send(setting)) }
30
- end
31
-
32
- def model_class
33
- described_class.to_s.split('::')
34
- .last.sub('Controller', '').singularize.underscore
35
- end
36
-
37
- def model_class_object
38
- model_class.classify.constantize
39
- end
40
-
41
- def model
42
- @model ||= create_record(model_class, model_attributes)
43
- end
44
-
45
- def model_attributes
46
- association = resolve(config.owner_association)
47
- association ? { association => current_user } : {}
48
- end
49
-
50
- def create_record(klass, attributes)
51
- instance_exec(klass, attributes, &config.create_record)
52
- end
53
-
54
- def create_records(klass, count, attributes)
55
- instance_exec(klass, count, attributes, &config.create_records)
56
- end
57
-
58
- def params_for(klass)
59
- instance_exec(klass, &config.params_for)
60
- end
61
-
62
- def model_params
63
- @model_params ||= params_for(model_class)
64
- end
65
-
66
- def current_user
67
- @current_user ||= instance_exec(&config.current_user)
68
- end
69
-
70
- def authenticate_request
71
- instance_exec(&config.authenticate)
72
- end
73
-
74
- def owner_foreign_key
75
- :"#{owner_association}_id"
76
- end
77
-
78
- # The owner association attributed to current_user, if the model has one.
79
- def owner_params
80
- owner_association ? { owner_foreign_key => current_user.id } : {}
81
- end
82
-
83
- # Extra params (e.g. a parent slug) added to every request, for nested
84
- # resources like /classrooms/:classroom_slug/assignments.
85
- def route_params
86
- resolve(config.route_params)
87
- end
88
-
89
- def with_route_params(attrs)
90
- route_params.merge(attrs)
91
- end
92
-
93
- # The params that identify a record for the given action, using the
94
- # configured finder_key when the action has a custom finder.
95
- def record_param(action, record, not_found: false)
96
- key = check_finder(action) ? finder_key : :id
97
- value = not_found ? "nonexistent-#{key}" : record.public_send(key)
98
- { key => value }
99
- end
100
-
101
- # Wraps a request body under the model's strong-params key when
102
- # params_key is configured (nested strong params), else passes it flat.
103
- def body_params(attrs)
104
- key = params_key
105
- key ? { key => attrs } : attrs
106
- end
107
-
108
- def policy_class_object
109
- instance_exec(model_class_object, &config.policy_class)
110
- end
111
-
112
- def model_serializer
113
- defined?(serializer) ? serializer : instance_exec(model, &config.serializer_class)
114
- end
115
-
116
- def make_policies_fail(method)
117
- allow(policy_class_object).to receive(:new)
118
- .and_return(instance_double(policy_class_object, "#{method}?" => false))
119
- end
120
-
121
- def make_policies_succeed(method)
122
- allow(policy_class_object).to receive(:new)
123
- .and_return(instance_double(policy_class_object, "#{method}?" => true))
124
- end
125
-
126
- def request_format(action)
127
- check_html(action) ? :html : :json
128
- end
129
-
130
- def resolve(value)
131
- value.is_a?(Proc) ? instance_exec(&value) : value
132
- end
133
-
134
- # Runs the block with a config setting temporarily overridden, restoring
135
- # the original value afterwards (even if the block raises). Useful when
136
- # a controller's spec needs a different setting than the app-wide default.
137
- def with_config_override(setting, value)
138
- config_instance = SimpleCrud::RSpec::Config.instance
139
- original = config_instance.public_send(setting)
140
- SimpleCrud::RSpec.configure { |c| c.public_send("#{setting}=", value) }
141
- yield
142
- ensure
143
- SimpleCrud::RSpec.configure { |c| c.public_send("#{setting}=", original) }
144
- end
17
+ include Settings
18
+ include ControllerOptions
19
+ include Models
20
+ include Authentication
21
+ include Requests
22
+ include Policies
145
23
  end
146
24
  end
147
25
  end
@@ -10,6 +10,7 @@ require_relative '../spec/shared_examples/simple_crud_for_index'
10
10
  require_relative '../spec/shared_examples/simple_crud_for_destroy'
11
11
  require_relative '../spec/shared_examples/simple_crud_for_create'
12
12
  require_relative '../spec/shared_examples/simple_crud_for_new'
13
+ require_relative '../spec/shared_examples/simple_crud_for_edit'
13
14
  require_relative '../spec/shared_examples/unauthorized_when_not_logged_in'
14
15
  require_relative '../spec/shared_examples/simple_crud_renders_template'
15
16
  require_relative '../spec/shared_examples/simple_crud_without_authenticated_user'
@@ -3,10 +3,13 @@
3
3
  require 'active_support/all'
4
4
  require_relative 'config'
5
5
  require_relative 'controller_helpers'
6
+ require_relative 'action_lambdas'
6
7
 
7
8
  # Extended onto a controller for CRUD actions.
8
9
  module SimpleCrudController
9
10
  extend SimpleCrud::ControllerHelpers
11
+ # Include, not extend: controllers gain these by extending SimpleCrudController.
12
+ include SimpleCrud::ActionLambdas
10
13
 
11
14
  cattr_accessor :params, :permitted
12
15
 
@@ -15,15 +18,16 @@ module SimpleCrudController
15
18
  ### paginate: paginate the list via Config.pagination_adapter (wor-paginate by default)
16
19
  ### authenticate: use devise to authenticate
17
20
  ### serializer: use a particular serializer (both each_serializer and serializer)
18
- ### html: render the action's ERB template instead of JSON (index/show/new/create/update/destroy)
19
- ### finder: custom record lookup (Proc/lambda or Symbol) for :show/:update/:destroy
21
+ ### html: render the action's ERB template instead of JSON (index/show/new/edit/create/update/destroy)
22
+ ### finder: custom record lookup (Proc/lambda or Symbol) for :show/:update/:destroy/:edit
20
23
  ### scope: custom index scope (Proc/lambda taking current_user and optional params), overrides policy_scope
21
24
  ### build: custom record builder (Proc/lambda) for :new/:create, invoked with the controller as self
22
25
  ### raise_on_invalid: use strict create!/update! semantics instead of returning 422
23
26
  ### A block given to simple_crud_for renders explicitly: it receives the records for :index,
24
- ### the record for :show/:new, or the record and a saved flag for :create/:update/:destroy
27
+ ### the record for :show/:new/:edit, or the record and a saved flag for :create/:update/:destroy
25
28
  def simple_crud_for(method, parameters = {}, &block)
26
29
  parameters[:html] = true if block && !parameters.key?(:html)
30
+ parameters[:block] = true if block
27
31
  parameters = parameters_with_defaults(parameters)
28
32
  klass = simple_crud_controller_model
29
33
  check_valid_method(method)
@@ -47,77 +51,12 @@ module SimpleCrudController
47
51
  @simple_crud_metadata[method] = parameters
48
52
  end
49
53
 
50
- def crud_lambda_for_show(klass, parameters = {}, &block)
51
- lambda do
52
- authenticate_user! if parameters[:authenticate]
53
- requested = SimpleCrudController.find_record(klass, self, parameters)
54
-
55
- options = {}.merge(serializer: parameters[:serializer]).compact
56
- SimpleCrudController.maybe_authorize(self, requested, parameters)
57
- SimpleCrudController.render_show(self, requested, options, parameters, &block)
58
- end
59
- end
60
-
61
- def crud_lambda_for_new(klass, parameters = {}, &block)
62
- lambda do
63
- authenticate_user! if parameters[:authenticate]
64
- record = SimpleCrudController.build_record(self, klass, parameters)
65
- SimpleCrudController.maybe_authorize(self, record, parameters)
66
- SimpleCrudController.render_new(self, record, parameters, &block)
67
- end
68
- end
69
-
70
- def crud_lambda_for_index(klass, parameters = {}, &block)
71
- lambda do
72
- authenticate_user! if parameters[:authenticate]
73
- SimpleCrudController.maybe_authorize(self, klass.new, parameters)
74
- options = {}.merge(each_serializer: parameters[:serializer]).compact
75
- SimpleCrudController.render_index(self, klass, options, parameters, &block)
76
- end
77
- end
78
-
79
- def crud_lambda_for_create(klass, parameters = {}, &block)
80
- lambda do
81
- authenticate_user! if parameters[:authenticate]
82
- permitted_params = send("#{self.class.simple_crud_controller_model.to_s.underscore}_params")
83
- record = SimpleCrudController.build_record(self, klass, parameters)
84
- record.assign_attributes(permitted_params)
85
- SimpleCrudController.maybe_authorize(self, record, parameters)
86
- persist = ->(bang:) { bang ? record.save! : record.save }
87
- options = { status: :created, failure_template: :new }
88
- SimpleCrudController.save_and_render(self, record, parameters, options, persist, &block)
89
- end
90
- end
91
-
92
- def crud_lambda_for_update(klass, parameters = {}, &block)
93
- lambda do
94
- authenticate_user! if parameters[:authenticate]
95
- requested = SimpleCrudController.find_record(klass, self, parameters)
96
- SimpleCrudController.maybe_authorize(self, requested, parameters)
97
- permitted_params = send("#{self.class.simple_crud_controller_model.to_s.underscore}_params")
98
- persist = ->(bang:) { bang ? requested.update!(permitted_params) : requested.update(permitted_params) }
99
- options = { status: :ok, failure_template: :edit }
100
- SimpleCrudController.save_and_render(self, requested, parameters, options, persist, &block)
101
- end
102
- end
103
-
104
- def crud_lambda_for_destroy(klass, parameters = {}, &block)
105
- lambda do
106
- authenticate_user! if parameters[:authenticate]
107
- requested = SimpleCrudController.find_record(klass, self, parameters)
108
- SimpleCrudController.maybe_authorize(self, requested, parameters)
109
- options = { status: :ok, failure_template: :show, redirect: parameters[:redirect] || klass }
110
- persist = ->(bang:) { bang ? requested.destroy! : requested.destroy }
111
- SimpleCrudController.persist_and_render(self, requested, parameters, options, persist, &block)
112
- end
113
- end
114
-
115
54
  def simple_crud_controller_model
116
55
  to_s.split('::').last.sub('Controller', '').singularize.classify.constantize
117
56
  end
118
57
 
119
58
  def check_valid_method(method)
120
- raise ArgumentError, 'invalid method' unless %i[show index create update destroy new].include? method
59
+ raise ArgumentError, 'invalid method' unless %i[show index create update destroy new edit].include? method
121
60
  end
122
61
 
123
62
  def check_policies(parameters)
@@ -130,11 +69,9 @@ module SimpleCrudController
130
69
  end
131
70
 
132
71
  def check_serializer(parameters)
133
- return if parameters[:serializer].blank?
134
-
135
- serializer_name = parameters[:serializer].to_s
136
- return if Kernel.const_defined?(serializer_name)
72
+ name = parameters[:serializer].to_s
73
+ return if name.blank? || Kernel.const_defined?(name)
137
74
 
138
- raise ArgumentError, "create a valid serializer with name #{serializer_name}"
75
+ raise ArgumentError, "create a valid serializer with name #{name}"
139
76
  end
140
77
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCrud
4
- VERSION = '0.3.0'
4
+ VERSION = '0.3.1'
5
5
  end
@@ -47,8 +47,9 @@ shared_examples 'simple crud for create' do
47
47
  end
48
48
  end
49
49
 
50
- it 'creates the record' do
50
+ it 'creates the record', :aggregate_failures do
51
51
  expect(model_class_object.count).to be 1
52
+ instance_exec(model_class_object.last, &created_record_check) if created_record_check
52
53
  end
53
54
  end
54
55
 
@@ -60,20 +61,22 @@ shared_examples 'simple crud for create' do
60
61
  post :create, params: with_route_params(body_params(owner_params)), format: request_format(:create)
61
62
  end
62
63
 
63
- if check_html(:create)
64
- include_examples 'simple crud renders template', :new, -> { invalid_status }
65
- else
66
- it 'responds with unprocessable entity' do
67
- expect(response).to have_http_status(unprocessable_status)
68
- end
64
+ unless check_block(:create)
65
+ if check_html(:create)
66
+ include_examples 'simple crud renders template', :new, -> { invalid_status }
67
+ else
68
+ it 'responds with unprocessable entity' do
69
+ expect(response).to have_http_status(unprocessable_status)
70
+ end
69
71
 
70
- it 'returns the validation errors' do
71
- expect(response_body['errors']).to include(required_error)
72
+ it 'returns the validation errors' do
73
+ expect(response_body['errors']).to include(required_error)
74
+ end
72
75
  end
76
+ end
73
77
 
74
- it 'does not create a model' do
75
- expect(model_class_object.count).to be 0
76
- end
78
+ it 'does not create a model' do
79
+ expect(model_class_object.count).to be 0
77
80
  end
78
81
  end
79
82
  end
@@ -2,17 +2,35 @@
2
2
 
3
3
  shared_examples 'simple crud for create with block' do
4
4
  describe 'POST #create with a render block' do
5
- it 'passes the saved record to the block' do
6
- post :create, params: with_route_params(body_params({ name: 'Blocked' }.merge(owner_params)))
5
+ context 'when successfully creating a record' do
6
+ include_context 'with authenticated user' if check_authenticate(:create)
7
+ let(:create_params) { model_params.merge(owner_params) }
7
8
 
8
- expect(response.parsed_body).to include('saved' => true)
9
+ before do
10
+ post :create, params: with_route_params(body_params(create_params)), format: request_format(:create)
11
+ end
12
+
13
+ it 'passes the saved record to the block' do
14
+ expect(response).to be_successful
15
+ end
16
+
17
+ it 'creates the record' do
18
+ expect(model_class_object.count).to be 1
19
+ end
9
20
  end
10
21
 
11
- it 'passes the failed record to the block', :aggregate_failures do
12
- post :create, params: with_route_params(body_params(owner_params))
22
+ unless check_raise_on_invalid(:create)
23
+ context 'when creating with invalid attributes' do
24
+ include_context 'with authenticated user' if check_authenticate(:create)
25
+
26
+ before do
27
+ post :create, params: with_route_params(body_params(owner_params)), format: request_format(:create)
28
+ end
13
29
 
14
- expect(response).to have_http_status(422)
15
- expect(response.parsed_body).to include('saved' => false)
30
+ it 'does not create a model' do
31
+ expect(model_class_object.count).to be 0
32
+ end
33
+ end
16
34
  end
17
35
  end
18
36
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  shared_examples 'simple crud for create with build' do
4
4
  describe 'POST #create with a build hook' do
5
- include_context 'with authenticated user'
5
+ include_context 'with authenticated user' if check_authenticate(:create)
6
6
 
7
7
  before do
8
- post :create, params: with_route_params(body_params({ name: 'Built' }))
8
+ post :create, params: with_route_params(body_params(params_for(model_class))), format: request_format(:create)
9
9
  end
10
10
 
11
11
  it 'creates the record owned by the current user' do
@@ -46,6 +46,31 @@ shared_examples 'simple crud for destroy' do
46
46
  end
47
47
  end
48
48
 
49
+ context 'when destroying fails' do
50
+ include_context 'with authenticated user' if check_authenticate(:destroy)
51
+
52
+ before do
53
+ model
54
+ allow(model).to receive(:destroy).and_return(false)
55
+ allow(SimpleCrudController).to receive(:find_record).and_return(model)
56
+ delete :destroy, params: with_route_params(record_param(:destroy, model)), format: request_format(:destroy)
57
+ end
58
+
59
+ it 'keeps the record' do
60
+ expect(model_class_object.exists?(model.id)).to be true
61
+ end
62
+
63
+ unless check_block(:destroy) || check_raise_on_invalid(:destroy)
64
+ if check_html(:destroy)
65
+ include_examples 'simple crud renders template', :show
66
+ else
67
+ it 'responds with unprocessable entity' do
68
+ expect(response).to have_http_status(unprocessable_status)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
49
74
  context 'when ID is invalid' do
50
75
  include_context 'with authenticated user' if check_authenticate(:destroy)
51
76
 
@@ -2,12 +2,20 @@
2
2
 
3
3
  shared_examples 'simple crud for destroy with block' do
4
4
  describe 'DELETE #destroy with a render block' do
5
+ include_context 'with authenticated user' if check_authenticate(:destroy)
6
+
5
7
  let!(:record) { model }
6
8
 
7
- before { delete :destroy, params: with_route_params({ id: record.id }) }
9
+ before do
10
+ delete :destroy, params: with_route_params(record_param(:destroy, record)), format: request_format(:destroy)
11
+ end
8
12
 
9
13
  it 'passes the destroyed record to the block' do
10
- expect(response.parsed_body).to eq('destroyed' => true)
14
+ expect(response).to be_successful
15
+ end
16
+
17
+ it 'destroys the record' do
18
+ expect(model_class_object.exists?(record.id)).to be false
11
19
  end
12
20
  end
13
21
  end
@@ -2,21 +2,32 @@
2
2
 
3
3
  shared_examples 'simple crud for destroy with finder' do
4
4
  describe 'DELETE #destroy with a custom finder' do
5
- include_context 'with authenticated user'
5
+ include_context 'with authenticated user' if check_authenticate(:destroy)
6
6
 
7
7
  context 'when the model exists' do
8
8
  before do
9
9
  model
10
- delete :destroy, params: with_route_params(finder_key => model.public_send(finder_key))
11
- end
12
-
13
- it 'responds with ok status' do
14
- expect(response).to have_http_status(:ok)
10
+ delete :destroy, params: with_route_params(finder_key => model.public_send(finder_key)),
11
+ format: request_format(:destroy)
15
12
  end
16
13
 
17
14
  it 'destroys the model' do
18
15
  expect(model_class_object.exists?(model.id)).to be false
19
16
  end
17
+
18
+ if check_block(:destroy)
19
+ it 'passes the destroyed record to the block' do
20
+ expect(rendered_record.id).to eq(model.id)
21
+ end
22
+ elsif check_html(:destroy)
23
+ it 'redirects to the collection' do
24
+ expect(response).to have_http_status(:found)
25
+ end
26
+ else
27
+ it 'responds with ok status' do
28
+ expect(response).to have_http_status(:ok)
29
+ end
30
+ end
20
31
  end
21
32
 
22
33
  include_examples 'simple crud not found with finder', :delete, :destroy
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples 'simple crud for edit' do
4
+ describe 'GET #edit' do
5
+ subject(:edit_request) { get :edit, params: with_route_params(edit_params), format: request_format(:edit) }
6
+
7
+ before do
8
+ model
9
+ end
10
+
11
+ context 'without authenticated user' do
12
+ subject!(:req) do
13
+ get :edit, params: with_route_params(record_param(:edit, model)), format: request_format(:edit)
14
+ end
15
+
16
+ include_examples 'unauthorized when not logged in' if check_authenticate(:edit)
17
+ end
18
+
19
+ if check_authorize(:edit)
20
+ context 'when the user is not authorized' do
21
+ include_context 'with authenticated user' if check_authenticate(:edit)
22
+
23
+ let(:edit_params) { record_param(:edit, model) }
24
+
25
+ before do
26
+ model
27
+ make_policies_fail(:edit)
28
+ edit_request
29
+ end
30
+
31
+ it 'responds with forbidden status' do
32
+ expect(response).to have_http_status(:forbidden)
33
+ end
34
+ end
35
+ end
36
+
37
+ context 'when the model exists' do
38
+ include_context 'with authenticated user' if check_authenticate(:edit)
39
+
40
+ let(:edit_params) { record_param(:edit, model) }
41
+
42
+ before do
43
+ model
44
+ make_policies_succeed(:edit) if check_authorize(:edit)
45
+ edit_request
46
+ end
47
+
48
+ it 'responds with ok status' do
49
+ expect(response).to have_http_status(:ok)
50
+ end
51
+
52
+ if check_block(:edit)
53
+ it 'renders the asked model through the block' do
54
+ expect(response).to be_successful
55
+ end
56
+ elsif check_html(:edit)
57
+ include_examples 'simple crud renders template', :edit
58
+ else
59
+ it 'returns the asked model' do
60
+ expect(response_body['id']).to eq(model.id)
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'when the model does not exist' do
66
+ include_context 'with authenticated user'
67
+
68
+ let(:edit_params) { record_param(:edit, nil, not_found: true) }
69
+
70
+ before do
71
+ edit_request
72
+ end
73
+
74
+ it 'responds with not found status' do
75
+ expect(response).to have_http_status(:not_found)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -13,7 +13,7 @@ shared_examples 'simple crud for index' do
13
13
  include_context 'with authenticated user' if check_authenticate(:index)
14
14
 
15
15
  before do
16
- make_policies_succeed(:index)
16
+ make_policies_succeed(:index) if check_authorize(:index)
17
17
  get :index, params: with_route_params({}), format: request_format(:index)
18
18
  end
19
19
 
@@ -2,13 +2,21 @@
2
2
 
3
3
  shared_examples 'simple crud for index with block' do
4
4
  describe 'GET #index with a render block' do
5
- let(:created_models) { create_records(model_class, 2, {}) }
5
+ let(:created_models) { create_records(model_class, 2, model_attributes) }
6
+
7
+ include_context 'with authenticated user' if check_authenticate(:index)
6
8
 
7
9
  before do
8
10
  created_models
9
- get :index, params: with_route_params({}), format: :html
11
+ get :index, params: with_route_params({}), format: request_format(:index)
10
12
  end
11
13
 
12
- include_examples 'simple crud renders template', :index
14
+ if check_html(:index)
15
+ include_examples 'simple crud renders template', :index
16
+ else
17
+ it 'renders the records through the block' do
18
+ expect(response).to be_successful
19
+ end
20
+ end
13
21
  end
14
22
  end
@@ -1,21 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  shared_examples 'simple crud for index with scope' do
4
- let(:user) { current_user }
5
- let(:other_user) { resolve(config.other_user) }
6
- let(:my_models) { create_records(model_class, 2, owner_association => user) }
7
- let(:other_models) { create_records(model_class, 3, owner_association => other_user) }
8
4
  let(:request_params) { {} }
5
+ let(:my_models) { create_records(model_class, 2, scoped_attributes) }
6
+ let(:other_models) { create_records(model_class, 3, other_scoped_attributes) }
9
7
 
10
- before do
11
- authenticate_request
12
- make_policies_succeed(:index)
13
- my_models
14
- other_models
15
- get :index, params: with_route_params(request_params)
16
- end
8
+ # Hooks stay inside this context so co-including the example never
9
+ # authenticates or stubs policies for sibling examples.
10
+ context 'when listing the scoped records' do
11
+ before do
12
+ authenticate_request if check_authenticate(:index)
13
+ make_policies_succeed(:index) if check_authorize(:index)
14
+ my_models
15
+ other_models
16
+ get :index, params: with_route_params(request_params), format: request_format(:index)
17
+ end
17
18
 
18
- it 'returns only the scoped records' do
19
- expect(response_body['page'].map { |m| m['id'] }).to match_array(my_models.map(&:id))
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))
30
+ end
31
+ end
20
32
  end
21
33
  end