operations 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -3
- data/README.md +30 -3
- data/UPGRADING_FORMS.md +101 -0
- data/lib/operations/command.rb +2 -1
- data/lib/operations/form/base.rb +2 -1
- data/lib/operations/form/builder.rb +4 -3
- data/lib/operations/form.rb +4 -3
- 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: 41842fbe905d259c1c39bd2922caa0dc50583cdfceac0c0b78ce7e4e8e1d26f4
|
4
|
+
data.tar.gz: 37e703dca9a9db80167e2750e77af20e59494a3fb1c0f208720e08ae2bb83a8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33ebaee09aff4d2cd32742566e1c3959c5f43558b6e3c57760936a31f8431af2806a8b6b8807da10588fbf2916de5e34857cc9ce5378b93d70099dd09b049d78
|
7
|
+
data.tar.gz: 1f733f0567064105528b480d47f766aa6acd9b2dd2766d011e11edeb542f3eea6233f2e30cb810d10f4e9412ab9548a186643072dd80414cf07b9aecc7189570
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [0.7.
|
3
|
+
## [0.7.1](https://github.com/BookingSync/operations/tree/v0.7.1)
|
4
4
|
|
5
5
|
### Added
|
6
6
|
|
7
|
-
-
|
7
|
+
- Added `persisted:` option to the new forms definition. [\#48](https://github.com/BookingSync/operations/pull/48) ([pyromaniac](https://github.com/pyromaniac))
|
8
|
+
|
9
|
+
## [0.7.0](https://github.com/BookingSync/operations/tree/v0.7.0)
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- 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
14
|
|
9
15
|
### Improvements
|
10
16
|
|
@@ -15,7 +21,7 @@
|
|
15
21
|
|
16
22
|
- 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
23
|
|
18
|
-
## [0.6.
|
24
|
+
## [0.6.3](https://github.com/BookingSync/operations/tree/v0.6.3)
|
19
25
|
|
20
26
|
### Added
|
21
27
|
|
data/README.md
CHANGED
@@ -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
|
@@ -939,7 +941,18 @@ class Post::Update::ModelMap
|
|
939
941
|
end
|
940
942
|
```
|
941
943
|
|
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`.
|
944
|
+
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:
|
945
|
+
|
946
|
+
```ruby
|
947
|
+
class Post::Update
|
948
|
+
def self.default_form
|
949
|
+
@default_form ||= Operations::Form.new(
|
950
|
+
default,
|
951
|
+
model_name: "custom_post_update_form", # form name can be customized
|
952
|
+
)
|
953
|
+
end
|
954
|
+
end
|
955
|
+
```
|
943
956
|
|
944
957
|
It is possible to add more params transfomations to the form in cases when operation contract is different from the params structure:
|
945
958
|
|
@@ -948,7 +961,6 @@ class Post::Update
|
|
948
961
|
def self.default_form
|
949
962
|
@default_form ||= Operations::Form.new(
|
950
963
|
default,
|
951
|
-
model_name: "post_update_form", # form name can be customized
|
952
964
|
params_transformations: [
|
953
965
|
ParamsMap.new(id: :post_id),
|
954
966
|
NestedAttributes.new(:sections)
|
@@ -994,6 +1006,21 @@ class NestedAttributes
|
|
994
1006
|
end
|
995
1007
|
```
|
996
1008
|
|
1009
|
+
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:
|
1010
|
+
|
1011
|
+
```ruby
|
1012
|
+
class Post::Create
|
1013
|
+
def self.default_form
|
1014
|
+
@default_form ||= Operations::Form.new(
|
1015
|
+
default,
|
1016
|
+
persisted: false
|
1017
|
+
)
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
```
|
1021
|
+
|
1022
|
+
Note that operation itself is agnostic to the persistence layer, so there is no way for it to figure out this semanticsa automatically.
|
1023
|
+
|
997
1024
|
## Development
|
998
1025
|
|
999
1026
|
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
|
+
hydrator: 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
@@ -387,7 +387,8 @@ class Operations::Command
|
|
387
387
|
key_map: contract.class.schema.key_map,
|
388
388
|
model_map: Operations::Form::DeprecatedLegacyModelMapImplementation.new(form_model_map),
|
389
389
|
namespace: operation.class,
|
390
|
-
class_name: form_class_name
|
390
|
+
class_name: form_class_name,
|
391
|
+
persisted: true
|
391
392
|
)
|
392
393
|
end
|
393
394
|
|
data/lib/operations/form/base.rb
CHANGED
@@ -47,6 +47,7 @@ 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
53
|
args.empty? && kwargs.present? ? super(kwargs, **{}) : super(*args, **kwargs)
|
@@ -132,7 +133,7 @@ class Operations::Form::Base
|
|
132
133
|
end
|
133
134
|
|
134
135
|
def persisted?
|
135
|
-
|
136
|
+
self.class.persisted.nil? ? read_attribute(self.class.primary_key).present? : self.class.persisted
|
136
137
|
end
|
137
138
|
|
138
139
|
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,8 @@
|
|
21
21
|
#
|
22
22
|
class Operations::Form
|
23
23
|
include Dry::Core::Constants
|
24
|
-
include Dry::Equalizer(:command, :model_map, :params_transformations, :hydrator, :form_class)
|
25
|
-
include Operations::Inspect.new(:model_name, :model_map, :params_transformations, :hydrator, :form_class)
|
24
|
+
include Dry::Equalizer(:command, :model_map, :persisted, :params_transformations, :hydrator, :form_class)
|
25
|
+
include Operations::Inspect.new(:model_name, :model_map, :persisted, :params_transformations, :hydrator, :form_class)
|
26
26
|
|
27
27
|
# We need to make deprecated inheritance from Operations::Form act exactly the
|
28
28
|
# same way as from Operations::Form::Base. In order to do this, we are encapsulating all the
|
@@ -48,6 +48,7 @@ class Operations::Form
|
|
48
48
|
param :command, type: Operations::Types.Interface(:operation, :contract, :call)
|
49
49
|
option :model_name, type: Operations::Types::String.optional, default: proc {}, reader: false
|
50
50
|
option :model_map, type: Operations::Types.Interface(:call).optional, default: proc {}
|
51
|
+
option :persisted, type: Operations::Types::Bool, default: proc { true }
|
51
52
|
option :params_transformations, type: Operations::Types::Coercible::Array.of(Operations::Types.Interface(:call)),
|
52
53
|
default: proc { [] }
|
53
54
|
option :hydrator, type: Operations::Types.Interface(:call).optional, default: proc {}
|
@@ -64,7 +65,7 @@ class Operations::Form
|
|
64
65
|
|
65
66
|
def form_class
|
66
67
|
@form_class ||= Operations::Form::Builder.new(base_class: base_class)
|
67
|
-
.build(key_map: key_map, model_map: model_map, model_name: model_name)
|
68
|
+
.build(key_map: key_map, model_map: model_map, model_name: model_name, persisted: persisted)
|
68
69
|
end
|
69
70
|
|
70
71
|
private
|
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.1
|
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-07-
|
11
|
+
date: 2024-07-29 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
|