operations 0.7.0 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +4 -4
- data/CHANGELOG.md +20 -3
- data/README.md +42 -11
- data/UPGRADING_FORMS.md +101 -0
- data/lib/operations/command.rb +7 -1
- data/lib/operations/form/attribute.rb +16 -20
- data/lib/operations/form/base.rb +14 -6
- data/lib/operations/form/builder.rb +4 -3
- data/lib/operations/form.rb +23 -5
- data/lib/operations/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: affb68a66ecbc1ee0e687ab13efb202037d10054adb114e684e775224957b65e
|
4
|
+
data.tar.gz: 255180d0be3704a73c0bf6e148658df8b662e33a4508dff3ae3fee819e73caba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f639386ccb6d003909737a4606b44f21cf49dc90b36fe0c854ea1e8c824049b2de7a70c480b44b2f4713558f21417c755361b6fccad4ed2f2af1068bc7cd135
|
7
|
+
data.tar.gz: a24d378627511c1198728a9aebe38c7f39cb057756e957906df29bf059804eef94d1188146b845842be42d94c559ebccd84b3d4d6a613b95a2a31d427771fe09
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2024-
|
3
|
+
# on 2024-08-07 02:24:58 UTC using RuboCop version 1.65.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -22,14 +22,14 @@ Metrics/AbcSize:
|
|
22
22
|
# Offense count: 1
|
23
23
|
# Configuration parameters: CountComments, CountAsOne.
|
24
24
|
Metrics/ClassLength:
|
25
|
-
Max:
|
25
|
+
Max: 149
|
26
26
|
|
27
27
|
# Offense count: 1
|
28
28
|
# Configuration parameters: CountComments, CountAsOne.
|
29
29
|
Metrics/ModuleLength:
|
30
|
-
Max:
|
30
|
+
Max: 141
|
31
31
|
|
32
|
-
# Offense count:
|
32
|
+
# Offense count: 3
|
33
33
|
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
34
34
|
Metrics/ParameterLists:
|
35
35
|
Max: 7
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,27 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [0.7.
|
3
|
+
## [0.7.2](https://github.com/BookingSync/operations/tree/v0.7.2)
|
4
4
|
|
5
5
|
### Added
|
6
6
|
|
7
|
-
-
|
7
|
+
- Allow referencing and arbitrary model attrbiute from form object attribute with `model_name: "Post#title"` [\#50](https://github.com/BookingSync/operations/pull/50) ([pyromaniac](https://github.com/pyromaniac))
|
8
|
+
- Allow passing multiple `hydrators:` to Operations::Form [\#49](https://github.com/BookingSync/operations/pull/49) ([pyromaniac](https://github.com/pyromaniac))
|
9
|
+
|
10
|
+
### Improvements
|
11
|
+
|
12
|
+
- Change default form hydration behavior - it is now deep merging params after hydration, so no need to do it in the hydrator. Controlled with `hydration_merge_params:` option. [\#49](https://github.com/BookingSync/operations/pull/49) ([pyromaniac](https://github.com/pyromaniac))
|
13
|
+
|
14
|
+
## [0.7.1](https://github.com/BookingSync/operations/tree/v0.7.1)
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Added `persisted:` option to the new forms definition. [\#48](https://github.com/BookingSync/operations/pull/48) ([pyromaniac](https://github.com/pyromaniac))
|
19
|
+
|
20
|
+
## [0.7.0](https://github.com/BookingSync/operations/tree/v0.7.0)
|
21
|
+
|
22
|
+
### Added
|
23
|
+
|
24
|
+
- Implement new forms system detaching it from operations. Please check [UPGRADING_FORMS.md](UPGRADING_FORMS.md) for more details. [\#47](https://github.com/BookingSync/operations/pull/47) ([pyromaniac](https://github.com/pyromaniac))
|
8
25
|
|
9
26
|
### Improvements
|
10
27
|
|
@@ -15,7 +32,7 @@
|
|
15
32
|
|
16
33
|
- In some cases, `Operation::Command#form_class` was evaluated before `form_base` was evaluated [\#41](https://github.com/BookingSync/operations/pull/41) ([pyromaniac](https://github.com/pyromaniac))
|
17
34
|
|
18
|
-
## [0.6.
|
35
|
+
## [0.6.3](https://github.com/BookingSync/operations/tree/v0.6.3)
|
19
36
|
|
20
37
|
### Added
|
21
38
|
|
data/README.md
CHANGED
@@ -312,7 +312,7 @@ class ActiveRecordRepository
|
|
312
312
|
include Dry::Monads[:result]
|
313
313
|
extend Dry::Initializer
|
314
314
|
|
315
|
-
param :model, type: Types
|
315
|
+
param :model, type: Types::Class.constrained(lt: ActiveRecord::Base)
|
316
316
|
|
317
317
|
def create(**attributes)
|
318
318
|
record = model.new(**attributes)
|
@@ -853,6 +853,8 @@ In this case, `Order::MarkAsCompleted.system.call(...)` will be used in, say, co
|
|
853
853
|
|
854
854
|
### Form objects
|
855
855
|
|
856
|
+
Form objects were refactored to be separate from Command. Please check [UPGRADING_FORMS.md](UPGRADING_FORMS.md) for more details.
|
857
|
+
|
856
858
|
While we normally recommend using frontend-backend separation, it is still possible to use this framework with Rails view helpers:
|
857
859
|
|
858
860
|
```ruby
|
@@ -885,7 +887,7 @@ class Post::Update
|
|
885
887
|
end
|
886
888
|
```
|
887
889
|
|
888
|
-
Then, the form can be used as any other form object:
|
890
|
+
Then, the form can be used as any other form object. Unfortunately, there is no way to figure out the correct route for the operation form object, so it have to be provided manually:
|
889
891
|
|
890
892
|
```erb
|
891
893
|
# views/posts/edit.html.erb
|
@@ -902,7 +904,7 @@ class Post::Update
|
|
902
904
|
def self.default_form
|
903
905
|
@default_form ||= Operations::Form.new(
|
904
906
|
default,
|
905
|
-
|
907
|
+
hydrators: [Post::Update::Hydrator.new]
|
906
908
|
)
|
907
909
|
end
|
908
910
|
end
|
@@ -910,9 +912,7 @@ end
|
|
910
912
|
class Post::Update::Hydrator
|
911
913
|
def call(form_class, params, post:, **_context)
|
912
914
|
value_attributes = form_class.attributes.keys - %i[post_id]
|
913
|
-
|
914
|
-
|
915
|
-
data.merge!(params)
|
915
|
+
value_attributes.index_with { |name| post.public_send(name) }
|
916
916
|
end
|
917
917
|
end
|
918
918
|
```
|
@@ -927,19 +927,36 @@ class Post::Update
|
|
927
927
|
@default_form ||= Operations::Form.new(
|
928
928
|
default,
|
929
929
|
model_map: Post::Update::ModelMap.new,
|
930
|
-
|
930
|
+
hydrators: [Post::Update::Hydrator.new]
|
931
931
|
)
|
932
932
|
end
|
933
933
|
end
|
934
934
|
|
935
935
|
class Post::Update::ModelMap
|
936
|
-
|
937
|
-
Post
|
936
|
+
MAPPING = {
|
937
|
+
%w[published_at] => Post, # a model can be passed but beware of circular dependencies, better use strings
|
938
|
+
%w[title] => "Post", # or a model name - safer option
|
939
|
+
%w[content] => "Post#body" # referencing different attribute is possible, useful for naming migration or translations
|
940
|
+
}.freeze
|
941
|
+
|
942
|
+
def call(path)
|
943
|
+
MAPPING[path] # returns the mapping for a single path
|
938
944
|
end
|
939
945
|
end
|
940
946
|
```
|
941
947
|
|
942
|
-
In forms, params input is already transformed to extract the nested data with the form name. `form_for @post_update_form` will generate the form that send params nested under the `params[:post_update_form]` key. By default operation forms extract this form data and send it to the operation at the top level, so `{ id: 42, post_update_form: { title: "Post Title" } }` params will be sent to the operation as `{ id: 42, title: "Post Title" }`. Strong params are also accepted by the form, though they are being converted with `to_unsafe_hash`.
|
948
|
+
In forms, params input is already transformed to extract the nested data with the form name. `form_for @post_update_form` will generate the form that send params nested under the `params[:post_update_form]` key. By default operation forms extract this form data and send it to the operation at the top level, so `{ id: 42, post_update_form: { title: "Post Title" } }` params will be sent to the operation as `{ id: 42, title: "Post Title" }`. Strong params are also accepted by the form, though they are being converted with `to_unsafe_hash`. Though the form name can be customized if necessary:
|
949
|
+
|
950
|
+
```ruby
|
951
|
+
class Post::Update
|
952
|
+
def self.default_form
|
953
|
+
@default_form ||= Operations::Form.new(
|
954
|
+
default,
|
955
|
+
model_name: "custom_post_update_form", # form name can be customized
|
956
|
+
)
|
957
|
+
end
|
958
|
+
end
|
959
|
+
```
|
943
960
|
|
944
961
|
It is possible to add more params transfomations to the form in cases when operation contract is different from the params structure:
|
945
962
|
|
@@ -948,7 +965,6 @@ class Post::Update
|
|
948
965
|
def self.default_form
|
949
966
|
@default_form ||= Operations::Form.new(
|
950
967
|
default,
|
951
|
-
model_name: "post_update_form", # form name can be customized
|
952
968
|
params_transformations: [
|
953
969
|
ParamsMap.new(id: :post_id),
|
954
970
|
NestedAttributes.new(:sections)
|
@@ -994,6 +1010,21 @@ class NestedAttributes
|
|
994
1010
|
end
|
995
1011
|
```
|
996
1012
|
|
1013
|
+
By default, the top-level form objects instantiated from the form will have `persisted?` flag set to `true`. This will result the form to use the `PATCH` werb like for any persisted AR object. If it is required to generate a form with the `POST` verb in case of operation, say, creating some objects, this default behavior can be customised:
|
1014
|
+
|
1015
|
+
```ruby
|
1016
|
+
class Post::Create
|
1017
|
+
def self.default_form
|
1018
|
+
@default_form ||= Operations::Form.new(
|
1019
|
+
default,
|
1020
|
+
persisted: false
|
1021
|
+
)
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
```
|
1025
|
+
|
1026
|
+
Note that operation itself is agnostic to the persistence layer, so there is no way for it to figure out this semanticsa automatically.
|
1027
|
+
|
997
1028
|
## Development
|
998
1029
|
|
999
1030
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/UPGRADING_FORMS.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# New form objects
|
2
|
+
|
3
|
+
In version 0.7.0, a new form objects system were introduced. The old way of using form objects is deprecated and will be removed in 1.0.0. In order to upgrade your code to start using the new way please follow the guide:
|
4
|
+
|
5
|
+
## Replace `Operations::Form` with `Operations::Form::Base`
|
6
|
+
|
7
|
+
If you have any classes that were inherited from `Operations::Form`, please change them to inherit from `Operations::Form::Base`
|
8
|
+
|
9
|
+
## Define forms as separate objects on top of the existing operations:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# Before
|
13
|
+
class Post::Update
|
14
|
+
def self.default
|
15
|
+
@default ||= Operations::Command.new(
|
16
|
+
...,
|
17
|
+
form_hydrator: Post::Update::Hydrator.new,
|
18
|
+
form_model_map: {
|
19
|
+
[%r{.+}] => "Post"
|
20
|
+
}
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# After
|
26
|
+
class Post::Update
|
27
|
+
def self.default
|
28
|
+
@default ||= Operations::Command.new(...)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.default_form
|
32
|
+
@default_form ||= Operations::Form.new(
|
33
|
+
default,
|
34
|
+
hydrators: [Post::Update::Hydrator.new],
|
35
|
+
model_map: Post::Update::ModelMap.new,
|
36
|
+
params_transformations: [
|
37
|
+
ParamsMap.new(id: :post_id)
|
38
|
+
]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
Where `Post::Update::ModelMap` can be a copy of [Operations::Form::DeprecatedLegacyModelMapImplementation](https://github.com/BookingSync/operations/blob/main/lib/operations/form/deprecated_legacy_model_map_implementation.rb) or your own implementation.
|
45
|
+
|
46
|
+
And `ParamsMap` can be as simple as:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class ParamsMap
|
50
|
+
extend Dry::Initializer
|
51
|
+
|
52
|
+
param :params_map
|
53
|
+
|
54
|
+
def call(_form_class, params, **_context)
|
55
|
+
params.transform_keys { |key| params_map[key] || key }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
## Change the way you use forms in you controllers and views:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
# Before
|
64
|
+
class PostsController < ApplicationController
|
65
|
+
def edit
|
66
|
+
@post_update = Post::Update.default.callable(
|
67
|
+
{ post_id: params[:id] },
|
68
|
+
current_user: current_user
|
69
|
+
)
|
70
|
+
|
71
|
+
respond_with @post_update
|
72
|
+
end
|
73
|
+
|
74
|
+
def update
|
75
|
+
# With operations we don't need strong parameters as the operation contract takes care of this.
|
76
|
+
@post_update = Post::Update.default.call(
|
77
|
+
{ **params[:post_update_default_form], post_id: params[:id] },
|
78
|
+
current_user: current_user
|
79
|
+
)
|
80
|
+
|
81
|
+
respond_with @post_update, location: edit_post_url(@post_update.context[:post])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# After
|
86
|
+
class PostsController < ApplicationController
|
87
|
+
def edit
|
88
|
+
@post_update_form = Post::Update.default_form.build(params, current_user: current_user)
|
89
|
+
|
90
|
+
respond_with @post_update_form
|
91
|
+
end
|
92
|
+
|
93
|
+
def update
|
94
|
+
@post_update_form = Post::Update.default_form.persist(params, current_user: current_user)
|
95
|
+
|
96
|
+
respond_with @post_update_form, location: edit_post_url(@post_update_form.operation_result.context[:post])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
Notice that `callable` and `call` methond are replaced with `build` and `persist` respectively.
|
data/lib/operations/command.rb
CHANGED
@@ -197,6 +197,11 @@ class Operations::Command
|
|
197
197
|
result_policies = policies_sum - [Undefined] unless policies_sum == [Undefined, Undefined]
|
198
198
|
options[:policies] = result_policies if result_policies
|
199
199
|
|
200
|
+
if after.present?
|
201
|
+
ActiveSupport::Deprecation.new.warn("Operations::Command `after:` option is deprecated and will be " \
|
202
|
+
"removed in 1.0.0. Please use `on_success:` instead")
|
203
|
+
end
|
204
|
+
|
200
205
|
preconditions.push(precondition) if precondition.present?
|
201
206
|
super(operation, preconditions: preconditions, on_success: after, **options)
|
202
207
|
end
|
@@ -387,7 +392,8 @@ class Operations::Command
|
|
387
392
|
key_map: contract.class.schema.key_map,
|
388
393
|
model_map: Operations::Form::DeprecatedLegacyModelMapImplementation.new(form_model_map),
|
389
394
|
namespace: operation.class,
|
390
|
-
class_name: form_class_name
|
395
|
+
class_name: form_class_name,
|
396
|
+
persisted: true
|
391
397
|
)
|
392
398
|
end
|
393
399
|
|
@@ -5,39 +5,35 @@
|
|
5
5
|
# legacy UI.
|
6
6
|
class Operations::Form::Attribute
|
7
7
|
extend Dry::Initializer
|
8
|
-
include Dry::Equalizer(:name, :collection, :
|
9
|
-
include Operations::Inspect.new(:name, :collection, :
|
8
|
+
include Dry::Equalizer(:name, :collection, :model_class, :model_attribute, :form)
|
9
|
+
include Operations::Inspect.new(:name, :collection, :model_class, :model_attribute, :form)
|
10
10
|
|
11
11
|
param :name, type: Operations::Types::Coercible::Symbol
|
12
12
|
option :collection, type: Operations::Types::Bool, default: proc { false }
|
13
|
-
option :model_name,
|
14
|
-
type: (Operations::Types::String | Operations::Types.Instance(Class).constrained(lt: ActiveRecord::Base)).optional,
|
15
|
-
default: proc {}
|
13
|
+
option :model_name, type: (Operations::Types::String | Operations::Types::Class).optional, default: proc {}
|
16
14
|
option :form, type: Operations::Types::Class.optional, default: proc {}
|
17
15
|
|
18
|
-
def
|
19
|
-
@
|
20
|
-
end
|
16
|
+
def model_class
|
17
|
+
return @model_class if defined?(@model_class)
|
21
18
|
|
22
|
-
|
23
|
-
owning_model.human_attribute_name(string_name, options) if model_name
|
19
|
+
@model_class = model_name.is_a?(String) ? model_name.split("#").first.constantize : model_name
|
24
20
|
end
|
25
21
|
|
26
|
-
def
|
27
|
-
@
|
28
|
-
end
|
22
|
+
def model_attribute
|
23
|
+
return @model_attribute if defined?(@model_attribute)
|
29
24
|
|
30
|
-
|
31
|
-
owning_model.localized_attr_name_for(string_name, locale) if model_name
|
25
|
+
@model_attribute = model_class && (model_name.to_s.split("#").second.presence || name.to_s)
|
32
26
|
end
|
33
27
|
|
34
|
-
|
28
|
+
def model_type
|
29
|
+
model_class.type_for_attribute(model_attribute) if model_name
|
30
|
+
end
|
35
31
|
|
36
|
-
def
|
37
|
-
|
32
|
+
def model_human_name(options = {})
|
33
|
+
model_class.human_attribute_name(model_attribute, options) if model_name
|
38
34
|
end
|
39
35
|
|
40
|
-
def
|
41
|
-
|
36
|
+
def model_validators
|
37
|
+
model_name ? model_class.validators_on(model_attribute) : []
|
42
38
|
end
|
43
39
|
end
|
data/lib/operations/form/base.rb
CHANGED
@@ -47,9 +47,16 @@ class Operations::Form::Base
|
|
47
47
|
|
48
48
|
base.class_attribute :attributes, instance_accessor: false, default: {}
|
49
49
|
base.class_attribute :primary_key, instance_accessor: false, default: :id
|
50
|
+
base.class_attribute :persisted, instance_accessor: false, default: nil
|
50
51
|
|
51
52
|
base.define_method :initialize do |*args, **kwargs|
|
52
|
-
args.empty?
|
53
|
+
if args.empty?
|
54
|
+
# Initializing Operations::Form::Base instance
|
55
|
+
super(kwargs, **{})
|
56
|
+
else
|
57
|
+
# Initializing Operations::Form instance as form object (deprecated)
|
58
|
+
super(*args, **kwargs)
|
59
|
+
end
|
53
60
|
end
|
54
61
|
end
|
55
62
|
|
@@ -82,12 +89,13 @@ class Operations::Form::Base
|
|
82
89
|
|
83
90
|
# :nodoc:
|
84
91
|
module InstanceMethods
|
85
|
-
|
86
|
-
|
92
|
+
# Copied from globalize-accessors, should be deprecated and removed as it is not a core method
|
93
|
+
def localized_attr_name_for(attr_name, locale)
|
94
|
+
"#{attr_name}_#{locale.to_s.underscore}"
|
87
95
|
end
|
88
96
|
|
89
|
-
def
|
90
|
-
self.class.attributes[name.to_sym].
|
97
|
+
def type_for_attribute(name)
|
98
|
+
self.class.attributes[name.to_sym].model_type
|
91
99
|
end
|
92
100
|
|
93
101
|
def has_attribute?(name) # rubocop:disable Naming/PredicateName
|
@@ -132,7 +140,7 @@ class Operations::Form::Base
|
|
132
140
|
end
|
133
141
|
|
134
142
|
def persisted?
|
135
|
-
|
143
|
+
self.class.persisted.nil? ? read_attribute(self.class.primary_key).present? : self.class.persisted
|
136
144
|
end
|
137
145
|
|
138
146
|
def new_record?
|
@@ -11,18 +11,19 @@ class Operations::Form::Builder
|
|
11
11
|
|
12
12
|
option :base_class, Operations::Types::Instance(Class)
|
13
13
|
|
14
|
-
def build(key_map:, model_map:, namespace: nil, class_name: nil, model_name: nil)
|
14
|
+
def build(key_map:, model_map:, namespace: nil, class_name: nil, model_name: nil, persisted: nil)
|
15
15
|
return namespace.const_get(class_name) if namespace && class_name && namespace.const_defined?(class_name)
|
16
16
|
|
17
|
-
traverse(key_map, model_map, namespace, class_name, model_name, [])
|
17
|
+
traverse(key_map, model_map, namespace, class_name, model_name, [], persisted: persisted)
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
|
-
def traverse(key_map, model_map, namespace, class_name, model_name, path)
|
22
|
+
def traverse(key_map, model_map, namespace, class_name, model_name, path, persisted: nil)
|
23
23
|
form = Class.new(base_class)
|
24
24
|
namespace.const_set(class_name, form) if namespace&.name && class_name
|
25
25
|
define_model_name(form, model_name) if model_name && !form.name
|
26
|
+
form.persisted = persisted
|
26
27
|
|
27
28
|
key_map.each { |key| define_attribute(form, model_map, key, path) }
|
28
29
|
form
|
data/lib/operations/form.rb
CHANGED
@@ -21,8 +21,10 @@
|
|
21
21
|
#
|
22
22
|
class Operations::Form
|
23
23
|
include Dry::Core::Constants
|
24
|
-
include Dry::Equalizer(:command, :model_map, :
|
25
|
-
|
24
|
+
include Dry::Equalizer(:command, :model_map, :persisted,
|
25
|
+
:params_transformations, :hydrators, :hydration_merge_params, :form_class)
|
26
|
+
include Operations::Inspect.new(:model_name, :model_map, :persisted,
|
27
|
+
:params_transformations, :hydrators, :hydration_merge_params, :form_class)
|
26
28
|
|
27
29
|
# We need to make deprecated inheritance from Operations::Form act exactly the
|
28
30
|
# same way as from Operations::Form::Base. In order to do this, we are encapsulating all the
|
@@ -48,12 +50,20 @@ class Operations::Form
|
|
48
50
|
param :command, type: Operations::Types.Interface(:operation, :contract, :call)
|
49
51
|
option :model_name, type: Operations::Types::String.optional, default: proc {}, reader: false
|
50
52
|
option :model_map, type: Operations::Types.Interface(:call).optional, default: proc {}
|
53
|
+
option :persisted, type: Operations::Types::Bool, default: proc { true }
|
51
54
|
option :params_transformations, type: Operations::Types::Coercible::Array.of(Operations::Types.Interface(:call)),
|
52
55
|
default: proc { [] }
|
53
|
-
option :
|
56
|
+
option :hydrators, type: Operations::Types::Array.of(Operations::Types.Interface(:call)), default: proc { [] }
|
57
|
+
option :hydration_merge_params, type: Operations::Types::Bool, default: proc { true }
|
54
58
|
option :base_class, type: Operations::Types::Class, default: proc { ::Operations::Form::Base }
|
55
59
|
end)
|
56
60
|
|
61
|
+
def initialize(command, hydrator: nil, hydrators: [], **options)
|
62
|
+
hydrators.push(hydrator) if hydrator.present?
|
63
|
+
|
64
|
+
super(command, hydrators: hydrators, **options)
|
65
|
+
end
|
66
|
+
|
57
67
|
def build(params = EMPTY_HASH, **context)
|
58
68
|
instantiate_form(command.callable(transform_params(params, **context), **context))
|
59
69
|
end
|
@@ -64,7 +74,7 @@ class Operations::Form
|
|
64
74
|
|
65
75
|
def form_class
|
66
76
|
@form_class ||= Operations::Form::Builder.new(base_class: base_class)
|
67
|
-
.build(key_map: key_map, model_map: model_map, model_name: model_name)
|
77
|
+
.build(key_map: key_map, model_map: model_map, model_name: model_name, persisted: persisted)
|
68
78
|
end
|
69
79
|
|
70
80
|
private
|
@@ -80,12 +90,20 @@ class Operations::Form
|
|
80
90
|
|
81
91
|
def instantiate_form(operation_result)
|
82
92
|
form_class.new(
|
83
|
-
|
93
|
+
hydrate_params(form_class, operation_result.params, **operation_result.context),
|
84
94
|
messages: operation_result.errors.to_h,
|
85
95
|
operation_result: operation_result
|
86
96
|
)
|
87
97
|
end
|
88
98
|
|
99
|
+
def hydrate_params(form_class, params, **context)
|
100
|
+
hydrated_params = hydrators.inject({}) do |value, hydrator|
|
101
|
+
value.merge(hydrator.call(form_class, params, **context).deep_symbolize_keys)
|
102
|
+
end
|
103
|
+
hydrated_params.deep_merge!(params) if hydration_merge_params
|
104
|
+
hydrated_params
|
105
|
+
end
|
106
|
+
|
89
107
|
def key_map
|
90
108
|
@key_map ||= command.contract.schema.key_map
|
91
109
|
end
|
data/lib/operations/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: operations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arkadiy Zabazhanov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: appraisal
|
@@ -154,6 +154,7 @@ files:
|
|
154
154
|
- LICENSE.txt
|
155
155
|
- README.md
|
156
156
|
- Rakefile
|
157
|
+
- UPGRADING_FORMS.md
|
157
158
|
- bin/console
|
158
159
|
- bin/setup
|
159
160
|
- gemfiles/rails.5.2.gemfile
|