wor-simple_crud 0.3.0 → 0.4.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/CHANGELOG.md +31 -2
  4. data/README.md +93 -26
  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 +24 -0
  10. data/lib/simple_crud/rspec/helpers/models.rb +70 -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 +33 -81
  17. data/lib/simple_crud/version.rb +1 -1
  18. data/lib/simple_crud.rb +1 -0
  19. data/lib/spec/shared_examples/authorization_adapter_authorize.rb +1 -1
  20. data/lib/spec/shared_examples/simple_crud_for_create.rb +16 -13
  21. data/lib/spec/shared_examples/simple_crud_for_create_with_block.rb +26 -8
  22. data/lib/spec/shared_examples/simple_crud_for_create_with_build.rb +3 -3
  23. data/lib/spec/shared_examples/simple_crud_for_destroy.rb +26 -1
  24. data/lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb +11 -3
  25. data/lib/spec/shared_examples/simple_crud_for_destroy_with_finder.rb +18 -7
  26. data/lib/spec/shared_examples/simple_crud_for_edit.rb +79 -0
  27. data/lib/spec/shared_examples/simple_crud_for_index.rb +2 -2
  28. data/lib/spec/shared_examples/simple_crud_for_index_with_block.rb +12 -4
  29. data/lib/spec/shared_examples/simple_crud_for_index_with_scope.rb +26 -14
  30. data/lib/spec/shared_examples/simple_crud_for_new.rb +2 -2
  31. data/lib/spec/shared_examples/simple_crud_for_new_with_block.rb +14 -3
  32. data/lib/spec/shared_examples/simple_crud_for_new_with_build.rb +11 -5
  33. data/lib/spec/shared_examples/simple_crud_for_show.rb +1 -1
  34. data/lib/spec/shared_examples/simple_crud_for_show_with_block.rb +14 -3
  35. data/lib/spec/shared_examples/simple_crud_for_show_with_finder.rb +17 -8
  36. data/lib/spec/shared_examples/simple_crud_for_update.rb +17 -10
  37. data/lib/spec/shared_examples/simple_crud_for_update_with_finder.rb +17 -7
  38. data/lib/spec/shared_examples/simple_crud_not_found_with_finder.rb +3 -2
  39. data/lib/spec/shared_examples/simple_crud_renders_template.rb +1 -1
  40. data/lib/spec/shared_examples/simple_crud_when_not_authorized.rb +1 -1
  41. data/lib/spec/shared_examples/simple_crud_without_authenticated_user.rb +1 -1
  42. data/lib/spec/shared_examples/unauthorized_when_not_logged_in.rb +1 -1
  43. data/spec/block_finder/dummy_models_controller_spec.rb +10 -0
  44. data/spec/block_json/dummy_models_controller_spec.rb +9 -0
  45. data/spec/block_redirect/dummy_models_controller_spec.rb +7 -0
  46. data/spec/dummy/app/controllers/block/dummy_models_controller.rb +1 -6
  47. data/spec/dummy/app/controllers/block_finder/dummy_models_controller.rb +25 -0
  48. data/spec/dummy/app/controllers/block_json/dummy_models_controller.rb +17 -0
  49. data/spec/dummy/app/controllers/block_new/dummy_models_controller.rb +1 -1
  50. data/spec/dummy/app/controllers/block_redirect/dummy_models_controller.rb +13 -0
  51. data/spec/dummy/app/controllers/block_show/dummy_models_controller.rb +1 -1
  52. data/spec/dummy/app/controllers/built/dummy_models_controller.rb +0 -5
  53. data/spec/dummy/app/controllers/dummy_models_controller.rb +0 -5
  54. data/spec/dummy/app/controllers/finder/dummy_models_controller.rb +1 -5
  55. data/spec/dummy/app/controllers/html_finder/dummy_models_controller.rb +17 -0
  56. data/spec/dummy/app/controllers/html_modes/base_controller.rb +0 -5
  57. data/spec/dummy/app/controllers/html_scoped/dummy_models_controller.rb +1 -1
  58. data/spec/dummy/app/controllers/invalid_status/dummy_models_controller.rb +1 -1
  59. data/spec/dummy/app/controllers/nested/dummy_models_controller.rb +0 -5
  60. data/spec/dummy/app/controllers/nested_route/dummy_models_controller.rb +0 -5
  61. data/spec/dummy/app/controllers/redirect_auth/dummy_models_controller.rb +0 -5
  62. data/spec/dummy/app/controllers/scoped/dummy_models_controller.rb +0 -5
  63. data/spec/dummy/app/controllers/scoped_params/dummy_models_controller.rb +0 -5
  64. data/spec/dummy/app/controllers/strict/dummy_models_controller.rb +0 -5
  65. data/spec/dummy/app/controllers/unpaginated_scoped/dummy_models_controller.rb +11 -0
  66. data/spec/dummy/app/controllers/without_pagination/dummy_models_controller.rb +0 -5
  67. data/spec/dummy/app/models/dummy_model_policy.rb +5 -1
  68. data/spec/dummy/app/views/html_finder/dummy_models/edit.html.erb +1 -0
  69. data/spec/dummy/app/views/html_finder/dummy_models/new.html.erb +1 -0
  70. data/spec/dummy/app/views/html_finder/dummy_models/show.html.erb +1 -0
  71. data/spec/dummy/config/routes.rb +21 -1
  72. data/spec/dummy_model_policy_spec.rb +18 -8
  73. data/spec/finder/dummy_models_controller_spec.rb +1 -0
  74. data/spec/html_finder/dummy_models_controller_spec.rb +11 -0
  75. data/spec/html_scoped/dummy_models_controller_spec.rb +1 -1
  76. data/spec/scoped_params/dummy_models_controller_spec.rb +4 -3
  77. data/spec/simple_crud/rspec_config_spec.rb +39 -0
  78. data/spec/simple_crud_controller_spec.rb +38 -0
  79. data/spec/unpaginated_scoped/dummy_models_controller_spec.rb +7 -0
  80. metadata +23 -2
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCrud
4
+ module RSpec
5
+ module Helpers
6
+ # Request params and format helpers shared by every action example.
7
+ module Requests
8
+ # :unprocessable_entity was renamed :unprocessable_content in Rack 3.1.
9
+ # Resolve the current Rack's canonical symbol for 422.
10
+ def unprocessable_status
11
+ Rack::Utils::SYMBOL_TO_STATUS_CODE.invert[422]
12
+ end
13
+
14
+ def request_format(action)
15
+ check_html(action) ? :html : :json
16
+ end
17
+
18
+ # Extra params (e.g. a parent slug) added to every request, for nested
19
+ # resources like /projects/:project_slug/tasks.
20
+ def route_params
21
+ resolve(setting(:route_params))
22
+ end
23
+
24
+ def with_route_params(attrs)
25
+ route_params.merge(attrs)
26
+ end
27
+
28
+ # The params that identify a record for the given action, using the
29
+ # configured finder_key when the action has a custom finder.
30
+ def record_param(action, record, not_found: false)
31
+ key = check_finder(action) ? finder_key : :id
32
+ value = not_found ? "nonexistent-#{key}" : record.public_send(key)
33
+ { key => value }
34
+ end
35
+
36
+ # Wraps a request body under the model's strong-params key when
37
+ # params_key is configured (nested strong params), else passes it flat.
38
+ def body_params(attrs)
39
+ key = params_key
40
+ key ? { key => attrs } : attrs
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCrud
4
+ module RSpec
5
+ module Helpers
6
+ # Settings resolution: `simple_crud:` example-group metadata first,
7
+ # falling back to the app-wide SimpleCrud::RSpec::Config singleton.
8
+ module Settings
9
+ def config
10
+ SimpleCrud::RSpec::Config.instance
11
+ end
12
+
13
+ def simple_crud_settings
14
+ source = ::RSpec.current_example ? ::RSpec.current_example.metadata : metadata
15
+ source[:simple_crud] || {}
16
+ end
17
+
18
+ def setting(name)
19
+ simple_crud_settings.fetch(name) { config.public_send(name) }
20
+ end
21
+
22
+ def resolve(value)
23
+ value.is_a?(Proc) ? instance_exec(&value) : value
24
+ end
25
+
26
+ %i[owner_association required_attribute required_error finder_key params_key invalid_status
27
+ unauthenticated_status assert_html_template scoped_attributes other_scoped_attributes].each do |name|
28
+ define_method(name) { resolve(setting(name)) }
29
+ end
30
+
31
+ def created_record_check
32
+ setting(:created_record_check)
33
+ end
34
+
35
+ # Runs the block with a config setting temporarily overridden, restoring
36
+ # the original value afterwards (even if the block raises). Useful when
37
+ # a controller's spec needs a different setting than the app-wide default.
38
+ def with_config_override(setting, value)
39
+ config_instance = SimpleCrud::RSpec::Config.instance
40
+ original = config_instance.public_send(setting)
41
+ SimpleCrud::RSpec.configure { |c| c.public_send("#{setting}=", value) }
42
+ yield
43
+ ensure
44
+ SimpleCrud::RSpec.configure { |c| c.public_send("#{setting}=", original) }
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -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,27 +3,30 @@
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
10
-
11
- cattr_accessor :params, :permitted
11
+ # Include, not extend: controllers gain these by extending SimpleCrudController.
12
+ include SimpleCrud::ActionLambdas
12
13
 
