trailblazer 2.0.2 → 2.0.3
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/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
|