trailblazer-macro 2.1.0.beta1 → 2.1.0.beta2
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/.rubocop.yml +8 -8
- data/.rubocop_todo.yml +213 -277
- data/CHANGES.md +5 -0
- data/Gemfile +2 -1
- data/README.md +271 -1
- data/Rakefile +6 -2
- data/lib/trailblazer/macro/version.rb +1 -1
- data/lib/trailblazer/operation/model.rb +9 -7
- data/test/docs/model_test.rb +32 -3
- data/test/operation/model_test.rb +33 -5
- data/trailblazer-macro.gemspec +1 -1
- metadata +4 -4
data/CHANGES.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
# 2.1.0.beta2
|
|
2
|
+
|
|
3
|
+
* Move all code related to DSL (`ClassDependencies`) back to the `trailblazer` gem.
|
|
4
|
+
* Configurable field key for the `Model()` macro
|
|
5
|
+
|
|
1
6
|
# 2.1.0.beta1
|
|
2
7
|
|
|
3
8
|
* First release into an unsuspecting world. Goal is to have this gem decoupled from any Representable and Reform dependencies.
|
data/Gemfile
CHANGED
|
@@ -7,7 +7,8 @@ gemspec
|
|
|
7
7
|
# gem "trailblazer-operation", path: "../operation"
|
|
8
8
|
# gem "trailblazer-operation", github: "trailblazer/trailblazer-operation"
|
|
9
9
|
gem "trailblazer-activity"#, github: "trailblazer/trailblazer-activity"
|
|
10
|
-
gem "trailblazer-macro-contract", git: "https://github.com/trailblazer/trailblazer-macro-contract"
|
|
10
|
+
# gem "trailblazer-macro-contract", git: "https://github.com/trailblazer/trailblazer-macro-contract"
|
|
11
|
+
gem "trailblazer-macro-contract", path: "../trailblazer-macro-contract"
|
|
11
12
|
|
|
12
13
|
gem "minitest-line"
|
|
13
14
|
gem "rubocop", require: false
|
data/README.md
CHANGED
|
@@ -2,4 +2,274 @@
|
|
|
2
2
|
All common Macro's for Trailblazer::Operation, will come here
|
|
3
3
|
|
|
4
4
|
## TODO
|
|
5
|
-
Describe the Macro's
|
|
5
|
+
Describe the following Macro's:
|
|
6
|
+
- Nested
|
|
7
|
+
- Rescue
|
|
8
|
+
- Wrap
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
- [Model Macro](#model-macro)
|
|
12
|
+
- [Policy Macro](#policy-macro)
|
|
13
|
+
* [Policy::Pundit - Macro](#policy--pundit---macro)
|
|
14
|
+
+ [Policy::Pundit - API](#policy--pundit---api)
|
|
15
|
+
+ [Policy::Pundit - Name](#policy--pundit---name)
|
|
16
|
+
+ [Policy::Pundit - Dependency Injection](#policy--pundit---dependency-injection)
|
|
17
|
+
* [Policy::Guard - Macro](#policy--guard---macro)
|
|
18
|
+
+ [Policy::Guard - API](#policy--guard---api)
|
|
19
|
+
+ [Policy::Guard - Callable](#policy--guard---callable)
|
|
20
|
+
+ [Policy::Guard - Instance Method](#policy--guard---instance-method)
|
|
21
|
+
+ [Policy::Guard - Name](#policy--guard---name)
|
|
22
|
+
+ [Policy::Guard - Dependency Injection](#policy--guard---dependency-injection)
|
|
23
|
+
+ [Policy::Guard - Position](#policy--guard---position)
|
|
24
|
+
|
|
25
|
+
## Model Macro
|
|
26
|
+
Trailblazer also has a convenient Macro to handle model creation and basic finding by id. The Model macro literally does what our model! step did.
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
class Song::Create < Trailblazer::Operation
|
|
30
|
+
step Policy::Guard( :authorize! )
|
|
31
|
+
step Model( Song, :new )
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Note that Model is not designed for complex query logic - should you need that, you might want to use [Trailblazer Finder][trailblazer_finder_link] or simply write your own customized step.
|
|
36
|
+
|
|
37
|
+
Due to a lot of requests, we have adjusted the `:find_by` method so you can specify a key to find by.
|
|
38
|
+
```ruby
|
|
39
|
+
class Song::Create < Trailblazer::Operation
|
|
40
|
+
step Policy::Guard( :authorize! )
|
|
41
|
+
step Model( Song, :find_by, :title )
|
|
42
|
+
end
|
|
43
|
+
```
|
|
44
|
+
Not specifying the third parameter in the Model Macro for `:find_by`, will result in defaulting it back to `:id`.
|
|
45
|
+
|
|
46
|
+
[trailblazer-finder-link]: https://github.com/trailblazer/trailblazer-finder/
|
|
47
|
+
|
|
48
|
+
## Policy Macro
|
|
49
|
+
An optional Policy Macro for Trailblazer Operations that blocks unauthorized users from running the operation.
|
|
50
|
+
|
|
51
|
+
You can abort running an operation using a policy. "Pundit-style" policy classes define the rules.
|
|
52
|
+
```ruby
|
|
53
|
+
class Comment::Policy
|
|
54
|
+
def initialize(user, comment)
|
|
55
|
+
@user, @comment = user, comment
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def create?
|
|
59
|
+
@user.admin?
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The rule is enabled via the ::policy call.
|
|
65
|
+
```ruby
|
|
66
|
+
class Comment::Create < Trailblazer::Operation
|
|
67
|
+
step Policy( Comment::Policy, :create? )
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The policy is evaluated in #setup!, raises an exception if false and suppresses running #process.
|
|
72
|
+
|
|
73
|
+
### Policy::Pundit - Macro
|
|
74
|
+
The Policy::Pundit Macro allows using Pundit-compatible policy classes in an operation.
|
|
75
|
+
|
|
76
|
+
A Pundit policy has various rule methods and a special constructor that receives the current user and the current model.
|
|
77
|
+
```ruby
|
|
78
|
+
class MyPolicy
|
|
79
|
+
def initialize(user, model)
|
|
80
|
+
@user, @model = user, model
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def create?
|
|
84
|
+
@user == Module && @model.id.nil?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def new?
|
|
88
|
+
@user == Class
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
In pundit policies, it is a convention to have access to those objects at runtime and build rules on top of those.
|
|
94
|
+
|
|
95
|
+
You can plug this policy into your pipe at any point. However, this must be inserted after the "model" skill is available.
|
|
96
|
+
```ruby
|
|
97
|
+
class Create < Trailblazer::Operation
|
|
98
|
+
step Model( Song, :new )
|
|
99
|
+
step Policy::Pundit( MyPolicy, :create? )
|
|
100
|
+
# ...
|
|
101
|
+
end
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Note that you don’t have to create the model via the Model macro - you can use any logic you want. The Pundit macro will grab the model from ["model"], though.
|
|
105
|
+
|
|
106
|
+
This policy will only pass when the operation is invoked as follows.
|
|
107
|
+
```ruby
|
|
108
|
+
Create.( {}, "current_user" => User.find(1) )
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Any other call will cause a policy breach and stop the pipe from executing after the Policy::Pundit step.
|
|
112
|
+
|
|
113
|
+
#### Policy::Pundit - API
|
|
114
|
+
Add your polices using the Policy::Pundit macro. It accepts the policy class name, and the rule method to call.
|
|
115
|
+
```ruby
|
|
116
|
+
class Create < Trailblazer::Operation
|
|
117
|
+
step Model( Song, :new )
|
|
118
|
+
step Policy::Pundit( MyPolicy, :create? )
|
|
119
|
+
# ...
|
|
120
|
+
end
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The step will create the policy instance automatically for you and passes the "model" and the "current_user" skill into the policies constructor. Just make sure those dependencies are available before the step is executed.
|
|
124
|
+
|
|
125
|
+
If the policy returns falsey, it deviates to the left track.
|
|
126
|
+
|
|
127
|
+
After running the Pundit step, its result is readable from the Result object.
|
|
128
|
+
```ruby
|
|
129
|
+
result = Create.({}, "current_user" => Module)
|
|
130
|
+
result["result.policy.default"].success? #=> true
|
|
131
|
+
result["result.policy.default"]["policy"] #=> #<MyPolicy ...>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Note that the actual policy instance is available via ["result.policy.#{name}"]["policy"] to be reinvoked with other rules (e.g. in the view layer).
|
|
135
|
+
|
|
136
|
+
#### Policy::Pundit - Name
|
|
137
|
+
You can add any number of Pundit policies to your pipe. Make sure to use name: to name them, though.
|
|
138
|
+
```ruby
|
|
139
|
+
class Create < Trailblazer::Operation
|
|
140
|
+
step Model( Song, :new )
|
|
141
|
+
step Policy::Pundit( MyPolicy, :create?, name: "after_model" )
|
|
142
|
+
# ...
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The result will be stored in "result.policy.#{name}"
|
|
147
|
+
```ruby
|
|
148
|
+
result = Create.({}, "current_user" => Module)
|
|
149
|
+
result["result.policy.after_model"].success? #=> true
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Policy::Pundit - Dependency Injection
|
|
153
|
+
Override a configured policy using dependency injection.
|
|
154
|
+
```ruby
|
|
155
|
+
Create.({},
|
|
156
|
+
"current_user" => Module,
|
|
157
|
+
"policy.default.eval" => Trailblazer::Operation::Policy::Pundit.build(AnotherPolicy, :create?)
|
|
158
|
+
)
|
|
159
|
+
```
|
|
160
|
+
You can inject it using "policy.#{name}.eval". It can be any object responding to call.
|
|
161
|
+
|
|
162
|
+
### Policy::Guard - Macro
|
|
163
|
+
A guard is a step that helps you evaluating a condition and writing the result. If the condition was evaluated as falsey, the pipe won’t be further processed and a policy breach is reported in Result["result.policy.default"].
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
class Create < Trailblazer::Operation
|
|
167
|
+
step Policy::Guard( ->(options, params:, **) { params[:pass] } )
|
|
168
|
+
step :process
|
|
169
|
+
|
|
170
|
+
def process(*)
|
|
171
|
+
self["x"] = true
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The only way to make the above operation invoke the second step :process is as follows.
|
|
177
|
+
```ruby
|
|
178
|
+
result = Create.({ pass: true })
|
|
179
|
+
result["x"] #=> true
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Any other input will result in an abortion of the pipe after the guard.
|
|
183
|
+
```ruby
|
|
184
|
+
result = Create.()
|
|
185
|
+
result["x"] #=> nil
|
|
186
|
+
result["result.policy.default"].success? #=> false
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### Policy::Guard - API
|
|
190
|
+
The Policy::Guard macro helps you inserting your guard logic. If not defined, it will be evaluated where you insert it.
|
|
191
|
+
```ruby
|
|
192
|
+
class Create < Trailblazer::Operation
|
|
193
|
+
step Policy::Guard( ->(options, params:, **) { params[:pass] } )
|
|
194
|
+
# ...
|
|
195
|
+
end
|
|
196
|
+
```
|
|
197
|
+
The options object is passed into the guard and allows you to read and inspect data like params or current_user. Please use kw args.
|
|
198
|
+
|
|
199
|
+
#### Policy::Guard - Callable
|
|
200
|
+
As always, the guard can also be a Callable-marked object.
|
|
201
|
+
```ruby
|
|
202
|
+
class MyGuard
|
|
203
|
+
include Uber::Callable
|
|
204
|
+
|
|
205
|
+
def call(options, params:, **)
|
|
206
|
+
params[:pass]
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Insert the object instance via the Policy::Guard macro.
|
|
212
|
+
```ruby
|
|
213
|
+
class Create < Trailblazer::Operation
|
|
214
|
+
step Policy::Guard( MyGuard.new )
|
|
215
|
+
# ...
|
|
216
|
+
end
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### Policy::Guard - Instance Method
|
|
220
|
+
As always, you may also use an instance method to implement a guard.
|
|
221
|
+
```ruby
|
|
222
|
+
class Create < Trailblazer::Operation
|
|
223
|
+
step Policy::Guard( :pass? )
|
|
224
|
+
|
|
225
|
+
def pass?(options, params:, **)
|
|
226
|
+
params[:pass]
|
|
227
|
+
end
|
|
228
|
+
# ...
|
|
229
|
+
end
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Policy::Guard - Name
|
|
233
|
+
The guard name defaults to default and can be set via name:. This allows having multiple guards.
|
|
234
|
+
```ruby
|
|
235
|
+
class Create < Trailblazer::Operation
|
|
236
|
+
step Policy::Guard( ->(options, current_user:, **) { current_user }, name: :user )
|
|
237
|
+
# ...
|
|
238
|
+
end
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The result will sit in result.policy.#{name}.
|
|
242
|
+
```ruby
|
|
243
|
+
result = Create.({}, "current_user" => true)
|
|
244
|
+
result["result.policy.user"].success? #=> true
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Policy::Guard - Dependency Injection
|
|
248
|
+
Instead of using the configured guard, you can inject any callable object that returns a Result object. Do so by overriding the policy.#{name}.eval path when calling the operation.
|
|
249
|
+
```ruby
|
|
250
|
+
Create.({},
|
|
251
|
+
"current_user" => Module,
|
|
252
|
+
"policy.default.eval" => Trailblazer::Operation::Policy::Guard.build(->(options) { false })
|
|
253
|
+
)
|
|
254
|
+
```
|
|
255
|
+
An easy way to let Trailblazer build a compatible object for you is using Guard.build.
|
|
256
|
+
|
|
257
|
+
This is helpful to override a certain policy for testing, or to invoke it with special rights, e.g. for an admin.
|
|
258
|
+
|
|
259
|
+
#### Policy::Guard - Position
|
|
260
|
+
You may specify a position.
|
|
261
|
+
```ruby
|
|
262
|
+
class Create < Trailblazer::Operation
|
|
263
|
+
step :model!
|
|
264
|
+
step Policy::Guard( :authorize! ), before: :model!
|
|
265
|
+
end
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Resulting in the guard inserted before model!, even though it was added at a later point.
|
|
269
|
+
```ruby
|
|
270
|
+
puts Create["pipetree"].inspect(style: :rows) #=>
|
|
271
|
+
# 0 ========================>operation.new
|
|
272
|
+
# 1 ==================>policy.default.eval
|
|
273
|
+
# 2 ===============================>model!
|
|
274
|
+
```
|
|
275
|
+
This is helpful if you maintain modules for operations with generic steps.
|
data/Rakefile
CHANGED
|
@@ -2,7 +2,7 @@ require "bundler/gem_tasks"
|
|
|
2
2
|
require "rake/testtask"
|
|
3
3
|
require "rubocop/rake_task"
|
|
4
4
|
|
|
5
|
-
task :default => [
|
|
5
|
+
task :default => %i[test rubocop]
|
|
6
6
|
|
|
7
7
|
Rake::TestTask.new(:test) do |test|
|
|
8
8
|
test.libs << 'test'
|
|
@@ -10,4 +10,8 @@ Rake::TestTask.new(:test) do |test|
|
|
|
10
10
|
test.verbose = true
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
RuboCop::RakeTask.new
|
|
13
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
|
14
|
+
task.patterns = ['lib/**/*.rb', 'test/**/*.rb']
|
|
15
|
+
task.options << "--display-cop-names"
|
|
16
|
+
task.fail_on_error = false
|
|
17
|
+
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
class Trailblazer::Operation
|
|
2
|
-
def self.Model(model_class, action=nil)
|
|
2
|
+
def self.Model(model_class, action=nil, find_by_key=nil)
|
|
3
3
|
task = Trailblazer::Activity::TaskBuilder::Binary.call(Model.new)
|
|
4
4
|
|
|
5
5
|
extension = Trailblazer::Activity::TaskWrap::Merge.new(
|
|
6
6
|
Wrap::Inject::Defaults(
|
|
7
|
-
"model.class"
|
|
8
|
-
"model.action"
|
|
7
|
+
"model.class" => model_class,
|
|
8
|
+
"model.action" => action,
|
|
9
|
+
"model.find_by_key" => find_by_key
|
|
9
10
|
)
|
|
10
11
|
)
|
|
11
12
|
|
|
@@ -25,9 +26,10 @@ class Trailblazer::Operation
|
|
|
25
26
|
def call(options, params)
|
|
26
27
|
action = options["model.action"] || :new
|
|
27
28
|
model_class = options["model.class"]
|
|
29
|
+
find_by_key = options["model.find_by_key"] || :id
|
|
28
30
|
action = :pass_through unless %i[new find_by find].include?(action)
|
|
29
31
|
|
|
30
|
-
send("#{action}!", model_class, params, options["model.action"])
|
|
32
|
+
send("#{action}!", model_class, params, options["model.action"], find_by_key)
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
def new!(model_class, params, *)
|
|
@@ -39,12 +41,12 @@ class Trailblazer::Operation
|
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
# Doesn't throw an exception and will return false to divert to Left.
|
|
42
|
-
def find_by!(model_class, params, *)
|
|
43
|
-
model_class.find_by(
|
|
44
|
+
def find_by!(model_class, params, action, find_by_key, *)
|
|
45
|
+
model_class.find_by(find_by_key.to_sym => params[find_by_key])
|
|
44
46
|
end
|
|
45
47
|
|
|
46
48
|
# Call any method on the model class and pass :id.
|
|
47
|
-
def pass_through!(model_class, params, action)
|
|
49
|
+
def pass_through!(model_class, params, action, *)
|
|
48
50
|
model_class.send(action, params[:id])
|
|
49
51
|
end
|
|
50
52
|
end
|
data/test/docs/model_test.rb
CHANGED
|
@@ -2,8 +2,11 @@ require "test_helper"
|
|
|
2
2
|
|
|
3
3
|
class DocsModelTest < Minitest::Spec
|
|
4
4
|
Song = Struct.new(:id, :title) do
|
|
5
|
-
def self.find_by(
|
|
6
|
-
|
|
5
|
+
def self.find_by(args)
|
|
6
|
+
key, value = args.flatten
|
|
7
|
+
return nil if value.nil?
|
|
8
|
+
return new(value) if key == :id
|
|
9
|
+
new(2, value) if key == :title
|
|
7
10
|
end
|
|
8
11
|
|
|
9
12
|
def self.[](id)
|
|
@@ -27,6 +30,7 @@ class DocsModelTest < Minitest::Spec
|
|
|
27
30
|
result[:model].inspect.must_equal %{#<struct DocsModelTest::Song id=nil, title=nil>}
|
|
28
31
|
end
|
|
29
32
|
|
|
33
|
+
|
|
30
34
|
#:update
|
|
31
35
|
class Update < Trailblazer::Operation
|
|
32
36
|
step Model( Song, :find_by )
|
|
@@ -34,22 +38,47 @@ class DocsModelTest < Minitest::Spec
|
|
|
34
38
|
end
|
|
35
39
|
#:update end
|
|
36
40
|
|
|
41
|
+
#:update-with-find-by-key
|
|
42
|
+
class UpdateWithFindByKey < Trailblazer::Operation
|
|
43
|
+
step Model( Song, :find_by, :title )
|
|
44
|
+
# ..
|
|
45
|
+
end
|
|
46
|
+
#:update-with-find-by-key end
|
|
47
|
+
|
|
37
48
|
it do
|
|
38
49
|
#:update-ok
|
|
39
50
|
result = Update.(params: { id: 1 })
|
|
40
|
-
result[:model] #=> #<struct Song id=1, title="
|
|
51
|
+
result[:model] #=> #<struct Song id=1, title="nil">
|
|
41
52
|
#:update-ok end
|
|
42
53
|
|
|
43
54
|
result[:model].inspect.must_equal %{#<struct DocsModelTest::Song id=1, title=nil>}
|
|
44
55
|
end
|
|
45
56
|
|
|
57
|
+
it do
|
|
58
|
+
#:update-with-find-by-key-ok
|
|
59
|
+
result = UpdateWithFindByKey.(params: { title: "Test" } )
|
|
60
|
+
result[:model] #=> #<struct Song id=2, title="Test">
|
|
61
|
+
#:update-with-find-by-key-ok end
|
|
62
|
+
|
|
63
|
+
result[:model].inspect.must_equal %{#<struct DocsModelTest::Song id=2, title="Test">}
|
|
64
|
+
end
|
|
65
|
+
|
|
46
66
|
it do
|
|
47
67
|
#:update-fail
|
|
48
68
|
result = Update.(params: {})
|
|
49
69
|
result[:model] #=> nil
|
|
50
70
|
result.success? #=> false
|
|
51
71
|
#:update-fail end
|
|
72
|
+
result[:model].must_be_nil
|
|
73
|
+
result.success?.must_equal false
|
|
74
|
+
end
|
|
52
75
|
|
|
76
|
+
it do
|
|
77
|
+
#:update-with-find-by-key-fail
|
|
78
|
+
result = UpdateWithFindByKey.(params: {title: nil})
|
|
79
|
+
result[:model] #=> nil
|
|
80
|
+
result.success? #=> false
|
|
81
|
+
#:update-with-find-by-key-fail end
|
|
53
82
|
result[:model].must_be_nil
|
|
54
83
|
result.success?.must_equal false
|
|
55
84
|
end
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
|
|
3
3
|
class ModelTest < Minitest::Spec
|
|
4
|
-
Song = Struct.new(:id) do
|
|
4
|
+
Song = Struct.new(:id, :title) do
|
|
5
5
|
def self.find(id); new(id) end
|
|
6
|
-
def self.find_by(
|
|
6
|
+
def self.find_by(args)
|
|
7
|
+
key, value = args.flatten
|
|
8
|
+
return nil if value.nil?
|
|
9
|
+
return new(value) if key == :id
|
|
10
|
+
new(2, value) if key == :title
|
|
11
|
+
end
|
|
7
12
|
end
|
|
8
13
|
|
|
9
14
|
#---
|
|
@@ -13,7 +18,7 @@ class ModelTest < Minitest::Spec
|
|
|
13
18
|
end
|
|
14
19
|
|
|
15
20
|
# :new new.
|
|
16
|
-
it { Create.(params: {})[:model].inspect.must_equal %{#<struct ModelTest::Song id=nil>} }
|
|
21
|
+
it { Create.(params: {})[:model].inspect.must_equal %{#<struct ModelTest::Song id=nil, title=nil>} }
|
|
17
22
|
|
|
18
23
|
class Update < Create
|
|
19
24
|
step Model( Song, :find ), override: true
|
|
@@ -23,7 +28,7 @@ class ModelTest < Minitest::Spec
|
|
|
23
28
|
#- inheritance
|
|
24
29
|
|
|
25
30
|
# :find it
|
|
26
|
-
it { Update.(params: { id: 1 })[:model].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
|
|
31
|
+
it { Update.(params: { id: 1 })[:model].inspect.must_equal %{#<struct ModelTest::Song id=1, title=nil>} }
|
|
27
32
|
|
|
28
33
|
# inherited inspect is ok
|
|
29
34
|
it { Trailblazer::Operation::Inspect.(Update).must_equal %{[>model.build]} }
|
|
@@ -37,6 +42,14 @@ class ModelTest < Minitest::Spec
|
|
|
37
42
|
def process(options, **); options["x"] = true end
|
|
38
43
|
end
|
|
39
44
|
|
|
45
|
+
# :find_by, exceptionless.
|
|
46
|
+
class FindByKey < Trailblazer::Operation
|
|
47
|
+
step Model( Song, :find_by, :title )
|
|
48
|
+
step :process
|
|
49
|
+
|
|
50
|
+
def process(options, **); options["x"] = true end
|
|
51
|
+
end
|
|
52
|
+
|
|
40
53
|
# can't find model.
|
|
41
54
|
#- result object, model
|
|
42
55
|
it do
|
|
@@ -49,6 +62,21 @@ class ModelTest < Minitest::Spec
|
|
|
49
62
|
it do
|
|
50
63
|
Find.(params: {id: 9})["result.model"].success?.must_equal true
|
|
51
64
|
Find.(params: {id: 9})["x"].must_equal true
|
|
52
|
-
Find.(params: {id: 9})[:model].inspect.must_equal %{#<struct ModelTest::Song id=9>}
|
|
65
|
+
Find.(params: {id: 9})[:model].inspect.must_equal %{#<struct ModelTest::Song id=9, title=nil>}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# can't find model by title.
|
|
69
|
+
#- result object, model
|
|
70
|
+
it do
|
|
71
|
+
FindByKey.(params: {title: nil})["result.model"].failure?.must_equal true
|
|
72
|
+
FindByKey.(params: {title: nil})["x"].must_be_nil
|
|
73
|
+
FindByKey.(params: {title: nil}).failure?.must_equal true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#- result object, model by title
|
|
77
|
+
it do
|
|
78
|
+
FindByKey.(params: {title: "Test"})["result.model"].success?.must_equal true
|
|
79
|
+
FindByKey.(params: {title: "Test"})["x"].must_equal true
|
|
80
|
+
FindByKey.(params: {title: "Test"})[:model].inspect.must_equal %{#<struct ModelTest::Song id=2, title="Test">}
|
|
53
81
|
end
|
|
54
82
|
end
|