13
14
  # Possible options:
14
15
  ### authorize: check authorization via Config.authorization_adapter (Pundit by default)
15
16
  ### paginate: paginate the list via Config.pagination_adapter (wor-paginate by default)
16
- ### authenticate: use devise to authenticate
17
+ ### authenticate: true calls authenticate_user! inside the action lambda; false skips it
18
+ ### authenticate_headers: whether shared examples set auth headers and run the unauthorized
19
+ ### test (defaults to authenticate:); set independently when a base-controller before_action handles auth
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
- parameters[:html] = true if block && !parameters.key?(:html)
29
+ parameters[:block] = true if block
27
30
  parameters = parameters_with_defaults(parameters)
28
31
  klass = simple_crud_controller_model
29
32
  check_valid_method(method)
@@ -33,83 +36,34 @@ module SimpleCrudController
33
36
  write_metadata(method, parameters)
34
37
  end
35
38
 
36
- def parameters_with_defaults(parameters)
37
- defaults = {
38
- authorize: true, paginate: true, authenticate: true, serializer: nil,
39
- html: false, finder: nil, scope: nil, build: nil, raise_on_invalid: false
40
- }
41
- defaults.each { |key, value| parameters[key] = value unless parameters.key?(key) }
42
- parameters
43
- end
44
-
45
- def write_metadata(method, parameters)
46
- @simple_crud_metadata ||= {}
47
- @simple_crud_metadata[method] = parameters
48
- end
49
-
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
39
+ def simple_crud_defaults(options = {})
40
+ @simple_crud_defaults = simple_crud_inherited_defaults.merge(options)
68
41
  end
