trailblazer 2.0.2 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +7 -0
- data/lib/trailblazer/operation/guard.rb +1 -1
- data/lib/trailblazer/operation/nested.rb +90 -15
- data/lib/trailblazer/version.rb +1 -1
- data/test/docs/guard_test.rb +56 -32
- data/test/docs/nested_test.rb +106 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1f0c47cf26109508aa2f58b38a69f0a4b5f9b07
|
4
|
+
data.tar.gz: 98cddf35489a1ea61a95e01237d459682d9ab3df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60ef0796bcfa0c12255595c47b514f4a0cbfbac1d90091172459cd4a23b7250d444bff6ecb90d74504c216bebca893bab5cfbe6643d9779ecddd0f7bfc093f2f
|
7
|
+
data.tar.gz: 212e3134780b3146cd25e713b5b83beadd4219f291a772e31cdcb766b745fd334ec765b01f70b085c9fa999ca0e062ae7cff88064a86b65139e73d257241f2d2
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# 2.0.3
|
2
|
+
|
3
|
+
* `Guard` now allows kw args for its option.
|
4
|
+
* Fix a bug where `Nested( ->{} )` wouldn't `_call` the nested operation and did too much work on re-nested the already nested params. Thanks to @eliranf for spotting this.
|
5
|
+
* Add `Nested(..., input: )` to dynamically decide the input to the nested operation. http://trailblazer.to/gems/operation/2.0/api.html#nested-input
|
6
|
+
* Add `Nested(..., output: )`: http://localhost:4000/gems/operation/2.0/api.html#nested-output
|
7
|
+
|
1
8
|
# 2.0.2
|
2
9
|
|
3
10
|
* Remove `uber` dependency as we use our own `Option::KW` now.
|
@@ -8,7 +8,7 @@ class Trailblazer::Operation
|
|
8
8
|
|
9
9
|
module Guard
|
10
10
|
def self.build(callable)
|
11
|
-
value = Option.(callable) # Operation::Option
|
11
|
+
value = Option::KW.(callable) # Operation::Option
|
12
12
|
|
13
13
|
# this gets wrapped in a Operation::Result object.
|
14
14
|
->(input, options) { Result.new( !!value.(input, options), {} ) }
|
@@ -1,32 +1,107 @@
|
|
1
1
|
class Trailblazer::Operation
|
2
|
-
def self.Nested(step)
|
3
|
-
step = Nested.for(step)
|
2
|
+
def self.Nested(step, input:nil, output:nil)
|
3
|
+
step = Nested.for(step, input, output)
|
4
4
|
|
5
|
-
[ step, {} ]
|
5
|
+
[ step, { name: "Nested(#{step})" } ]
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
# WARNING: this is experimental API, but it will end up with something like that.
|
9
|
+
module Element
|
10
|
+
# DISCUSS: add builders here.
|
11
|
+
def initialize(wrapped=nil)
|
12
|
+
@wrapped = wrapped
|
13
|
+
end
|
14
|
+
|
15
|
+
module Dynamic
|
16
|
+
def initialize(wrapped)
|
17
|
+
@wrapped = Option::KW.(wrapped)
|
15
18
|
end
|
16
19
|
end
|
20
|
+
end
|
17
21
|
|
22
|
+
module Nested
|
18
23
|
# Please note that the instance_variable_get are here on purpose since the
|
19
24
|
# superinternal API is not entirely decided, yet.
|
20
|
-
def self.for(step)
|
21
|
-
|
25
|
+
def self.for(step, input, output) # DISCUSS: use builders here?
|
26
|
+
invoker = Caller::Dynamic.new(step)
|
27
|
+
invoker = Caller.new(step) if step.is_a?(Class) && step <= Trailblazer::Operation # interestingly, with < we get a weird nil exception. bug in Ruby?
|
28
|
+
|
29
|
+
options_for_nested = Options.new
|
30
|
+
options_for_nested = Options::Dynamic.new(input) if input
|
31
|
+
|
32
|
+
options_for_composer = Options::Output.new
|
33
|
+
options_for_composer = Options::Output::Dynamic.new(output) if output
|
34
|
+
|
35
|
+
# This lambda is the strut added on the track, executed at runtime.
|
36
|
+
->(operation, options) do
|
37
|
+
result = invoker.(operation, options, options_for_nested.(operation, options)) # TODO: what about containers?
|
22
38
|
|
23
|
-
|
24
|
-
result = proc.(input, options) # TODO: what about containers?
|
39
|
+
options_for_composer.(operation, options, result).each { |k,v| options[k] = v }
|
25
40
|
|
26
|
-
result.instance_variable_get(:@data).to_mutable_data.each { |k,v| options[k] = v }
|
27
41
|
result.success? # DISCUSS: what if we could simply return the result object here?
|
28
42
|
end
|
29
43
|
end
|
44
|
+
|
45
|
+
# Is executed at runtime and calls the nested operation.
|
46
|
+
class Caller
|
47
|
+
include Element
|
48
|
+
|
49
|
+
def call(input, options, options_for_nested)
|
50
|
+
call_nested(nested(input, options), options_for_nested)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def call_nested(operation, options)
|
55
|
+
operation._call(options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def nested(*); @wrapped end
|
59
|
+
|
60
|
+
class Dynamic < Caller
|
61
|
+
include Element::Dynamic
|
62
|
+
|
63
|
+
def nested(input, options)
|
64
|
+
@wrapped.(input, options)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Options
|
70
|
+
include Element
|
71
|
+
|
72
|
+
# Per default, only runtime data for nested operation.
|
73
|
+
def call(input, options)
|
74
|
+
options.to_runtime_data[0]
|
75
|
+
end
|
76
|
+
|
77
|
+
class Dynamic
|
78
|
+
include Element::Dynamic
|
79
|
+
|
80
|
+
def call(operation, options)
|
81
|
+
@wrapped.(operation, options, runtime_data: options.to_runtime_data[0], mutable_data: options.to_mutable_data )
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Output
|
86
|
+
include Element
|
87
|
+
|
88
|
+
def call(input, options, result)
|
89
|
+
mutable_data_for(result).each { |k,v| options[k] = v }
|
90
|
+
end
|
91
|
+
|
92
|
+
def mutable_data_for(result)
|
93
|
+
result.instance_variable_get(:@data).to_mutable_data
|
94
|
+
end
|
95
|
+
|
96
|
+
class Dynamic < Output
|
97
|
+
include Element::Dynamic
|
98
|
+
|
99
|
+
def call(input, options, result)
|
100
|
+
@wrapped.(input, options, mutable_data: mutable_data_for(result))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
30
105
|
end
|
31
106
|
end
|
32
107
|
|
data/lib/trailblazer/version.rb
CHANGED
data/test/docs/guard_test.rb
CHANGED
@@ -3,11 +3,21 @@ require "test_helper"
|
|
3
3
|
#--
|
4
4
|
# with proc
|
5
5
|
class DocsGuardProcTest < Minitest::Spec
|
6
|
+
# test without KWs, only options.
|
7
|
+
class Update < Trailblazer::Operation
|
8
|
+
step Policy::Guard( ->(options) { options["params"][:pass] } )
|
9
|
+
step ->(options, *) { options["x"] = true }
|
10
|
+
end
|
11
|
+
|
12
|
+
it { Update.(pass: false)["x"].must_equal nil }
|
13
|
+
it { Update.(pass: true)["x"].must_equal true }
|
14
|
+
# TODO: test excp when current_user not available
|
15
|
+
|
6
16
|
#:proc
|
7
17
|
class Create < Trailblazer::Operation
|
8
|
-
step Policy::Guard( ->(options) {
|
9
|
-
step :process
|
18
|
+
step Policy::Guard( ->(options, params:, **) { params[:pass] } )
|
10
19
|
#~pipeonly
|
20
|
+
step :process
|
11
21
|
|
12
22
|
def process(*)
|
13
23
|
self["x"] = true
|
@@ -29,7 +39,7 @@ class DocsGuardProcTest < Minitest::Spec
|
|
29
39
|
#---
|
30
40
|
#- Guard inheritance
|
31
41
|
class New < Create
|
32
|
-
step Policy::Guard( ->(options) {
|
42
|
+
step Policy::Guard( ->(options, current_user:, **) { current_user } ), override: true
|
33
43
|
end
|
34
44
|
|
35
45
|
it { New["pipetree"].inspect.must_equal %{[>operation.new,>policy.default.eval,>process]} }
|
@@ -42,8 +52,8 @@ class DocsGuardTest < Minitest::Spec
|
|
42
52
|
class MyGuard
|
43
53
|
include Uber::Callable
|
44
54
|
|
45
|
-
def call(options)
|
46
|
-
|
55
|
+
def call(options, params:, **)
|
56
|
+
params[:pass]
|
47
57
|
end
|
48
58
|
end
|
49
59
|
#:callable end
|
@@ -51,8 +61,8 @@ class DocsGuardTest < Minitest::Spec
|
|
51
61
|
#:callable-op
|
52
62
|
class Create < Trailblazer::Operation
|
53
63
|
step Policy::Guard( MyGuard.new )
|
54
|
-
step :process
|
55
64
|
#~pipe-only
|
65
|
+
step :process
|
56
66
|
def process(*); self[:x] = true; end
|
57
67
|
#~pipe-only end
|
58
68
|
end
|
@@ -68,11 +78,14 @@ class DocsGuardMethodTest < Minitest::Spec
|
|
68
78
|
#:method
|
69
79
|
class Create < Trailblazer::Operation
|
70
80
|
step Policy::Guard( :pass? )
|
81
|
+
|
82
|
+
def pass?(options, params:, **)
|
83
|
+
params[:pass]
|
84
|
+
end
|
85
|
+
#~pipe-onlyy
|
71
86
|
step :process
|
72
|
-
#~pipe-only
|
73
|
-
def pass?(options, *); options["params"][:pass] end
|
74
87
|
def process(*); self["x"] = true; end
|
75
|
-
#~pipe-
|
88
|
+
#~pipe-onlyy end
|
76
89
|
end
|
77
90
|
#:method end
|
78
91
|
|
@@ -85,12 +98,12 @@ end
|
|
85
98
|
class DocsGuardNamedTest < Minitest::Spec
|
86
99
|
#:name
|
87
100
|
class Create < Trailblazer::Operation
|
88
|
-
step Policy::Guard( ->(options) {
|
101
|
+
step Policy::Guard( ->(options, current_user:, **) { current_user }, name: :user )
|
89
102
|
# ...
|
90
103
|
end
|
91
104
|
#:name end
|
92
105
|
|
93
|
-
it { Create.()["result.policy.user"].success?.must_equal false }
|
106
|
+
it { Create.({}, "current_user" => nil )["result.policy.user"].success?.must_equal false }
|
94
107
|
it { Create.({}, "current_user" => Module)["result.policy.user"].success?.must_equal true }
|
95
108
|
|
96
109
|
it {
|
@@ -101,30 +114,12 @@ class DocsGuardNamedTest < Minitest::Spec
|
|
101
114
|
}
|
102
115
|
end
|
103
116
|
|
104
|
-
#---
|
105
|
-
# class-level guard
|
106
|
-
class DocsGuardClassLevelTest < Minitest::Spec
|
107
|
-
#:class-level
|
108
|
-
class Create < Trailblazer::Operation
|
109
|
-
step Policy::Guard( ->(options) { options["current_user"] == Module } ),
|
110
|
-
before: "operation.new"
|
111
|
-
#~pipe--only
|
112
|
-
step ->(options) { options["x"] = true }
|
113
|
-
#~pipe--only end
|
114
|
-
end
|
115
|
-
#:class-level end
|
116
|
-
|
117
|
-
it { Create.(); Create["result.policy"].must_equal nil }
|
118
|
-
it { Create.({}, "current_user" => Module)["x"].must_equal true }
|
119
|
-
it { Create.({} )["x"].must_equal nil }
|
120
|
-
end
|
121
|
-
|
122
117
|
#---
|
123
118
|
# dependency injection
|
124
119
|
class DocsGuardInjectionTest < Minitest::Spec
|
125
120
|
#:di-op
|
126
121
|
class Create < Trailblazer::Operation
|
127
|
-
step Policy::Guard( ->(options) {
|
122
|
+
step Policy::Guard( ->(options, current_user:, **) { current_user == Module } )
|
128
123
|
end
|
129
124
|
#:di-op end
|
130
125
|
|
@@ -140,6 +135,35 @@ class DocsGuardInjectionTest < Minitest::Spec
|
|
140
135
|
result.inspect("").must_equal %{<Result:false [nil] >} }
|
141
136
|
end
|
142
137
|
|
138
|
+
#---
|
139
|
+
# missing current_user throws exception
|
140
|
+
class DocsGuardMissingKeywordTest < Minitest::Spec
|
141
|
+
class Create < Trailblazer::Operation
|
142
|
+
step Policy::Guard( ->(options, current_user:, **) { current_user == Module } )
|
143
|
+
end
|
144
|
+
|
145
|
+
it { assert_raises(ArgumentError) { Create.() } }
|
146
|
+
it { Create.({}, "current_user" => Module).success?.must_equal true }
|
147
|
+
end
|
143
148
|
|
144
|
-
|
145
|
-
#
|
149
|
+
#---
|
150
|
+
# before:
|
151
|
+
class DocsGuardPositionTest < Minitest::Spec
|
152
|
+
#:before
|
153
|
+
class Create < Trailblazer::Operation
|
154
|
+
step :model!
|
155
|
+
step Policy::Guard( :authorize! ),
|
156
|
+
before: :model!
|
157
|
+
end
|
158
|
+
#:before end
|
159
|
+
|
160
|
+
it { Create["pipetree"].inspect.must_equal %{[>operation.new,>policy.default.eval,>model!]} }
|
161
|
+
it do
|
162
|
+
#:before-pipe
|
163
|
+
puts Create["pipetree"].inspect(style: :rows) #=>
|
164
|
+
# 0 ========================>operation.new
|
165
|
+
# 1 ==================>policy.default.eval
|
166
|
+
# 2 ===============================>model!
|
167
|
+
#:before-pipe end
|
168
|
+
end
|
169
|
+
end
|
data/test/docs/nested_test.rb
CHANGED
@@ -71,6 +71,7 @@ class DocsNestedOperationTest < Minitest::Spec
|
|
71
71
|
Update.(id: 2).inspect("model").must_equal %{<Result:false [nil] >}
|
72
72
|
end
|
73
73
|
|
74
|
+
#---
|
74
75
|
#- shared data
|
75
76
|
class B < Trailblazer::Operation
|
76
77
|
success ->(options) { options["can.B.see.it?"] = options["this.should.not.be.visible.in.B"] }
|
@@ -82,7 +83,7 @@ class DocsNestedOperationTest < Minitest::Spec
|
|
82
83
|
self["A.class.data"] = true
|
83
84
|
|
84
85
|
success ->(options) { options["this.should.not.be.visible.in.B"] = true }
|
85
|
-
step Nested B
|
86
|
+
step Nested( B )
|
86
87
|
end
|
87
88
|
|
88
89
|
# mutual data from A doesn't bleed into B.
|
@@ -97,6 +98,103 @@ class DocsNestedOperationTest < Minitest::Spec
|
|
97
98
|
# cr_result = Create.({}, "result" => result)
|
98
99
|
# puts cr_result["model"]
|
99
100
|
# puts cr_result["contract.default"]
|
101
|
+
|
102
|
+
#---
|
103
|
+
#- Nested( .., input: )
|
104
|
+
class C < Trailblazer::Operation
|
105
|
+
self["C.class.data"] = true
|
106
|
+
|
107
|
+
success ->(options) { options["this.should.not.be.visible.in.B"] = true }
|
108
|
+
|
109
|
+
step Nested( B, input: ->(options, runtime_data:, mutable_data:, **) {
|
110
|
+
runtime_data.merge( "this.should.not.be.visible.in.B" => mutable_data["this.should.not.be.visible.in.B"] )
|
111
|
+
} )
|
112
|
+
end
|
113
|
+
|
114
|
+
it { C.()["can.B.see.it?"].must_equal true }
|
115
|
+
it { C.()["this.should.not.be.visible.in.B"].must_equal true } # this IS visible since we use :input!
|
116
|
+
it { C.({}, "current_user" => Module)["can.B.see.current_user?"].must_equal Module }
|
117
|
+
it { C.()["can.B.see.A.class.data?"].must_equal nil }
|
118
|
+
end
|
119
|
+
|
120
|
+
class NestedInput < Minitest::Spec
|
121
|
+
#:input-multiply
|
122
|
+
class Multiplier < Trailblazer::Operation
|
123
|
+
step ->(options, x:, y:, **) { options["product"] = x*y }
|
124
|
+
end
|
125
|
+
#:input-multiply end
|
126
|
+
|
127
|
+
#:input-pi
|
128
|
+
class MultiplyByPi < Trailblazer::Operation
|
129
|
+
step ->(options) { options["pi_constant"] = 3.14159 }
|
130
|
+
step Nested( Multiplier, input: ->(options, mutable_data:, runtime_data:, **) do
|
131
|
+
{ "y" => mutable_data["pi_constant"],
|
132
|
+
"x" => runtime_data["x"] }
|
133
|
+
end )
|
134
|
+
end
|
135
|
+
#:input-pi end
|
136
|
+
|
137
|
+
it { MultiplyByPi.({}, "x" => 9).inspect("product").must_equal %{<Result:true [28.27431] >} }
|
138
|
+
|
139
|
+
it do
|
140
|
+
#:input-result
|
141
|
+
result = MultiplyByPi.({}, "x" => 9)
|
142
|
+
result["product"] #=> [28.27431]
|
143
|
+
#:input-result end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class NestedInputCallable < Minitest::Spec
|
148
|
+
Multiplier = NestedInput::Multiplier
|
149
|
+
|
150
|
+
#:input-callable
|
151
|
+
class MyInput
|
152
|
+
extend Uber::Callable
|
153
|
+
|
154
|
+
def self.call(options, mutable_data:, runtime_data:, **)
|
155
|
+
{
|
156
|
+
"y" => mutable_data["pi_constant"],
|
157
|
+
"x" => runtime_data["x"]
|
158
|
+
}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
#:input-callable end
|
162
|
+
|
163
|
+
#:input-callable-op
|
164
|
+
class MultiplyByPi < Trailblazer::Operation
|
165
|
+
step ->(options) { options["pi_constant"] = 3.14159 }
|
166
|
+
step Nested( Multiplier, input: MyInput )
|
167
|
+
end
|
168
|
+
#:input-callable-op end
|
169
|
+
|
170
|
+
it { MultiplyByPi.({}, "x" => 9).inspect("product").must_equal %{<Result:true [28.27431] >} }
|
171
|
+
end
|
172
|
+
|
173
|
+
#---
|
174
|
+
#- Nested( .., output: )
|
175
|
+
class NestedOutput < Minitest::Spec
|
176
|
+
Edit = DocsNestedOperationTest::Edit
|
177
|
+
|
178
|
+
#:output
|
179
|
+
class Update < Trailblazer::Operation
|
180
|
+
step Nested( Edit, output: ->(options, mutable_data:, **) do
|
181
|
+
{
|
182
|
+
"contract.my" => mutable_data["contract.default"],
|
183
|
+
"model" => mutable_data["model"]
|
184
|
+
}
|
185
|
+
end )
|
186
|
+
step Contract::Validate( name: "my" )
|
187
|
+
step Contract::Persist( method: :sync, name: "my" )
|
188
|
+
end
|
189
|
+
#:output end
|
190
|
+
|
191
|
+
it { Update.( id: 1, title: "Call It A Night" ).inspect("model", "contract.default").must_equal %{<Result:true [#<struct DocsNestedOperationTest::Song id=1, title=\"Call It A Night\">, nil] >} }
|
192
|
+
|
193
|
+
it do
|
194
|
+
result = Update.( id: 1, title: "Call It A Night" )
|
195
|
+
|
196
|
+
result["model"] #=> #<Song id=1 , title=\"Call It A Night\">
|
197
|
+
end
|
100
198
|
end
|
101
199
|
|
102
200
|
class NestedClassLevelTest < Minitest::Spec
|
@@ -107,7 +205,7 @@ class NestedClassLevelTest < Minitest::Spec
|
|
107
205
|
end
|
108
206
|
|
109
207
|
class Create < Trailblazer::Operation
|
110
|
-
step Nested New
|
208
|
+
step Nested( New )
|
111
209
|
step ->(options) { options["y"] = true }
|
112
210
|
end
|
113
211
|
#:class-level end
|
@@ -116,10 +214,13 @@ class NestedClassLevelTest < Minitest::Spec
|
|
116
214
|
it { Create.(); Create["class"].must_equal nil }
|
117
215
|
end
|
118
216
|
|
217
|
+
#---
|
218
|
+
# Nested( ->{} )
|
119
219
|
class NestedWithCallableTest < Minitest::Spec
|
120
220
|
Song = Struct.new(:id, :title)
|
121
221
|
|
122
222
|
class X < Trailblazer::Operation
|
223
|
+
step ->(options, params:, **) { options["params.original"] = params }
|
123
224
|
step ->(options) { options["x"] = true }
|
124
225
|
end
|
125
226
|
|
@@ -132,7 +233,7 @@ class NestedWithCallableTest < Minitest::Spec
|
|
132
233
|
step Nested( ->(options, *) { options["class"] } )
|
133
234
|
end
|
134
235
|
|
135
|
-
it { A.({}, "class" => X).inspect("x", "y", "z").must_equal "<Result:true [true, nil, true] >" }
|
236
|
+
it { A.({ a: 1 }, "class" => X).inspect("x", "y", "z", "params.original").must_equal "<Result:true [true, nil, true, {:a=>1}] >" }
|
136
237
|
it { A.({}, "class" => Y).inspect("x", "y", "z").must_equal "<Result:true [nil, true, true] >" }
|
137
238
|
# it { Create.({}).inspect("x", "y", "z").must_equal "<Result:true [nil, true, true] >" }
|
138
239
|
|
@@ -177,6 +278,7 @@ class NestedWithCallableTest < Minitest::Spec
|
|
177
278
|
it { Create.({}, "current_user" => anonymous).inspect("x").must_equal %{<Result:true [true] >} }
|
178
279
|
it { Create.({}, "current_user" => admin) .inspect("x").must_equal %{<Result:true [nil] >} }
|
179
280
|
|
281
|
+
#---
|
180
282
|
#:method
|
181
283
|
class Update < Trailblazer::Operation
|
182
284
|
step Nested( :build! )
|
@@ -190,6 +292,7 @@ class NestedWithCallableTest < Minitest::Spec
|
|
190
292
|
it { Update.({}, "current_user" => anonymous).inspect("x").must_equal %{<Result:true [true] >} }
|
191
293
|
it { Update.({}, "current_user" => admin) .inspect("x").must_equal %{<Result:true [nil] >} }
|
192
294
|
|
295
|
+
#---
|
193
296
|
#:callable-builder
|
194
297
|
class MyBuilder
|
195
298
|
extend Uber::Callable
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trailblazer-operation
|