rails_ops 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -0
- data/Gemfile.lock +1 -1
- data/README.md +40 -0
- data/VERSION +1 -1
- data/lib/rails_ops/mixins/model/authorization.rb +2 -0
- data/lib/rails_ops/mixins/policies.rb +7 -0
- data/lib/rails_ops/operation/model/load.rb +9 -1
- data/lib/rails_ops/operation/model/update.rb +13 -9
- data/lib/rails_ops/operation/model.rb +2 -0
- data/rails_ops.gemspec +3 -3
- data/test/unit/rails_ops/mixins/policies_test.rb +52 -0
- data/test/unit/rails_ops/operation/model/create_test.rb +47 -0
- data/test/unit/rails_ops/operation/model/update_test.rb +48 -0
- data/test/unit/rails_ops/operation/update_lazy_auth_test.rb +13 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 010b1029e9dfdeef808a17c61fb3b5b6f47749b363962234a472d7b59f2830c3
|
4
|
+
data.tar.gz: f8ba45974bd863da77b7c5d27530bd94d6b4a9b04962848d948799d2fa5e37c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7bef9d33ac6d02a9a6634d8a4fcabce53bfad4abc4d589cef63f51e2aceebb751bba4a3e65c7462b7fe670d0bed0b94a334b3d0c9b119c43b26ac62dd2ae90e7
|
7
|
+
data.tar.gz: c382da348b07a9b4ed75d184defcd15bc6d26470158d0ab717ef919de7fd2fa8c4e3443be235be42a01eb63fcf760baf71365c851ce2e02fa50f07dbf52b21fc
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,64 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.7.0 (2025-01-30)
|
4
|
+
|
5
|
+
* Introduce new `:before_attr_assign` policy chain which allows to
|
6
|
+
access the `model` instance before the parameters are assigned.
|
7
|
+
|
8
|
+
* Introduce new `model_includes` DSL method which can be used to eager load
|
9
|
+
associations in Model operations. See the [corresponding section in the README](README.md#including-associated-records)
|
10
|
+
for more information.
|
11
|
+
|
12
|
+
* Fix bug with lazy authorization in `RailsOps::Operation::Model::Update`
|
13
|
+
operation which used a version of the `model` which was missing some
|
14
|
+
attributes.
|
15
|
+
|
16
|
+
* Deprecate lazy authorization in `RailsOps::Operation::Model::Update`
|
17
|
+
operations.
|
18
|
+
|
19
|
+
### Migrating from earlier versions
|
20
|
+
|
21
|
+
* Make sure you use the correct policy chains, depending on the state you
|
22
|
+
need the `model` to be in. If you need the model before the attributes are
|
23
|
+
assigned to the passed-in params, use the `:before_attr_assign` chain.
|
24
|
+
In all other chains, the `model` instance has its attributes assigned to the
|
25
|
+
params you supplied to the operation.
|
26
|
+
|
27
|
+
* If you use `lazy` authorizaion in any of your `Update` operations, you are
|
28
|
+
advised to remove them and replace the lazy authorization by a custom functionality.
|
29
|
+
For example, this is the operation before:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class Operations::User::Update < RailsOps::Operation::Model::Update
|
33
|
+
model User
|
34
|
+
|
35
|
+
model_authorization_action :update, lazy: true
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
and this is the operation afterwards:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
class Operations::User::Update < RailsOps::Operation::Model::Update
|
43
|
+
model User
|
44
|
+
|
45
|
+
# Disable automatically authorizing against the `:update` action
|
46
|
+
model_authorization_action nil
|
47
|
+
|
48
|
+
policy :before_perform do
|
49
|
+
# Using "find_model" to retrieve the model from the database with
|
50
|
+
# the attributes before assigning the params to the model instance.
|
51
|
+
authorize_model! :update, find_model
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
## 1.6.1 (2025-01-24)
|
57
|
+
|
58
|
+
* Fix lazy authorization in `RailsOps::Operation::Model::Update` operation
|
59
|
+
|
60
|
+
Internal reference: `#133962`.
|
61
|
+
|
3
62
|
## 1.6.0 (2025-01-23)
|
4
63
|
|
5
64
|
* Stable release based on previous RC releases
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -454,6 +454,14 @@ an appropriate exception.
|
|
454
454
|
As mentioned above, policies can be executed at various points in your
|
455
455
|
operation's lifecycle. This is possible using *policy chains*:
|
456
456
|
|
457
|
+
- `:before_attr_assign`
|
458
|
+
|
459
|
+
Policies in this chain run before assigning the attributes to the model. This chain is only run
|
460
|
+
in `Model` operations, which at some point call the `assign_attributes` method. This chain is
|
461
|
+
the only chain in which the model is in the state *before* the passed in params are assigned.
|
462
|
+
If you need to run any code which needs the state of the model from the database (e.g. to run
|
463
|
+
custom authentications), this is the correct place.
|
464
|
+
|
457
465
|
- `:on_init`
|
458
466
|
|
459
467
|
Policies in this chain run after the operation class is instantiated.
|
@@ -1318,6 +1326,34 @@ end
|
|
1318
1326
|
As this base class is very minimalistic, it is recommended to fully read and
|
1319
1327
|
comprehend its source code.
|
1320
1328
|
|
1329
|
+
### Including associated records
|
1330
|
+
|
1331
|
+
Normaly, when inheriting from `RailsOps::Operation::Model::Load` (as well as from the
|
1332
|
+
`Update` and the `Destroy` operations respectively), RailsOps only loads the instance
|
1333
|
+
of the model specified by the `id` parameter. In some cases, you'd want to eagerly load
|
1334
|
+
associations of the model, e.g. when you need to access associated records.
|
1335
|
+
|
1336
|
+
For this, RailsOps provides the `model_includes` DSL method, with which you can
|
1337
|
+
pass-in associations to eager load (the value will simply be passed on to an `includes`
|
1338
|
+
call). See the following code snipped for an example:
|
1339
|
+
|
1340
|
+
```ruby
|
1341
|
+
class Operations::User::Load < RailsOps::Operation::Model::Load
|
1342
|
+
schema3 do
|
1343
|
+
int! :id, cast_str: true
|
1344
|
+
end
|
1345
|
+
|
1346
|
+
model ::User
|
1347
|
+
|
1348
|
+
# This will result in RailsOps eagerly loading the `posts`
|
1349
|
+
# association, as well as the comments and authors of the
|
1350
|
+
# comments.
|
1351
|
+
# The call that RailsOps will create is:
|
1352
|
+
# User.includes(posts: { comments: :author }).find_by(id: params[:id])
|
1353
|
+
model_includes posts: { comments: :author }
|
1354
|
+
end
|
1355
|
+
```
|
1356
|
+
|
1321
1357
|
### Parameter extraction for create and update
|
1322
1358
|
|
1323
1359
|
As mentioned before, the `Create` and `Update` base classes provide an
|
@@ -1392,6 +1428,10 @@ sensible default. See the respective class' source code for details.
|
|
1392
1428
|
|
1393
1429
|
#### Lazy model update authorization
|
1394
1430
|
|
1431
|
+
*Please note that using lazy model update authorization is deprecated any may
|
1432
|
+
be removed in a future release. See the changelog for instructions on how to
|
1433
|
+
adapt your application.*
|
1434
|
+
|
1395
1435
|
In case of operations inheriting from `RailsOps::Operation::Model::Update`, you
|
1396
1436
|
can specify the `model_authorization_action` to be `lazy`, meaning that it will
|
1397
1437
|
only be checked when *performing* the operation, but not on initialization. This
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.7.0
|
@@ -12,6 +12,8 @@ module RailsOps::Mixins::Model::Authorization
|
|
12
12
|
module ClassMethods
|
13
13
|
# Gets or sets the action verb used for authorizing models.
|
14
14
|
def model_authorization_action(*action, lazy: false)
|
15
|
+
RailsOps.deprecator.warn('Using `lazy` model authorization is deprecated and will be removed in a future version.') if lazy
|
16
|
+
|
15
17
|
if action.size == 1
|
16
18
|
self._model_authorization_action = action.first
|
17
19
|
self._model_authorization_lazy = lazy
|
@@ -6,6 +6,7 @@ module RailsOps::Mixins::Policies
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
POLICY_CHAIN_KEYS = %i[
|
9
|
+
before_attr_assign
|
9
10
|
on_init
|
10
11
|
before_perform
|
11
12
|
after_perform
|
@@ -26,6 +27,12 @@ module RailsOps::Mixins::Policies
|
|
26
27
|
fail "Unknown policy chain #{chain.inspect}, available are #{POLICY_CHAIN_KEYS.inspect}."
|
27
28
|
end
|
28
29
|
|
30
|
+
# The `before_attr_assign` chain is only allowed if the operation is a model
|
31
|
+
# operation, i.e. it needs to implement the `build_model` method.
|
32
|
+
if chain == :before_attr_assign && !method_defined?(:assign_attributes)
|
33
|
+
fail 'Policy :before_attr_assign may not be used unless your operation defines the `assign_attributes` method!'
|
34
|
+
end
|
35
|
+
|
29
36
|
self._policy_chains = _policy_chains.dup
|
30
37
|
if prepend_action
|
31
38
|
_policy_chains[chain] = [block] + _policy_chains[chain]
|
@@ -2,6 +2,7 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
|
|
2
2
|
class_attribute :_lock_model_at_build
|
3
3
|
class_attribute :_load_model_authorization_action
|
4
4
|
class_attribute :_lock_mode
|
5
|
+
class_attribute :_model_includes
|
5
6
|
|
6
7
|
policy :on_init do
|
7
8
|
model
|
@@ -64,6 +65,10 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
|
|
64
65
|
:id
|
65
66
|
end
|
66
67
|
|
68
|
+
def self.model_includes(includes)
|
69
|
+
self._model_includes = includes
|
70
|
+
end
|
71
|
+
|
67
72
|
def find_model
|
68
73
|
unless params[model_id_field]
|
69
74
|
fail "Param #{model_id_field.inspect} must be given."
|
@@ -75,6 +80,9 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
|
|
75
80
|
# Express intention to lock if required
|
76
81
|
relation = lock_relation(relation)
|
77
82
|
|
83
|
+
# Apply includes if given in the operation
|
84
|
+
relation = relation.includes(self.class._model_includes) if self.class._model_includes.present?
|
85
|
+
|
78
86
|
# Fetch (and possibly lock) model
|
79
87
|
model = relation.find_by!(model_id_field => params[model_id_field])
|
80
88
|
|
@@ -116,7 +124,7 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model
|
|
116
124
|
adapter_type = ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
117
125
|
|
118
126
|
case adapter_type
|
119
|
-
when :mysql, :mysql2, :oracleenhanced
|
127
|
+
when :mysql, :mysql2, :oracleenhanced, :trilogy
|
120
128
|
return 'LOCK IN SHARE MODE'
|
121
129
|
when :postgresql
|
122
130
|
return 'FOR SHARE'
|
@@ -19,8 +19,17 @@ class RailsOps::Operation::Model::Update < RailsOps::Operation::Model::Load
|
|
19
19
|
|
20
20
|
policy :before_perform do
|
21
21
|
# If the authorization is configured to be lazy, we need to call the authorization
|
22
|
-
# on
|
23
|
-
|
22
|
+
# on a fresh copy of the model, before assigning the attributes. We simply use the `find_model`
|
23
|
+
# method from our parent class and then run the authorization on this instance.
|
24
|
+
if self.class._model_authorization_lazy
|
25
|
+
model_from_database = find_model
|
26
|
+
|
27
|
+
if model_from_database.respond_to?(:parent_op=)
|
28
|
+
model_from_database.parent_op = self
|
29
|
+
end
|
30
|
+
|
31
|
+
authorize_model! model_authorization_action, model_from_database
|
32
|
+
end
|
24
33
|
end
|
25
34
|
|
26
35
|
def model_authorization
|
@@ -37,13 +46,8 @@ class RailsOps::Operation::Model::Update < RailsOps::Operation::Model::Load
|
|
37
46
|
build_nested_model_ops :update
|
38
47
|
|
39
48
|
# Perform update authorization BEFORE assigning attributes. If the authorization is lazy,
|
40
|
-
# we
|
41
|
-
|
42
|
-
if self.class._model_authorization_lazy
|
43
|
-
@model_before_assigning_attributes = @model.dup
|
44
|
-
else
|
45
|
-
model_authorization
|
46
|
-
end
|
49
|
+
# we'll call the authorization later on in the `before_perform` block.
|
50
|
+
model_authorization unless self.class._model_authorization_lazy
|
47
51
|
|
48
52
|
# Assign attributes
|
49
53
|
assign_attributes
|
@@ -131,6 +131,8 @@ class RailsOps::Operation::Model < RailsOps::Operation
|
|
131
131
|
# are meant for nested models (registered via `nested_model_op`) will not
|
132
132
|
# be assigned. You can turn this filtering off by passing `false`.
|
133
133
|
def assign_attributes(attributes = nil, model: nil, without_protection: false, without_nested_models: true)
|
134
|
+
run_policies :before_attr_assign
|
135
|
+
|
134
136
|
model ||= self.model
|
135
137
|
|
136
138
|
attributes ||= extract_attributes_from_params(model)
|
data/rails_ops.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: rails_ops 1.
|
2
|
+
# stub: rails_ops 1.7.0 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "rails_ops".freeze
|
6
|
-
s.version = "1.
|
6
|
+
s.version = "1.7.0"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib".freeze]
|
10
10
|
s.authors = ["Sitrox".freeze]
|
11
|
-
s.date = "2025-01-
|
11
|
+
s.date = "2025-01-30"
|
12
12
|
s.files = [".github/workflows/rubocop.yml".freeze, ".github/workflows/ruby.yml".freeze, ".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, "Appraisals".freeze, "CHANGELOG.md".freeze, "Gemfile".freeze, "Gemfile.lock".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "VERSION".freeze, "gemfiles/rails_6.0.gemfile".freeze, "gemfiles/rails_6.1.gemfile".freeze, "gemfiles/rails_7.0.gemfile".freeze, "gemfiles/rails_7.1.gemfile".freeze, "gemfiles/rails_7.2.gemfile".freeze, "gemfiles/rails_8.0.gemfile".freeze, "lib/generators/operation/USAGE".freeze, "lib/generators/operation/operation_generator.rb".freeze, "lib/generators/operation/templates/controller.erb".freeze, "lib/generators/operation/templates/controller_wrapper.erb".freeze, "lib/generators/operation/templates/create.erb".freeze, "lib/generators/operation/templates/destroy.erb".freeze, "lib/generators/operation/templates/load.erb".freeze, "lib/generators/operation/templates/update.erb".freeze, "lib/generators/operation/templates/view.erb".freeze, "lib/rails_ops.rb".freeze, "lib/rails_ops/authorization_backend/abstract.rb".freeze, "lib/rails_ops/authorization_backend/can_can_can.rb".freeze, "lib/rails_ops/configuration.rb".freeze, "lib/rails_ops/context.rb".freeze, "lib/rails_ops/controller_mixin.rb".freeze, "lib/rails_ops/exceptions.rb".freeze, "lib/rails_ops/hooked_job.rb".freeze, "lib/rails_ops/hookup.rb".freeze, "lib/rails_ops/hookup/dsl.rb".freeze, "lib/rails_ops/hookup/dsl_validator.rb".freeze, "lib/rails_ops/hookup/hook.rb".freeze, "lib/rails_ops/log_subscriber.rb".freeze, "lib/rails_ops/mixins.rb".freeze, "lib/rails_ops/mixins/authorization.rb".freeze, "lib/rails_ops/mixins/log_settings.rb".freeze, "lib/rails_ops/mixins/model.rb".freeze, "lib/rails_ops/mixins/model/authorization.rb".freeze, "lib/rails_ops/mixins/model/nesting.rb".freeze, "lib/rails_ops/mixins/param_authorization.rb".freeze, "lib/rails_ops/mixins/policies.rb".freeze, "lib/rails_ops/mixins/require_context.rb".freeze, "lib/rails_ops/mixins/routes.rb".freeze, "lib/rails_ops/mixins/schema_validation.rb".freeze, "lib/rails_ops/mixins/sub_ops.rb".freeze, "lib/rails_ops/model_mixins.rb".freeze, "lib/rails_ops/model_mixins/ar_extension.rb".freeze, "lib/rails_ops/model_mixins/marshalling.rb".freeze, "lib/rails_ops/model_mixins/parent_op.rb".freeze, "lib/rails_ops/model_mixins/sti_fixes.rb".freeze, "lib/rails_ops/model_mixins/virtual_attributes.rb".freeze, "lib/rails_ops/model_mixins/virtual_attributes/virtual_column_wrapper.rb".freeze, "lib/rails_ops/model_mixins/virtual_has_one.rb".freeze, "lib/rails_ops/model_mixins/virtual_model_name.rb".freeze, "lib/rails_ops/operation.rb".freeze, "lib/rails_ops/operation/model.rb".freeze, "lib/rails_ops/operation/model/create.rb".freeze, "lib/rails_ops/operation/model/destroy.rb".freeze, "lib/rails_ops/operation/model/load.rb".freeze, "lib/rails_ops/operation/model/update.rb".freeze, "lib/rails_ops/profiler.rb".freeze, "lib/rails_ops/profiler/node.rb".freeze, "lib/rails_ops/railtie.rb".freeze, "lib/rails_ops/scoped_env.rb".freeze, "lib/rails_ops/virtual_model.rb".freeze, "rails_ops.gemspec".freeze, "test/db/models.rb".freeze, "test/db/schema.rb".freeze, "test/dummy/Rakefile".freeze, "test/dummy/app/assets/config/manifest.js".freeze, "test/dummy/app/assets/images/.keep".freeze, "test/dummy/app/assets/javascripts/application.js".freeze, "test/dummy/app/assets/javascripts/cable.js".freeze, "test/dummy/app/assets/javascripts/channels/.keep".freeze, "test/dummy/app/assets/stylesheets/application.css".freeze, "test/dummy/app/channels/application_cable/channel.rb".freeze, "test/dummy/app/channels/application_cable/connection.rb".freeze, "test/dummy/app/controllers/application_controller.rb".freeze, "test/dummy/app/controllers/concerns/.keep".freeze, "test/dummy/app/controllers/group_controller.rb".freeze, "test/dummy/app/helpers/application_helper.rb".freeze, "test/dummy/app/jobs/application_job.rb".freeze, "test/dummy/app/mailers/application_mailer.rb".freeze, "test/dummy/app/models/ability.rb".freeze, "test/dummy/app/models/animal.rb".freeze, "test/dummy/app/models/application_record.rb".freeze, "test/dummy/app/models/bird.rb".freeze, "test/dummy/app/models/cat.rb".freeze, "test/dummy/app/models/computer.rb".freeze, "test/dummy/app/models/concerns/.keep".freeze, "test/dummy/app/models/cpu.rb".freeze, "test/dummy/app/models/dog.rb".freeze, "test/dummy/app/models/flower.rb".freeze, "test/dummy/app/models/group.rb".freeze, "test/dummy/app/models/mainboard.rb".freeze, "test/dummy/app/models/nightingale.rb".freeze, "test/dummy/app/models/phoenix.rb".freeze, "test/dummy/app/models/user.rb".freeze, "test/dummy/app/views/layouts/application.html.erb".freeze, "test/dummy/app/views/layouts/mailer.html.erb".freeze, "test/dummy/app/views/layouts/mailer.text.erb".freeze, "test/dummy/bin/bundle".freeze, "test/dummy/bin/rails".freeze, "test/dummy/bin/rake".freeze, "test/dummy/bin/setup".freeze, "test/dummy/bin/update".freeze, "test/dummy/bin/yarn".freeze, "test/dummy/config.ru".freeze, "test/dummy/config/application.rb".freeze, "test/dummy/config/boot.rb".freeze, "test/dummy/config/cable.yml".freeze, "test/dummy/config/database.yml".freeze, "test/dummy/config/environment.rb".freeze, "test/dummy/config/environments/development.rb".freeze, "test/dummy/config/environments/production.rb".freeze, "test/dummy/config/environments/test.rb".freeze, "test/dummy/config/hookup.rb".freeze, "test/dummy/config/initializers/application_controller_renderer.rb".freeze, "test/dummy/config/initializers/assets.rb".freeze, "test/dummy/config/initializers/backtrace_silencers.rb".freeze, "test/dummy/config/initializers/cookies_serializer.rb".freeze, "test/dummy/config/initializers/filter_parameter_logging.rb".freeze, "test/dummy/config/initializers/inflections.rb".freeze, "test/dummy/config/initializers/mime_types.rb".freeze, "test/dummy/config/initializers/rails_ops.rb".freeze, "test/dummy/config/initializers/wrap_parameters.rb".freeze, "test/dummy/config/locales/en.yml".freeze, "test/dummy/config/puma.rb".freeze, "test/dummy/config/routes.rb".freeze, "test/dummy/config/secrets.yml".freeze, "test/dummy/config/spring.rb".freeze, "test/dummy/db/schema.rb".freeze, "test/dummy/lib/assets/.keep".freeze, "test/dummy/log/.keep".freeze, "test/dummy/package.json".freeze, "test/dummy/public/404.html".freeze, "test/dummy/public/422.html".freeze, "test/dummy/public/500.html".freeze, "test/dummy/public/apple-touch-icon-precomposed.png".freeze, "test/dummy/public/apple-touch-icon.png".freeze, "test/dummy/public/favicon.ico".freeze, "test/dummy/tmp/.keep".freeze, "test/test_helper.rb".freeze, "test/unit/rails_ops/generators/operation_generator_test.rb".freeze, "test/unit/rails_ops/hookup_test.rb".freeze, "test/unit/rails_ops/mixins/controller_test.rb".freeze, "test/unit/rails_ops/mixins/model/deep_nesting_test.rb".freeze, "test/unit/rails_ops/mixins/model/marshalling_test.rb".freeze, "test/unit/rails_ops/mixins/param_authorization_test.rb".freeze, "test/unit/rails_ops/mixins/policies_test.rb".freeze, "test/unit/rails_ops/operation/auth_test.rb".freeze, "test/unit/rails_ops/operation/model/create_test.rb".freeze, "test/unit/rails_ops/operation/model/destroy_test.rb".freeze, "test/unit/rails_ops/operation/model/load_test.rb".freeze, "test/unit/rails_ops/operation/model/sti_test.rb".freeze, "test/unit/rails_ops/operation/model/update_test.rb".freeze, "test/unit/rails_ops/operation/model_test.rb".freeze, "test/unit/rails_ops/operation/update_lazy_auth_test.rb".freeze, "test/unit/rails_ops/operation_test.rb".freeze, "test/unit/rails_ops/profiler_test.rb".freeze]
|
13
13
|
s.licenses = ["MIT".freeze]
|
14
14
|
s.rubygems_version = "3.4.6".freeze
|
@@ -33,6 +33,58 @@ class RailsOps::Mixins::PoliciesTest < ActiveSupport::TestCase
|
|
33
33
|
op.run!.sequence
|
34
34
|
end
|
35
35
|
|
36
|
+
def test_basic_policies_with_model
|
37
|
+
group = Group.create!
|
38
|
+
|
39
|
+
op = Class.new(RailsOps::Operation::Model::Update) do
|
40
|
+
attr_reader :sequence
|
41
|
+
|
42
|
+
model Group
|
43
|
+
|
44
|
+
policy do
|
45
|
+
@sequence << :default
|
46
|
+
end
|
47
|
+
|
48
|
+
policy :before_attr_assign do
|
49
|
+
@sequence = []
|
50
|
+
@sequence << :before_attr_assign
|
51
|
+
end
|
52
|
+
|
53
|
+
policy :on_init do
|
54
|
+
@sequence << :on_init
|
55
|
+
end
|
56
|
+
|
57
|
+
policy :before_perform do
|
58
|
+
@sequence << :before_perform
|
59
|
+
end
|
60
|
+
|
61
|
+
policy :after_perform do
|
62
|
+
@sequence << :after_perform
|
63
|
+
end
|
64
|
+
|
65
|
+
def perform
|
66
|
+
@sequence << :perform
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
assert_equal %i[before_attr_assign on_init default before_perform perform after_perform],
|
71
|
+
op.run!(id: group.id).sequence
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_before_attr_assign_needs_build_model
|
75
|
+
# When trying to use the `:before_attr_assign` chain, we need
|
76
|
+
# to have the `assign_attributes` method implemented, which usually
|
77
|
+
# is implemented in the `RailsOps::Operation::Model` base class
|
78
|
+
# and runs the `before_attr_assign` policy chain.
|
79
|
+
assert_raises RuntimeError, match: /Policy :before_attr_assign may not be used unless your operation defines the `assign_attributes` method!/ do
|
80
|
+
Class.new(RailsOps::Operation) do
|
81
|
+
policy :before_attr_assign do
|
82
|
+
# Nothing needed here
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
36
88
|
def test_prepend_action
|
37
89
|
op = Class.new(RailsOps::Operation) do
|
38
90
|
attr_reader :sequence
|
@@ -65,4 +65,51 @@ class RailsOps::Operation::Model::CreateTest < ActiveSupport::TestCase
|
|
65
65
|
op.build_model
|
66
66
|
end
|
67
67
|
end
|
68
|
+
|
69
|
+
def test_policies
|
70
|
+
op_klass = Class.new(RailsOps::Operation::Model::Create) do
|
71
|
+
model ::Group
|
72
|
+
|
73
|
+
policy do
|
74
|
+
# Here, we need the model to have the new name assigned
|
75
|
+
fail 'Attribute should be assigned to new value' unless model.name == 'new_name'
|
76
|
+
|
77
|
+
# However, the model should not be persisted yet
|
78
|
+
fail 'Model should not be persisted to the database yet' if model.persisted?
|
79
|
+
end
|
80
|
+
|
81
|
+
policy :before_attr_assign do
|
82
|
+
# The name of the model itself should still be nil
|
83
|
+
fail 'Attribute should not be assigned to a value yet' if model.name.present?
|
84
|
+
end
|
85
|
+
|
86
|
+
policy :on_init do
|
87
|
+
# Here, we need the model to have the new name assigned
|
88
|
+
fail 'Attribute should be assigned to new value' unless model.name == 'new_name'
|
89
|
+
|
90
|
+
# However, the model should not be persisted yet
|
91
|
+
fail 'Model should not be persisted to the database yet' if model.persisted?
|
92
|
+
end
|
93
|
+
|
94
|
+
policy :before_perform do
|
95
|
+
# Here, we need the model to have the new name assigned
|
96
|
+
fail 'Attribute should be assigned to new value' unless model.name == 'new_name'
|
97
|
+
|
98
|
+
# However, the model should not be persisted yet
|
99
|
+
fail 'Model should not be persisted to the database yet' if model.persisted?
|
100
|
+
end
|
101
|
+
|
102
|
+
policy :after_perform do
|
103
|
+
# Here, we need the model to have the new name assigned
|
104
|
+
fail 'Attribute should be assigned to new value' unless model.name == 'new_name'
|
105
|
+
|
106
|
+
# Now, the model should be persisted to the database
|
107
|
+
fail 'Model should not be persisted to the database yet' unless model.persisted?
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
assert_nothing_raised do
|
112
|
+
op_klass.run!(group: { name: 'new_name' })
|
113
|
+
end
|
114
|
+
end
|
68
115
|
end
|
@@ -163,4 +163,52 @@ class RailsOps::Operation::Model::UpdateTest < ActiveSupport::TestCase
|
|
163
163
|
ensure
|
164
164
|
RailsOps.config.authorization_backend = nil
|
165
165
|
end
|
166
|
+
|
167
|
+
def test_policies
|
168
|
+
op_klass = Class.new(RailsOps::Operation::Model::Update) do
|
169
|
+
model ::Group
|
170
|
+
|
171
|
+
policy do
|
172
|
+
# Here, we need the model to have the new name assigned
|
173
|
+
fail 'Attribute should be assigned to new value' unless model.name == 'new_name'
|
174
|
+
|
175
|
+
# However, the new name should not be persisted to the database yet
|
176
|
+
fail 'Attribute change should not be persisted yet' unless Group.find(model.id).name == 'foobar'
|
177
|
+
end
|
178
|
+
|
179
|
+
policy :before_attr_assign do
|
180
|
+
# The name of the model itself should still be the initial value
|
181
|
+
fail 'Attribute should not be assigned to new value yet' unless model.name == 'foobar'
|
182
|
+
end
|
183
|
+
|
184
|
+
policy :on_init do
|
185
|
+
# Here, we need the model to have the new name assigned
|
186
|
+
fail 'Attribute should be assigned to new value' unless model.name == 'new_name'
|
187
|
+
|
188
|
+
# However, the new name should not be persisted to the database yet
|
189
|
+
fail 'Attribute change should not be persisted yet' unless Group.find(model.id).name == 'foobar'
|
190
|
+
end
|
191
|
+
|
192
|
+
policy :before_perform do
|
193
|
+
# Here, we need the model to have the new name assigned
|
194
|
+
fail 'Attribute should be assigned to new value' unless model.name == 'new_name'
|
195
|
+
|
196
|
+
# However, the new name should not be persisted to the database yet
|
197
|
+
fail 'Attribute change should not be persisted yet' unless Group.find(model.id).name == 'foobar'
|
198
|
+
end
|
199
|
+
|
200
|
+
policy :after_perform do
|
201
|
+
# Here, we need the model to have the new name assigned
|
202
|
+
fail 'Attribute should be assigned to new value' unless model.name == 'new_name'
|
203
|
+
|
204
|
+
# Also, the new name should be persisted to the database
|
205
|
+
fail 'Attribute change should not be persisted yet' unless Group.find(model.id).name == 'new_name'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
model = Group.create!(name: 'foobar')
|
210
|
+
assert_nothing_raised do
|
211
|
+
op_klass.run!(id: model.id, group: { name: 'new_name' })
|
212
|
+
end
|
213
|
+
end
|
166
214
|
end
|
@@ -89,9 +89,21 @@ class RailsOps::Operation::UpdateLazyAuthTest < ActiveSupport::TestCase
|
|
89
89
|
|
90
90
|
def test_permitted_update_unpermitted_color
|
91
91
|
ctx = RailsOps::Context.new(ability: ABILITY.new(read: true, update: true))
|
92
|
-
op = BASIC_OP.new(ctx, id: 3
|
92
|
+
op = BASIC_OP.new(ctx, id: 3)
|
93
93
|
assert_raises CanCan::AccessDenied do
|
94
94
|
op.run!
|
95
95
|
end
|
96
96
|
end
|
97
|
+
|
98
|
+
def test_permitted_update_permitted_color_other_color_target_state
|
99
|
+
# Here, we test that we can update a record where the ability
|
100
|
+
# allows us the `:update` action on the current state of the model,
|
101
|
+
# despite bringing the object to a state where we won't have the
|
102
|
+
# ability to update the object anymore afterwards.
|
103
|
+
ctx = RailsOps::Context.new(ability: ABILITY.new(read: true, update: true))
|
104
|
+
op = BASIC_OP.new(ctx, id: 1, group: { color: 'blue' })
|
105
|
+
assert_nothing_raised do
|
106
|
+
op.run!
|
107
|
+
end
|
108
|
+
end
|
97
109
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_ops
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sitrox
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_type
|