69
42
 
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
43
+ def simple_crud_inherited_defaults
44
+ ancestors.each do |ancestor|
45
+ next unless ancestor.instance_variable_defined?(:@simple_crud_defaults)
78
46
 
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)
47
+ return ancestor.instance_variable_get(:@simple_crud_defaults)
89
48
  end
49
+ {}
90
50
  end
91
51
 
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)
52
+ def parameters_with_defaults(parameters)
53
+ defaults = {
54
+ authorize: true, paginate: true, authenticate: true, authenticate_headers: nil,
55
+ serializer: nil, html: false, finder: nil, scope: nil, build: nil, raise_on_invalid: false
56
+ }
57
+ defaults.merge(simple_crud_inherited_defaults).each do |key, value|
58
+ parameters[key] = value unless parameters.key?(key)
101
59
  end
60
+ parameters[:authenticate_headers] = parameters[:authenticate] if parameters[:authenticate_headers].nil?
61
+ parameters
102
62
  end
103
63
 
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
64
+ def write_metadata(method, parameters)
65
+ @simple_crud_metadata ||= {}
66
+ @simple_crud_metadata[method] = parameters
113
67
  end
114
68
 
115
69
  def simple_crud_controller_model
@@ -117,7 +71,7 @@ module SimpleCrudController
117
71
  end
118
72
 
119
73
  def check_valid_method(method)
120
- raise ArgumentError, 'invalid method' unless %i[show index create update destroy new].include? method
74
+ raise ArgumentError, 'invalid method' unless %i[show index create update destroy new edit].include? method
121
75
  end
122
76
 
123
77
  def check_policies(parameters)
@@ -130,11 +84,9 @@ module SimpleCrudController
130
84
  end
131
85
 
132
86
  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)
87
+ name = parameters[:serializer].to_s
88
+ return if name.blank? || Kernel.const_defined?(name)
137
89
 
138
- raise ArgumentError, "create a valid serializer with name #{serializer_name}"
90
+ raise ArgumentError, "create a valid serializer with name #{name}"
139
91
  end
140
92
  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.4.0'
5
5
  end
data/lib/simple_crud.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'simple_crud/version'
4
4
  require_relative 'simple_crud/config'
5
+ require_relative 'simple_crud/authorization/adapter'
5
6
  require_relative 'simple_crud/simple_crud_controller'
6
7
 
7
8
  # Simplified CRUD endpoints for Rails API controllers.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- shared_examples 'authorization adapter #authorize' do |record_class, denied_error|
3
+ RSpec.shared_examples 'authorization adapter #authorize' do |record_class, denied_error|
4
4
  it 'passes for a record the adapter allows' do
5
5
  expect { adapter.authorize(controller, record_class.new(1)) }.not_to raise_error
6
6
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- shared_examples 'simple crud for create' do
3
+ RSpec.shared_examples 'simple crud for create' do
4
4
  describe 'POST #create' do
5
5
  context 'without authenticated user' do
6
6
  subject!(:req) do
@@ -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
@@ -1,18 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- shared_examples 'simple crud for create with block' do
3
+ RSpec.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
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- shared_examples 'simple crud for create with build' do
3
+ RSpec.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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- shared_examples 'simple crud for destroy' do
3
+ RSpec.shared_examples 'simple crud for destroy' do
4
4
  describe 'DELETE #destroy' do
5
5
  context 'without authenticated user' do
6
6
  subject!(:req) 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
 
@@ -1,13 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- shared_examples 'simple crud for destroy with block' do
3
+ RSpec.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