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.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/CHANGELOG.md +31 -2
- data/README.md +93 -26
- data/lib/simple_crud/action_lambdas.rb +81 -0
- data/lib/simple_crud/controller_helpers.rb +11 -2
- data/lib/simple_crud/rspec/config.rb +18 -5
- data/lib/simple_crud/rspec/helpers/authentication.rb +32 -0
- data/lib/simple_crud/rspec/helpers/controller_options.rb +24 -0
- data/lib/simple_crud/rspec/helpers/models.rb +70 -0
- data/lib/simple_crud/rspec/helpers/policies.rb +25 -0
- data/lib/simple_crud/rspec/helpers/requests.rb +45 -0
- data/lib/simple_crud/rspec/helpers/settings.rb +49 -0
- data/lib/simple_crud/rspec/helpers.rb +16 -138
- data/lib/simple_crud/rspec.rb +1 -0
- data/lib/simple_crud/simple_crud_controller.rb +33 -81
- data/lib/simple_crud/version.rb +1 -1
- data/lib/simple_crud.rb +1 -0
- data/lib/spec/shared_examples/authorization_adapter_authorize.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_create.rb +16 -13
- data/lib/spec/shared_examples/simple_crud_for_create_with_block.rb +26 -8
- data/lib/spec/shared_examples/simple_crud_for_create_with_build.rb +3 -3
- data/lib/spec/shared_examples/simple_crud_for_destroy.rb +26 -1
- data/lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb +11 -3
- data/lib/spec/shared_examples/simple_crud_for_destroy_with_finder.rb +18 -7
- data/lib/spec/shared_examples/simple_crud_for_edit.rb +79 -0
- data/lib/spec/shared_examples/simple_crud_for_index.rb +2 -2
- data/lib/spec/shared_examples/simple_crud_for_index_with_block.rb +12 -4
- data/lib/spec/shared_examples/simple_crud_for_index_with_scope.rb +26 -14
- data/lib/spec/shared_examples/simple_crud_for_new.rb +2 -2
- data/lib/spec/shared_examples/simple_crud_for_new_with_block.rb +14 -3
- data/lib/spec/shared_examples/simple_crud_for_new_with_build.rb +11 -5
- data/lib/spec/shared_examples/simple_crud_for_show.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_for_show_with_block.rb +14 -3
- data/lib/spec/shared_examples/simple_crud_for_show_with_finder.rb +17 -8
- data/lib/spec/shared_examples/simple_crud_for_update.rb +17 -10
- data/lib/spec/shared_examples/simple_crud_for_update_with_finder.rb +17 -7
- data/lib/spec/shared_examples/simple_crud_not_found_with_finder.rb +3 -2
- data/lib/spec/shared_examples/simple_crud_renders_template.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_when_not_authorized.rb +1 -1
- data/lib/spec/shared_examples/simple_crud_without_authenticated_user.rb +1 -1
- data/lib/spec/shared_examples/unauthorized_when_not_logged_in.rb +1 -1
- data/spec/block_finder/dummy_models_controller_spec.rb +10 -0
- data/spec/block_json/dummy_models_controller_spec.rb +9 -0
- data/spec/block_redirect/dummy_models_controller_spec.rb +7 -0
- data/spec/dummy/app/controllers/block/dummy_models_controller.rb +1 -6
- data/spec/dummy/app/controllers/block_finder/dummy_models_controller.rb +25 -0
- data/spec/dummy/app/controllers/block_json/dummy_models_controller.rb +17 -0
- data/spec/dummy/app/controllers/block_new/dummy_models_controller.rb +1 -1
- data/spec/dummy/app/controllers/block_redirect/dummy_models_controller.rb +13 -0
- data/spec/dummy/app/controllers/block_show/dummy_models_controller.rb +1 -1
- data/spec/dummy/app/controllers/built/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/controllers/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/controllers/finder/dummy_models_controller.rb +1 -5
- data/spec/dummy/app/controllers/html_finder/dummy_models_controller.rb +17 -0
- data/spec/dummy/app/controllers/html_modes/base_controller.rb +0 -5
- data/spec/dummy/app/controllers/html_scoped/dummy_models_controller.rb +1 -1
- data/spec/dummy/app/controllers/invalid_status/dummy_models_controller.rb +1 -1
- data/spec/dummy/app/controllers/nested/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/controllers/nested_route/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/controllers/redirect_auth/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/controllers/scoped/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/controllers/scoped_params/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/controllers/strict/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/controllers/unpaginated_scoped/dummy_models_controller.rb +11 -0
- data/spec/dummy/app/controllers/without_pagination/dummy_models_controller.rb +0 -5
- data/spec/dummy/app/models/dummy_model_policy.rb +5 -1
- data/spec/dummy/app/views/html_finder/dummy_models/edit.html.erb +1 -0
- data/spec/dummy/app/views/html_finder/dummy_models/new.html.erb +1 -0
- data/spec/dummy/app/views/html_finder/dummy_models/show.html.erb +1 -0
- data/spec/dummy/config/routes.rb +21 -1
- data/spec/dummy_model_policy_spec.rb +18 -8
- data/spec/finder/dummy_models_controller_spec.rb +1 -0
- data/spec/html_finder/dummy_models_controller_spec.rb +11 -0
- data/spec/html_scoped/dummy_models_controller_spec.rb +1 -1
- data/spec/scoped_params/dummy_models_controller_spec.rb +4 -3
- data/spec/simple_crud/rspec_config_spec.rb +39 -0
- data/spec/simple_crud_controller_spec.rb +38 -0
- data/spec/unpaginated_scoped/dummy_models_controller_spec.rb +7 -0
- 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)
|
|
7
|
-
#
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
data/lib/simple_crud/rspec.rb
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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[:
|
|
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
|
|
37
|
-
|
|
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
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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 #{
|
|
90
|
+
raise ArgumentError, "create a valid serializer with name #{name}"
|
|
139
91
|
end
|
|
140
92
|
end
|
data/lib/simple_crud/version.rb
CHANGED
data/lib/simple_crud.rb
CHANGED
|
@@ -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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|