wor-simple_crud 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -0
- data/Gemfile +1 -0
- data/README.md +157 -2
- data/lib/simple_crud/authorization/adapter.rb +7 -0
- data/lib/simple_crud/authorization/pundit_adapter.rb +6 -0
- data/lib/simple_crud/config.rb +6 -1
- data/lib/simple_crud/controller_helpers.rb +119 -0
- data/lib/simple_crud/pagination/adapter.rb +7 -1
- data/lib/simple_crud/pagination/kaminari_adapter.rb +23 -2
- data/lib/simple_crud/pagination/pagy_adapter.rb +7 -2
- data/lib/simple_crud/pagination/will_paginate_adapter.rb +7 -3
- data/lib/simple_crud/pagination/wor_paginate_adapter.rb +7 -2
- data/lib/simple_crud/rspec/config.rb +61 -0
- data/lib/simple_crud/rspec/helpers.rb +147 -0
- data/lib/simple_crud/rspec.rb +34 -0
- data/lib/simple_crud/simple_crud_controller.rb +51 -36
- data/lib/simple_crud/version.rb +1 -1
- data/lib/spec/shared_contexts/authenticate_user.rb +1 -7
- data/lib/spec/shared_examples/authorization_adapter_authorize.rb +11 -0
- data/lib/spec/shared_examples/simple_crud_for_create.rb +43 -11
- data/lib/spec/shared_examples/simple_crud_for_create_with_block.rb +18 -0
- data/lib/spec/shared_examples/simple_crud_for_create_with_build.rb +15 -0
- data/lib/spec/shared_examples/simple_crud_for_destroy.rb +19 -8
- data/lib/spec/shared_examples/simple_crud_for_destroy_with_block.rb +13 -0
- data/lib/spec/shared_examples/simple_crud_for_destroy_with_finder.rb +24 -0
- data/lib/spec/shared_examples/simple_crud_for_index.rb +8 -23
- data/lib/spec/shared_examples/simple_crud_for_index_with_block.rb +14 -0
- data/lib/spec/shared_examples/simple_crud_for_index_with_scope.rb +21 -0
- data/lib/spec/shared_examples/simple_crud_for_new.rb +29 -0
- data/lib/spec/shared_examples/simple_crud_for_new_with_block.rb +9 -0
- data/lib/spec/shared_examples/simple_crud_for_new_with_build.rb +13 -0
- data/lib/spec/shared_examples/simple_crud_for_show.rb +14 -10
- data/lib/spec/shared_examples/simple_crud_for_show_with_block.rb +11 -0
- data/lib/spec/shared_examples/simple_crud_for_show_with_finder.rb +24 -0
- data/lib/spec/shared_examples/simple_crud_for_update.rb +55 -15
- data/lib/spec/shared_examples/simple_crud_for_update_with_finder.rb +29 -0
- data/lib/spec/shared_examples/simple_crud_not_found_with_finder.rb +13 -0
- data/lib/spec/shared_examples/simple_crud_renders_template.rb +8 -0
- data/lib/spec/shared_examples/simple_crud_when_not_authorized.rb +18 -0
- data/lib/spec/shared_examples/simple_crud_without_authenticated_user.rb +9 -0
- data/lib/spec/shared_examples/unauthorized_when_not_logged_in.rb +2 -2
- data/spec/block/dummy_models_controller_spec.rb +8 -0
- data/spec/block_create/dummy_models_controller_spec.rb +7 -0
- data/spec/block_destroy/dummy_models_controller_spec.rb +7 -0
- data/spec/block_new/dummy_models_controller_spec.rb +7 -0
- data/spec/block_show/dummy_models_controller_spec.rb +7 -0
- data/spec/built/dummy_models_controller_spec.rb +8 -0
- data/spec/dummy/app/controllers/block/dummy_models_controller.rb +17 -0
- data/spec/dummy/app/controllers/block_create/dummy_models_controller.rb +13 -0
- data/spec/dummy/app/controllers/block_destroy/dummy_models_controller.rb +9 -0
- data/spec/dummy/app/controllers/block_modes/base_controller.rb +11 -0
- data/spec/dummy/app/controllers/block_new/dummy_models_controller.rb +9 -0
- data/spec/dummy/app/controllers/block_show/dummy_models_controller.rb +9 -0
- data/spec/dummy/app/controllers/built/dummy_models_controller.rb +21 -0
- data/spec/dummy/app/controllers/dummy_models_controller.rb +2 -1
- data/spec/dummy/app/controllers/finder/dummy_models_controller.rb +22 -0
- data/spec/dummy/app/controllers/html/dummy_models_controller.rb +7 -0
- data/spec/dummy/app/controllers/html_create/dummy_models_controller.rb +7 -0
- data/spec/dummy/app/controllers/html_destroy/dummy_models_controller.rb +7 -0
- data/spec/dummy/app/controllers/html_modes/base_controller.rb +17 -0
- data/spec/dummy/app/controllers/html_new/dummy_models_controller.rb +7 -0
- data/spec/dummy/app/controllers/html_scoped/dummy_models_controller.rb +8 -0
- data/spec/dummy/app/controllers/html_show/dummy_models_controller.rb +7 -0
- data/spec/dummy/app/controllers/html_update/dummy_models_controller.rb +7 -0
- data/spec/dummy/app/controllers/invalid_status/dummy_models_controller.rb +13 -0
- data/spec/dummy/app/controllers/nested/dummy_models_controller.rb +21 -0
- data/spec/dummy/app/controllers/nested_route/dummy_models_controller.rb +26 -0
- data/spec/dummy/app/controllers/redirect/dummy_models_controller.rb +10 -0
- data/spec/dummy/app/controllers/redirect_auth/dummy_models_controller.rb +28 -0
- data/spec/dummy/app/controllers/scoped/dummy_models_controller.rb +16 -0
- data/spec/dummy/app/controllers/scoped_params/dummy_models_controller.rb +16 -0
- data/spec/dummy/app/controllers/strict/dummy_models_controller.rb +21 -0
- data/spec/dummy/app/models/dummy_model.rb +4 -0
- data/spec/dummy/app/models/dummy_model_policy.rb +4 -0
- data/spec/dummy/app/views/block/dummy_models/index.html.erb +3 -0
- data/spec/dummy/app/views/block_new/dummy_models/new.html.erb +1 -0
- data/spec/dummy/app/views/block_show/dummy_models/show.html.erb +1 -0
- data/spec/dummy/app/views/html/dummy_models/index.html.erb +3 -0
- data/spec/dummy/app/views/html_create/dummy_models/new.html.erb +1 -0
- data/spec/dummy/app/views/html_destroy/dummy_models/show.html.erb +1 -0
- data/spec/dummy/app/views/html_new/dummy_models/new.html.erb +1 -0
- data/spec/dummy/app/views/html_scoped/dummy_models/index.html.erb +3 -0
- data/spec/dummy/app/views/html_show/dummy_models/show.html.erb +1 -0
- data/spec/dummy/app/views/html_update/dummy_models/edit.html.erb +1 -0
- data/spec/dummy/app/views/invalid_status/dummy_models/new.html.erb +1 -0
- data/spec/dummy/app/views/layouts/application.html.erb +4 -0
- data/spec/dummy/app/views/redirect/dummy_models/new.html.erb +1 -0
- data/spec/dummy/config/routes.rb +90 -0
- data/spec/dummy/db/migrate/20260806000000_add_slug_to_dummy_models.rb +7 -0
- data/spec/dummy/db/migrate/20260807000000_add_classroom_slug_to_dummy_models.rb +7 -0
- data/spec/dummy/db/schema.rb +3 -1
- data/spec/dummy/spec/factories/dummy_models.rb +1 -0
- data/spec/dummy_controller_spec.rb +1 -0
- data/spec/dummy_model_policy_spec.rb +8 -16
- data/spec/finder/dummy_models_controller_spec.rb +12 -0
- data/spec/html/dummy_models_controller_spec.rb +7 -0
- data/spec/html_create/dummy_models_controller_spec.rb +7 -0
- data/spec/html_destroy/dummy_models_controller_spec.rb +7 -0
- data/spec/html_new/dummy_models_controller_spec.rb +7 -0
- data/spec/html_scoped/dummy_models_controller_spec.rb +7 -0
- data/spec/html_show/dummy_models_controller_spec.rb +7 -0
- data/spec/html_update/dummy_models_controller_spec.rb +7 -0
- data/spec/invalid_status/dummy_models_controller_spec.rb +10 -0
- data/spec/nested/dummy_models_controller_spec.rb +12 -0
- data/spec/nested_route/dummy_models_controller_spec.rb +19 -0
- data/spec/redirect/dummy_models_controller_spec.rb +23 -0
- data/spec/redirect_auth/dummy_models_controller_spec.rb +11 -0
- data/spec/scoped/dummy_models_controller_spec.rb +7 -0
- data/spec/scoped_params/dummy_models_controller_spec.rb +11 -0
- data/spec/simple_crud/authorization/action_policy_adapter_spec.rb +1 -9
- data/spec/simple_crud/authorization/can_can_can_adapter_spec.rb +1 -7
- data/spec/simple_crud/authorization/pundit_adapter_spec.rb +41 -0
- data/spec/simple_crud/controller_helpers_spec.rb +49 -0
- data/spec/simple_crud/pagination/adapters_spec.rb +37 -13
- data/spec/simple_crud/rspec_config_spec.rb +67 -0
- data/spec/simple_crud_configuration_spec.rb +19 -16
- data/spec/simple_crud_controller_spec.rb +4 -4
- data/spec/spec_helper.rb +1 -0
- data/spec/strict/dummy_models_controller_spec.rb +23 -0
- metadata +87 -4
- data/lib/spec/shared_examples/helpers.rb +0 -50
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCrud
|
|
4
|
+
module RSpec
|
|
5
|
+
# 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.
|
|
8
|
+
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
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
data/lib/simple_crud/rspec.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'rspec/config'
|
|
4
|
+
require_relative 'rspec/helpers'
|
|
3
5
|
require_relative '../spec/response_helper'
|
|
4
6
|
require_relative '../spec/shared_contexts/authenticate_user'
|
|
5
7
|
require_relative '../spec/shared_examples/simple_crud_for_update'
|
|
@@ -7,5 +9,37 @@ require_relative '../spec/shared_examples/simple_crud_for_show'
|
|
|
7
9
|
require_relative '../spec/shared_examples/simple_crud_for_index'
|
|
8
10
|
require_relative '../spec/shared_examples/simple_crud_for_destroy'
|
|
9
11
|
require_relative '../spec/shared_examples/simple_crud_for_create'
|
|
12
|
+
require_relative '../spec/shared_examples/simple_crud_for_new'
|
|
10
13
|
require_relative '../spec/shared_examples/unauthorized_when_not_logged_in'
|
|
14
|
+
require_relative '../spec/shared_examples/simple_crud_renders_template'
|
|
15
|
+
require_relative '../spec/shared_examples/simple_crud_without_authenticated_user'
|
|
16
|
+
require_relative '../spec/shared_examples/simple_crud_when_not_authorized'
|
|
17
|
+
require_relative '../spec/shared_examples/simple_crud_for_index_with_block'
|
|
18
|
+
require_relative '../spec/shared_examples/simple_crud_for_index_with_scope'
|
|
19
|
+
require_relative '../spec/shared_examples/simple_crud_for_show_with_block'
|
|
20
|
+
require_relative '../spec/shared_examples/simple_crud_for_new_with_block'
|
|
21
|
+
require_relative '../spec/shared_examples/simple_crud_for_create_with_block'
|
|
22
|
+
require_relative '../spec/shared_examples/simple_crud_for_destroy_with_block'
|
|
23
|
+
require_relative '../spec/shared_examples/simple_crud_for_show_with_finder'
|
|
24
|
+
require_relative '../spec/shared_examples/simple_crud_for_update_with_finder'
|
|
25
|
+
require_relative '../spec/shared_examples/simple_crud_for_destroy_with_finder'
|
|
26
|
+
require_relative '../spec/shared_examples/simple_crud_not_found_with_finder'
|
|
27
|
+
require_relative '../spec/shared_examples/simple_crud_for_new_with_build'
|
|
28
|
+
require_relative '../spec/shared_examples/simple_crud_for_create_with_build'
|
|
29
|
+
require_relative '../spec/shared_examples/authorization_adapter_authorize'
|
|
11
30
|
require_relative '../spec/matchers/have_been_serialized_with'
|
|
31
|
+
|
|
32
|
+
module SimpleCrud
|
|
33
|
+
# Opt-in wiring for the shared examples: see .install!
|
|
34
|
+
module RSpec
|
|
35
|
+
# Opt-in wiring: call from your spec support after requiring this file.
|
|
36
|
+
# extend wires definition-time helpers (check_html...), include runtime ones (model...).
|
|
37
|
+
def self.install!
|
|
38
|
+
::RSpec.configure do |config|
|
|
39
|
+
config.extend Helpers
|
|
40
|
+
config.include Helpers
|
|
41
|
+
config.include Response::JSONParser, type: :controller
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
require 'active_support/all'
|
|
4
4
|
require_relative 'config'
|
|
5
|
+
require_relative 'controller_helpers'
|
|
5
6
|
|
|
6
7
|
# Extended onto a controller for CRUD actions.
|
|
7
8
|
module SimpleCrudController
|
|
9
|
+
extend SimpleCrud::ControllerHelpers
|
|
10
|
+
|
|
8
11
|
cattr_accessor :params, :permitted
|
|
9
12
|
|
|
10
13
|
# Possible options:
|
|
@@ -12,21 +15,30 @@ module SimpleCrudController
|
|
|
12
15
|
### paginate: paginate the list via Config.pagination_adapter (wor-paginate by default)
|
|
13
16
|
### authenticate: use devise to authenticate
|
|
14
17
|
### serializer: use a particular serializer (both each_serializer and serializer)
|
|
15
|
-
|
|
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
|
|
20
|
+
### scope: custom index scope (Proc/lambda taking current_user and optional params), overrides policy_scope
|
|
21
|
+
### build: custom record builder (Proc/lambda) for :new/:create, invoked with the controller as self
|
|
22
|
+
### raise_on_invalid: use strict create!/update! semantics instead of returning 422
|
|
23
|
+
### 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
|
|
25
|
+
def simple_crud_for(method, parameters = {}, &block)
|
|
26
|
+
parameters[:html] = true if block && !parameters.key?(:html)
|
|
16
27
|
parameters = parameters_with_defaults(parameters)
|
|
17
28
|
klass = simple_crud_controller_model
|
|
18
29
|
check_valid_method(method)
|
|
19
30
|
check_policies(parameters)
|
|
20
31
|
check_serializer(parameters)
|
|
21
|
-
define_method(method, send("crud_lambda_for_#{method}", klass, parameters))
|
|
32
|
+
define_method(method, send("crud_lambda_for_#{method}", klass, parameters, &block))
|
|
22
33
|
write_metadata(method, parameters)
|
|
23
34
|
end
|
|
24
35
|
|
|
25
36
|
def parameters_with_defaults(parameters)
|
|
26
|
-
defaults = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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) }
|
|
30
42
|
parameters
|
|
31
43
|
end
|
|
32
44
|
|
|
@@ -35,65 +47,68 @@ module SimpleCrudController
|
|
|
35
47
|
@simple_crud_metadata[method] = parameters
|
|
36
48
|
end
|
|
37
49
|
|
|
38
|
-
def
|
|
39
|
-
return unless parameters[:authorize] && parameters[:authenticate]
|
|
40
|
-
|
|
41
|
-
SimpleCrud::Config.authorization_adapter.authorize(controller, record)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def crud_lambda_for_show(klass, parameters = {})
|
|
50
|
+
def crud_lambda_for_show(klass, parameters = {}, &block)
|
|
45
51
|
lambda do
|
|
46
52
|
authenticate_user! if parameters[:authenticate]
|
|
47
|
-
requested =
|
|
53
|
+
requested = SimpleCrudController.find_record(klass, self, parameters)
|
|
48
54
|
|
|
49
55
|
options = {}.merge(serializer: parameters[:serializer]).compact
|
|
50
56
|
SimpleCrudController.maybe_authorize(self, requested, parameters)
|
|
51
|
-
|
|
57
|
+
SimpleCrudController.render_show(self, requested, options, parameters, &block)
|
|
52
58
|
end
|
|
53
59
|
end
|
|
54
60
|
|
|
55
|
-
def
|
|
61
|
+
def crud_lambda_for_new(klass, parameters = {}, &block)
|
|
56
62
|
lambda do
|
|
57
63
|
authenticate_user! if parameters[:authenticate]
|
|
58
|
-
SimpleCrudController.
|
|
59
|
-
|
|
60
|
-
SimpleCrudController.
|
|
64
|
+
record = SimpleCrudController.build_record(self, klass, parameters)
|
|
65
|
+
SimpleCrudController.maybe_authorize(self, record, parameters)
|
|
66
|
+
SimpleCrudController.render_new(self, record, parameters, &block)
|
|
61
67
|
end
|
|
62
68
|
end
|
|
63
69
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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)
|
|
69
76
|
end
|
|
70
77
|
end
|
|
71
78
|
|
|
72
|
-
def crud_lambda_for_create(klass, parameters = {})
|
|
79
|
+
def crud_lambda_for_create(klass, parameters = {}, &block)
|
|
73
80
|
lambda do
|
|
74
81
|
authenticate_user! if parameters[:authenticate]
|
|
75
82
|
permitted_params = send("#{self.class.simple_crud_controller_model.to_s.underscore}_params")
|
|
76
|
-
SimpleCrudController.
|
|
77
|
-
|
|
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)
|
|
78
89
|
end
|
|
79
90
|
end
|
|
80
91
|
|
|
81
|
-
def crud_lambda_for_update(klass, parameters = {})
|
|
92
|
+
def crud_lambda_for_update(klass, parameters = {}, &block)
|
|
82
93
|
lambda do
|
|
83
94
|
authenticate_user! if parameters[:authenticate]
|
|
84
|
-
requested =
|
|
95
|
+
requested = SimpleCrudController.find_record(klass, self, parameters)
|
|
85
96
|
SimpleCrudController.maybe_authorize(self, requested, parameters)
|
|
86
97
|
permitted_params = send("#{self.class.simple_crud_controller_model.to_s.underscore}_params")
|
|
87
|
-
|
|
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)
|
|
88
101
|
end
|
|
89
102
|
end
|
|
90
103
|
|
|
91
|
-
def crud_lambda_for_destroy(klass, parameters = {})
|
|
104
|
+
def crud_lambda_for_destroy(klass, parameters = {}, &block)
|
|
92
105
|
lambda do
|
|
93
106
|
authenticate_user! if parameters[:authenticate]
|
|
94
|
-
requested =
|
|
107
|
+
requested = SimpleCrudController.find_record(klass, self, parameters)
|
|
95
108
|
SimpleCrudController.maybe_authorize(self, requested, parameters)
|
|
96
|
-
|
|
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)
|
|
97
112
|
end
|
|
98
113
|
end
|
|
99
114
|
|
|
@@ -102,7 +117,7 @@ module SimpleCrudController
|
|
|
102
117
|
end
|
|
103
118
|
|
|
104
119
|
def check_valid_method(method)
|
|
105
|
-
|
|
120
|
+
raise ArgumentError, 'invalid method' unless %i[show index create update destroy new].include? method
|
|
106
121
|
end
|
|
107
122
|
|
|
108
123
|
def check_policies(parameters)
|
|
@@ -111,7 +126,7 @@ module SimpleCrudController
|
|
|
111
126
|
model = simple_crud_controller_model
|
|
112
127
|
return if SimpleCrud::Config.authorization_adapter.policy_defined?(model)
|
|
113
128
|
|
|
114
|
-
|
|
129
|
+
raise ArgumentError, "no authorization policy configured for #{model}"
|
|
115
130
|
end
|
|
116
131
|
|
|
117
132
|
def check_serializer(parameters)
|
|
@@ -120,6 +135,6 @@ module SimpleCrudController
|
|
|
120
135
|
serializer_name = parameters[:serializer].to_s
|
|
121
136
|
return if Kernel.const_defined?(serializer_name)
|
|
122
137
|
|
|
123
|
-
|
|
138
|
+
raise ArgumentError, "create a valid serializer with name #{serializer_name}"
|
|
124
139
|
end
|
|
125
140
|
end
|
data/lib/simple_crud/version.rb
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
RSpec.shared_context 'with authenticated user' do
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
before do
|
|
7
|
-
headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
|
|
8
|
-
auth_headers = Devise::JWT::TestHelpers.auth_headers(headers, current_user)
|
|
9
|
-
request.headers.merge!(auth_headers)
|
|
10
|
-
end
|
|
4
|
+
before { authenticate_request }
|
|
11
5
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
shared_examples 'authorization adapter #authorize' do |record_class, denied_error|
|
|
4
|
+
it 'passes for a record the adapter allows' do
|
|
5
|
+
expect { adapter.authorize(controller, record_class.new(1)) }.not_to raise_error
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'raises for a record the adapter denies' do
|
|
9
|
+
expect { adapter.authorize(controller, record_class.new(2)) }.to raise_error(denied_error)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'helpers'
|
|
4
|
-
|
|
5
3
|
shared_examples 'simple crud for create' do
|
|
6
4
|
describe 'POST #create' do
|
|
7
5
|
context 'without authenticated user' do
|
|
8
|
-
subject!(:req)
|
|
6
|
+
subject!(:req) do
|
|
7
|
+
post :create, params: with_route_params(body_params(params_for(model_class))), format: request_format(:create)
|
|
8
|
+
end
|
|
9
9
|
|
|
10
10
|
include_examples 'unauthorized when not logged in' if check_authenticate(:create)
|
|
11
11
|
end
|
|
@@ -16,7 +16,7 @@ shared_examples 'simple crud for create' do
|
|
|
16
16
|
|
|
17
17
|
before do
|
|
18
18
|
make_policies_fail(:create)
|
|
19
|
-
post :create, params: model_params
|
|
19
|
+
post :create, params: with_route_params(body_params(model_params)), format: request_format(:create)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
it 'fails with forbidden' do
|
|
@@ -27,22 +27,54 @@ shared_examples 'simple crud for create' do
|
|
|
27
27
|
|
|
28
28
|
context 'when successfully creating an article' do
|
|
29
29
|
include_context 'with authenticated user' if check_authenticate(:create)
|
|
30
|
-
let(:create_params) { model_params.merge(
|
|
30
|
+
let(:create_params) { model_params.merge(owner_params) }
|
|
31
31
|
|
|
32
32
|
before do
|
|
33
|
-
post :create, params: create_params
|
|
33
|
+
post :create, params: with_route_params(body_params(create_params)), format: request_format(:create)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
if check_html(:create)
|
|
37
|
+
it 'redirects to the created record' do
|
|
38
|
+
expect(response).to have_http_status(:found)
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
it 'response with 201 status code' do
|
|
42
|
+
expect(response).to have_http_status(:created)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'creates an article with valid attributes' do
|
|
46
|
+
expect(model_class_object.last).to have_attributes(create_params)
|
|
47
|
+
end
|
|
38
48
|
end
|
|
39
49
|
|
|
40
|
-
it 'creates
|
|
50
|
+
it 'creates the record' do
|
|
41
51
|
expect(model_class_object.count).to be 1
|
|
42
52
|
end
|
|
53
|
+
end
|
|
43
54
|
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
unless check_raise_on_invalid(:create)
|
|
56
|
+
context 'when creating with invalid attributes' do
|
|
57
|
+
include_context 'with authenticated user' if check_authenticate(:create)
|
|
58
|
+
|
|
59
|
+
before do
|
|
60
|
+
post :create, params: with_route_params(body_params(owner_params)), format: request_format(:create)
|
|
61
|
+
end
|
|
62
|
+
|
|
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
|
|
69
|
+
|
|
70
|
+
it 'returns the validation errors' do
|
|
71
|
+
expect(response_body['errors']).to include(required_error)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'does not create a model' do
|
|
75
|
+
expect(model_class_object.count).to be 0
|
|
76
|
+
end
|
|
77
|
+
end
|
|
46
78
|
end
|
|
47
79
|
end
|
|
48
80
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
shared_examples 'simple crud for create with block' do
|
|
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)))
|
|
7
|
+
|
|
8
|
+
expect(response.parsed_body).to include('saved' => true)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'passes the failed record to the block', :aggregate_failures do
|
|
12
|
+
post :create, params: with_route_params(body_params(owner_params))
|
|
13
|
+
|
|
14
|
+
expect(response).to have_http_status(422)
|
|
15
|
+
expect(response.parsed_body).to include('saved' => false)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
shared_examples 'simple crud for create with build' do
|
|
4
|
+
describe 'POST #create with a build hook' do
|
|
5
|
+
include_context 'with authenticated user'
|
|
6
|
+
|
|
7
|
+
before do
|
|
8
|
+
post :create, params: with_route_params(body_params({ name: 'Built' }))
|
|
9
|
+
end
|
|
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)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
shared_examples 'simple crud for destroy' do
|
|
4
4
|
describe 'DELETE #destroy' do
|
|
5
5
|
context 'without authenticated user' do
|
|
6
|
-
subject!(:req)
|
|
6
|
+
subject!(:req) do
|
|
7
|
+
delete :destroy, params: with_route_params(record_param(:destroy, nil, not_found: true)),
|
|
8
|
+
format: request_format(:destroy)
|
|
9
|
+
end
|
|
7
10
|
|
|
8
11
|
include_examples 'unauthorized when not logged in' if check_authenticate(:destroy)
|
|
9
12
|
end
|
|
@@ -14,7 +17,7 @@ shared_examples 'simple crud for destroy' do
|
|
|
14
17
|
|
|
15
18
|
before do
|
|
16
19
|
make_policies_fail(:destroy)
|
|
17
|
-
delete :destroy, params:
|
|
20
|
+
delete :destroy, params: with_route_params(record_param(:destroy, model)), format: request_format(:destroy)
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
it 'fails with forbidden' do
|
|
@@ -28,11 +31,18 @@ shared_examples 'simple crud for destroy' do
|
|
|
28
31
|
|
|
29
32
|
before do
|
|
30
33
|
model
|
|
31
|
-
delete :destroy, params:
|
|
34
|
+
delete :destroy, params: with_route_params(record_param(:destroy, model)), format: request_format(:destroy)
|
|
32
35
|
end
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
if check_html(:destroy)
|
|
38
|
+
it 'redirects to the collection and destroys the record', :aggregate_failures do
|
|
39
|
+
expect(response).to have_http_status(:found)
|
|
40
|
+
expect(model_class_object.exists?(model.id)).to be false
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
it 'response with 200 status code' do
|
|
44
|
+
expect(response).to have_http_status(:ok)
|
|
45
|
+
end
|
|
36
46
|
end
|
|
37
47
|
end
|
|
38
48
|
|
|
@@ -41,7 +51,8 @@ shared_examples 'simple crud for destroy' do
|
|
|
41
51
|
|
|
42
52
|
before do
|
|
43
53
|
model
|
|
44
|
-
delete :destroy, params:
|
|
54
|
+
delete :destroy, params: with_route_params(record_param(:destroy, nil, not_found: true)),
|
|
55
|
+
format: request_format(:destroy)
|
|
45
56
|
end
|
|
46
57
|
|
|
47
58
|
it 'responds with not found status' do
|
|
@@ -56,10 +67,10 @@ shared_examples 'simple crud for destroy' do
|
|
|
56
67
|
before do
|
|
57
68
|
model
|
|
58
69
|
make_policies_fail(:destroy)
|
|
59
|
-
delete :destroy, params:
|
|
70
|
+
delete :destroy, params: with_route_params(record_param(:destroy, model)), format: request_format(:destroy)
|
|
60
71
|
end
|
|
61
72
|
|
|
62
|
-
it '
|
|
73
|
+
it 'responds with forbidden status' do
|
|
63
74
|
expect(response).to have_http_status(:forbidden)
|
|
64
75
|
end
|
|
65
76
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
shared_examples 'simple crud for destroy with block' do
|
|
4
|
+
describe 'DELETE #destroy with a render block' do
|
|
5
|
+
let!(:record) { model }
|
|
6
|
+
|
|
7
|
+
before { delete :destroy, params: with_route_params({ id: record.id }) }
|
|
8
|
+
|
|
9
|
+
it 'passes the destroyed record to the block' do
|
|
10
|
+
expect(response.parsed_body).to eq('destroyed' => true)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
shared_examples 'simple crud for destroy with finder' do
|
|
4
|
+
describe 'DELETE #destroy with a custom finder' do
|
|
5
|
+
include_context 'with authenticated user'
|
|
6
|
+
|
|
7
|
+
context 'when the model exists' do
|
|
8
|
+
before do
|
|
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)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'destroys the model' do
|
|
18
|
+
expect(model_class_object.exists?(model.id)).to be false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
include_examples 'simple crud not found with finder', :delete, :destroy
|
|
23
|
+
end
|
|
24
|
+
end
|