trailblazer-operation 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/CHANGES.md +9 -0
- data/Gemfile +1 -0
- data/Rakefile +4 -2
- data/lib/trailblazer/operation.rb +4 -4
- data/lib/trailblazer/operation/pipetree.rb +81 -74
- data/lib/trailblazer/operation/version.rb +1 -1
- data/test/2.0.0-pipetree_test.rb +22 -3
- data/test/2.1.0-pipetree_test.rb +84 -0
- data/test/call_test.rb +4 -4
- data/test/gemfiles/Gemfile.ruby-1.9 +1 -1
- data/test/gemfiles/Gemfile.ruby-1.9.lock +3 -4
- data/test/gemfiles/Gemfile.ruby-2.0 +6 -0
- data/test/gemfiles/Gemfile.ruby-2.0.lock +39 -0
- data/test/operation_skill_test.rb +3 -3
- data/test/pipetree_test.rb +111 -29
- data/test/result_test.rb +1 -1
- data/trailblazer-operation.gemspec +2 -2
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0302dd8b35ae2d64dd482bbc330604e5c55b65c
|
4
|
+
data.tar.gz: fb8028b158f2be7fff5778ceffe5f2730dbccad5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f55c1460c22161d1ab7c75461da57476b6aee19abb8041132ac6b33893b9711616852cd553aa606929e732b8584f296b5df92d049905c609d2a2599df991ad9
|
7
|
+
data.tar.gz: 0f7dc9a51dbf361dc519e47c1fdb0fd1161326ace7fd78a253813c751ec007c8c07641967bf497d24e4e0fd535cb23d51a7af62d3abf57bf1704a8a26b75b65d
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 0.0.10
|
2
|
+
|
3
|
+
* `Flow` is now `Railway`.
|
4
|
+
* Any `Right` subclass will now be interpreted as success.
|
5
|
+
* Add `fail!`, `fail_fast!`, `pass!`, and `pass_fast!`.
|
6
|
+
* The only semi-public method to modify the pipe is `Railway#add`
|
7
|
+
* Removed `&`, `>`, `<` and `%` "operators" in favor of `#add`.
|
8
|
+
* Extremely simplified the macro API. Macros now return a callable step with the interface `->(input, options)` and their pipe options, e.g. `[ step, name: "my.macro"]`.
|
9
|
+
|
1
10
|
## 0.0.9
|
2
11
|
|
3
12
|
Removing `Operation::consider`, which is now `step`.
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -9,8 +9,10 @@ Rake::TestTask.new(:test) do |test|
|
|
9
9
|
|
10
10
|
test_files = FileList['test/*_test.rb']
|
11
11
|
|
12
|
-
if
|
13
|
-
test_files = test_files - %w{test/dry_container_test.rb test/2.0.0-pipetree_test.rb}
|
12
|
+
if RUBY_VERSION == "1.9.3"
|
13
|
+
test_files = test_files - %w{test/dry_container_test.rb test/2.1.0-pipetree_test.rb test/2.0.0-pipetree_test.rb}
|
14
|
+
elsif RUBY_VERSION == "2.0.0"
|
15
|
+
test_files = test_files - %w{test/dry_container_test.rb test/2.1.0-pipetree_test.rb}
|
14
16
|
end
|
15
17
|
|
16
18
|
test.test_files = test_files
|
@@ -10,14 +10,14 @@ module Trailblazer
|
|
10
10
|
extend Declarative::Heritage::Inherited
|
11
11
|
extend Declarative::Heritage::DSL
|
12
12
|
|
13
|
-
extend Skill::Accessors
|
13
|
+
extend Skill::Accessors # ::[] and ::[]=
|
14
14
|
|
15
|
-
include Pipetree
|
15
|
+
include Pipetree # ::call, ::step, ...
|
16
16
|
# we want the skill dependency-mechanism.
|
17
|
-
extend Skill::Call
|
17
|
+
extend Skill::Call # ::call(params: {}, current_user: ..)
|
18
18
|
extend Skill::Call::Positional # ::call(params, options)
|
19
19
|
|
20
20
|
# we want the initializer and the ::call method.
|
21
|
-
include Generic
|
21
|
+
include Generic # #initialize, #call, #process.
|
22
22
|
end
|
23
23
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require "pipetree"
|
2
|
-
require "pipetree/
|
2
|
+
require "pipetree/railway"
|
3
3
|
require "trailblazer/operation/result"
|
4
4
|
require "uber/option"
|
5
5
|
|
@@ -14,14 +14,13 @@ class Trailblazer::Operation
|
|
14
14
|
|
15
15
|
# Implements the API to populate the operation's pipetree and
|
16
16
|
# `Operation::call` to invoke the latter.
|
17
|
-
#
|
17
|
+
# Learn more about the Pipetree gem here: https://github.com/apotonick/pipetree
|
18
18
|
module Pipetree
|
19
19
|
def self.included(includer)
|
20
20
|
includer.extend ClassMethods # ::call, ::inititalize_pipetree!
|
21
21
|
includer.extend DSL # ::|, ::> and friends.
|
22
22
|
|
23
23
|
includer.initialize_pipetree!
|
24
|
-
includer._insert(:>>, New, name: "operation.new", wrap: false)
|
25
24
|
end
|
26
25
|
|
27
26
|
module ClassMethods
|
@@ -32,104 +31,112 @@ class Trailblazer::Operation
|
|
32
31
|
|
33
32
|
last, operation = pipe.(self, options)
|
34
33
|
|
35
|
-
#
|
36
|
-
|
37
|
-
# before op instantiation, this would be confusing (and wrong!).
|
38
|
-
Result.new(last == ::Pipetree::Flow::Right, options)
|
34
|
+
# Any subclass of Right will be interpreted as successful.
|
35
|
+
Result.new(!!(last <= Railway::Right), options)
|
39
36
|
end
|
40
37
|
|
41
38
|
# This method would be redundant if Ruby had a Class::finalize! method the way
|
42
39
|
# Dry.RB provides it. It has to be executed with every subclassing.
|
43
40
|
def initialize_pipetree!
|
44
41
|
heritage.record :initialize_pipetree!
|
45
|
-
|
42
|
+
|
43
|
+
self["pipetree"] = Railway.new
|
44
|
+
|
45
|
+
strut = ->(last, input, options) { [last, New.(input, options)] } # first step in pipe.
|
46
|
+
self["pipetree"].add(Railway::Right, strut, name: "operation.new") # DISCUSS: using pipe API directly here. clever?
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
def &(*args); _insert(:&, *args) end
|
53
|
-
def <(*args); _insert(:<, *args) end
|
54
|
-
|
55
|
-
def |(cfg, user_options={})
|
56
|
-
DSL.import(self, self["pipetree"], cfg, user_options) &&
|
57
|
-
heritage.record(:|, cfg, user_options)
|
58
|
-
end
|
50
|
+
class Railway < ::Pipetree::Railway
|
51
|
+
FailFast = Class.new(Left)
|
52
|
+
PassFast = Class.new(Right)
|
59
53
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
54
|
+
def self.fail! ; Left end
|
55
|
+
def self.fail_fast!; FailFast end
|
56
|
+
def self.pass! ; Right end
|
57
|
+
def self.pass_fast!; PassFast end
|
58
|
+
end
|
65
59
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
60
|
+
# The Strut wrapping each step. Makes sure that Track signals are returned immediately.
|
61
|
+
class Switch < ::Pipetree::Railway::Strut
|
62
|
+
Decider = ->(result, config, *args) do
|
63
|
+
return result if result.is_a?(Class) && result <= Railway::Track # this might be pretty slow?
|
70
64
|
|
71
|
-
|
65
|
+
config[:decider_class].(result, config, *args) # e.g. And::Decider.(result, ..)
|
72
66
|
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Strut that doesn't evaluate the step's result but stays on `last` or configured :signal.
|
70
|
+
class Stay < ::Pipetree::Railway::Strut
|
71
|
+
Decider = ->(result, config, last, *) { config[:signal] || last }
|
72
|
+
end
|
73
|
+
|
74
|
+
module DSL
|
75
|
+
def success(*args); add(Railway::Right, Stay::Decider, *args) end
|
76
|
+
def failure(*args); add(Railway::Left, Stay::Decider, *args) end
|
77
|
+
def step(*args) ; add(Railway::Right, Railway::And::Decider, *args) end
|
73
78
|
|
74
|
-
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
if options[:wrap] == false
|
81
|
-
proc
|
82
|
-
else
|
83
|
-
Option::KW.(proc) do |type|
|
84
|
-
options[:name] ||= proc if type == :symbol
|
85
|
-
options[:name] ||= "#{kws[:definer_name]}:#{proc.source_location.last}" if proc.is_a? Proc if type == :proc
|
86
|
-
options[:name] ||= proc.class if type == :callable
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
pipe.send(operator, _proc, options) # ex: pipetree.> Validate, after: Model::Build
|
79
|
+
private
|
80
|
+
# Operation-level entry point.
|
81
|
+
def add(track, decider_class, proc, options={})
|
82
|
+
heritage.record(:add, track, decider_class, proc, options)
|
83
|
+
|
84
|
+
DSL.insert(self["pipetree"], track, decider_class, proc, options)
|
91
85
|
end
|
92
86
|
|
93
|
-
#
|
94
|
-
|
95
|
-
|
96
|
-
|
87
|
+
def self.insert(pipe, track, decider_class, proc, options={}) # TODO: make :name required arg.
|
88
|
+
_proc, options = proc.is_a?(Array) ? macro!(proc, options) : step!(proc, options)
|
89
|
+
|
90
|
+
options = options.merge(replace: options[:name]) if options[:override] # :override
|
91
|
+
strut_class, strut_options = AddOptions.(decider_class, options) # :fail_fast and friends.
|
97
92
|
|
98
|
-
|
99
|
-
|
93
|
+
pipe.add(track, strut_class.new(_proc, strut_options), options)
|
94
|
+
end
|
100
95
|
|
101
|
-
|
96
|
+
def self.macro!(proc, options)
|
97
|
+
_proc, macro_options = proc
|
102
98
|
|
103
|
-
|
99
|
+
[ _proc, macro_options.merge(options) ]
|
104
100
|
end
|
105
101
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
insert_options = options.merge(user_options)
|
113
|
-
|
114
|
-
# Inheritance: when the step is already defined in the pipe,
|
115
|
-
# simply replace it with the new.
|
116
|
-
if name = insert_options[:name]
|
117
|
-
insert_options[:replace] = name if pipetree.index(name)
|
118
|
-
end
|
119
|
-
|
120
|
-
pipetree.send operator, step, insert_options
|
102
|
+
def self.step!(proc, options)
|
103
|
+
name = ""
|
104
|
+
_proc = Option::KW.(proc) do |type|
|
105
|
+
name = proc if type == :symbol
|
106
|
+
name = "#{proc.source_location[0].split("/").last}:#{proc.source_location.last}" if proc.is_a? Proc if type == :proc
|
107
|
+
name = proc.class if type == :callable
|
121
108
|
end
|
109
|
+
|
110
|
+
[ _proc, { name: name }.merge(options) ]
|
122
111
|
end
|
123
112
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
[
|
113
|
+
AddOptions = ->(decider_class, options) do
|
114
|
+
# for #failure and #success:
|
115
|
+
if decider_class == Stay::Decider
|
116
|
+
return [Stay, signal: Railway::FailFast] if options[:fail_fast]
|
117
|
+
return [Stay, signal: Railway::PassFast] if options[:pass_fast]
|
118
|
+
return [Stay, {}]
|
119
|
+
else # for #step:
|
120
|
+
return [Switch, decider_class: decider_class, on_false: Railway::FailFast] if options[:fail_fast]
|
121
|
+
return [Switch, decider_class: decider_class, on_true: Railway::PassFast] if options[:pass_fast]
|
122
|
+
return [Switch, decider_class: decider_class]
|
129
123
|
end
|
130
124
|
end
|
131
125
|
end # DSL
|
132
126
|
end
|
133
127
|
|
134
|
-
|
128
|
+
# Allows defining dependencies and inject/override them via runtime options, if desired.
|
129
|
+
class Pipetree::Step
|
130
|
+
include Uber::Callable
|
131
|
+
|
132
|
+
def initialize(step, dependencies={})
|
133
|
+
@step, @dependencies = step, dependencies
|
134
|
+
end
|
135
|
+
|
136
|
+
def call(input, options)
|
137
|
+
@dependencies.each { |k, v| options[k] ||= v } # not sure i like this, but the step's API is cool.
|
138
|
+
|
139
|
+
@step.(input, options)
|
140
|
+
end
|
141
|
+
end
|
135
142
|
end
|
data/test/2.0.0-pipetree_test.rb
CHANGED
@@ -1,8 +1,27 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
+
class KWBugsTest < Minitest::Spec
|
4
|
+
Merchant = Struct.new(:id)
|
5
|
+
|
6
|
+
class Merchant::New < Trailblazer::Operation
|
7
|
+
step ->(options) {
|
8
|
+
options["model"] = 1
|
9
|
+
options["bla"] = true # this breaks Ruby 2.2.2.
|
10
|
+
}
|
11
|
+
|
12
|
+
step :add!
|
13
|
+
def add!(yo:nil, model:nil, **)
|
14
|
+
raise if model.nil?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it { Merchant::New.( {}, { "yo"=> nil } ) }
|
19
|
+
end
|
20
|
+
|
21
|
+
|
3
22
|
class Ruby200PipetreeTest < Minitest::Spec
|
4
23
|
class Create < Trailblazer::Operation
|
5
|
-
step ->(*, params
|
24
|
+
step ->(*, params:nil, **) { params["run"] } # only test kws.
|
6
25
|
step ->(options, params:nil, **) { options["x"] = params["run"] } # read and write.
|
7
26
|
step ->(options) { options["y"] = options["params"]["run"] } # old API.
|
8
27
|
end
|
@@ -16,7 +35,7 @@ class Ruby200PipetreeTest < Minitest::Spec
|
|
16
35
|
step :x! # read and write.
|
17
36
|
step :y! # old API.
|
18
37
|
|
19
|
-
def params!(*, params
|
38
|
+
def params!(*, params:nil, **)
|
20
39
|
params["run"]
|
21
40
|
end
|
22
41
|
|
@@ -36,7 +55,7 @@ class Ruby200PipetreeTest < Minitest::Spec
|
|
36
55
|
class Delete < Trailblazer::Operation
|
37
56
|
class Params
|
38
57
|
extend Uber::Callable
|
39
|
-
def self.call(*, params
|
58
|
+
def self.call(*, params:nil, **)
|
40
59
|
params["run"]
|
41
60
|
end
|
42
61
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class KWBugsTest < Minitest::Spec
|
4
|
+
Merchant = Struct.new(:id)
|
5
|
+
|
6
|
+
class Merchant::New < Trailblazer::Operation
|
7
|
+
step ->(options) {
|
8
|
+
options["model"] = 1
|
9
|
+
options["bla"] = true # this breaks Ruby 2.2.2.
|
10
|
+
}
|
11
|
+
|
12
|
+
step :add!
|
13
|
+
def add!(yo:, model:, **)
|
14
|
+
raise if model.nil?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it { Merchant::New.( {}, { "yo"=> nil } ) }
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class Ruby200PipetreeTest < Minitest::Spec
|
23
|
+
class Create < Trailblazer::Operation
|
24
|
+
step ->(*, params:, **) { params["run"] } # only test kws.
|
25
|
+
step ->(options, params:nil, **) { options["x"] = params["run"] } # read and write.
|
26
|
+
step ->(options) { options["y"] = options["params"]["run"] } # old API.
|
27
|
+
end
|
28
|
+
|
29
|
+
it { Create.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
|
30
|
+
it { Create.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
|
31
|
+
|
32
|
+
#- instance methods
|
33
|
+
class Update < Trailblazer::Operation
|
34
|
+
step :params! # only test kws.
|
35
|
+
step :x! # read and write.
|
36
|
+
step :y! # old API.
|
37
|
+
|
38
|
+
def params!(*, params:, **)
|
39
|
+
params["run"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def x!(options, params:nil, **)
|
43
|
+
options["x"] = params["run"]
|
44
|
+
end
|
45
|
+
|
46
|
+
def y!(options)
|
47
|
+
options["y"] = options["params"]["run"]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it { Update.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
|
52
|
+
it { Update.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
|
53
|
+
|
54
|
+
|
55
|
+
class Delete < Trailblazer::Operation
|
56
|
+
class Params
|
57
|
+
extend Uber::Callable
|
58
|
+
def self.call(*, params:, **)
|
59
|
+
params["run"]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class X
|
64
|
+
extend Uber::Callable
|
65
|
+
def self.call(options, params:nil, **)
|
66
|
+
options["x"] = params["run"]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Y
|
71
|
+
extend Uber::Callable
|
72
|
+
def self.call(options)
|
73
|
+
options["y"] = options["params"]["run"]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
step Params
|
78
|
+
step X
|
79
|
+
step Y
|
80
|
+
end
|
81
|
+
|
82
|
+
it { Delete.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
|
83
|
+
it { Delete.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
|
84
|
+
end
|
data/test/call_test.rb
CHANGED
@@ -11,14 +11,14 @@ class CallTest < Minitest::Spec
|
|
11
11
|
|
12
12
|
it { Create.().must_be_instance_of Trailblazer::Operation::Result }
|
13
13
|
|
14
|
-
it { Create.({}).inspect.must_equal %{<Result:true <Skill {} {\"params\"=>{}} {\"pipetree\"=>[
|
15
|
-
it { Create.(name: "Jacob").inspect.must_equal %{<Result:true <Skill {} {\"params\"=>{:name=>\"Jacob\"}} {\"pipetree\"=>[
|
16
|
-
it { Create.({ name: "Jacob" }, { policy: Object }).inspect.must_equal %{<Result:true <Skill {} {:policy=>Object, \"params\"=>{:name=>\"Jacob\"}} {\"pipetree\"=>[
|
14
|
+
it { Create.({}).inspect.must_equal %{<Result:true <Skill {} {\"params\"=>{}} {\"pipetree\"=>[>operation.new]}> >} }
|
15
|
+
it { Create.(name: "Jacob").inspect.must_equal %{<Result:true <Skill {} {\"params\"=>{:name=>\"Jacob\"}} {\"pipetree\"=>[>operation.new]}> >} }
|
16
|
+
it { Create.({ name: "Jacob" }, { policy: Object }).inspect.must_equal %{<Result:true <Skill {} {:policy=>Object, \"params\"=>{:name=>\"Jacob\"}} {\"pipetree\"=>[>operation.new]}> >} }
|
17
17
|
|
18
18
|
#---
|
19
19
|
# success?
|
20
20
|
class Update < Trailblazer::Operation
|
21
|
-
|
21
|
+
step ->(options) { options["params"] }, after: "operation.new"
|
22
22
|
end
|
23
23
|
|
24
24
|
it { Update.(true).success?.must_equal true }
|
@@ -1,9 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../../
|
3
3
|
specs:
|
4
|
-
trailblazer-operation (0.0.
|
4
|
+
trailblazer-operation (0.0.10)
|
5
5
|
declarative
|
6
|
-
pipetree (>= 0.0
|
6
|
+
pipetree (>= 0.1.0, < 0.2.0)
|
7
7
|
uber (>= 0.1.0, < 0.2.0)
|
8
8
|
|
9
9
|
GEM
|
@@ -12,7 +12,7 @@ GEM
|
|
12
12
|
declarative (0.0.8)
|
13
13
|
uber (>= 0.0.15)
|
14
14
|
minitest (5.10.1)
|
15
|
-
pipetree (0.0
|
15
|
+
pipetree (0.1.0)
|
16
16
|
uber
|
17
17
|
rake (12.0.0)
|
18
18
|
uber (0.1.0)
|
@@ -24,7 +24,6 @@ DEPENDENCIES
|
|
24
24
|
bundler
|
25
25
|
declarative
|
26
26
|
minitest
|
27
|
-
pipetree
|
28
27
|
rake
|
29
28
|
trailblazer-operation!
|
30
29
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../../
|
3
|
+
specs:
|
4
|
+
trailblazer-operation (0.0.9)
|
5
|
+
declarative
|
6
|
+
pipetree (>= 0.0.5, < 0.1.0)
|
7
|
+
uber (>= 0.1.0, < 0.2.0)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
concurrent-ruby (1.0.2)
|
13
|
+
declarative (0.0.8)
|
14
|
+
uber (>= 0.0.15)
|
15
|
+
dry-configurable (0.4.0)
|
16
|
+
concurrent-ruby (~> 1.0)
|
17
|
+
dry-container (0.6.0)
|
18
|
+
concurrent-ruby (~> 1.0)
|
19
|
+
dry-configurable (~> 0.1, >= 0.1.3)
|
20
|
+
minitest (5.10.1)
|
21
|
+
pipetree (0.0.5)
|
22
|
+
uber
|
23
|
+
rake (12.0.0)
|
24
|
+
uber (0.1.0)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
bundler
|
31
|
+
declarative
|
32
|
+
dry-container
|
33
|
+
minitest
|
34
|
+
pipetree
|
35
|
+
rake
|
36
|
+
trailblazer-operation!
|
37
|
+
|
38
|
+
BUNDLED WITH
|
39
|
+
1.12.5
|
@@ -16,7 +16,7 @@ class OperationCompetenceTest < Minitest::Spec
|
|
16
16
|
# Operation#[]=
|
17
17
|
# arbitrary options can be saved via Op#[].
|
18
18
|
class Create < Trailblazer::Operation
|
19
|
-
|
19
|
+
success :call
|
20
20
|
|
21
21
|
def call(*)
|
22
22
|
self["drink"] = "Little Creatures"
|
@@ -37,7 +37,7 @@ class OperationCompetenceTest < Minitest::Spec
|
|
37
37
|
# Operation::[]
|
38
38
|
# Operation::[]=
|
39
39
|
class Update < Trailblazer::Operation
|
40
|
-
|
40
|
+
success :call
|
41
41
|
|
42
42
|
self["drink"] = "Beer"
|
43
43
|
|
@@ -56,7 +56,7 @@ class OperationCompetenceTest < Minitest::Spec
|
|
56
56
|
|
57
57
|
# instance can override class-level
|
58
58
|
class Delete < Trailblazer::Operation
|
59
|
-
|
59
|
+
success :call
|
60
60
|
|
61
61
|
self["drink"] = "Beer"
|
62
62
|
|
data/test/pipetree_test.rb
CHANGED
@@ -1,34 +1,28 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
3
|
class PipetreeTest < Minitest::Spec
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
def self.Validate
|
5
|
+
step = ->{ snippet }
|
6
|
+
|
7
|
+
[ step, name: "validate", before: "operation.new" ]
|
8
8
|
end
|
9
9
|
|
10
|
-
#---
|
11
|
-
# ::|
|
12
10
|
# without options
|
13
11
|
class Create < Trailblazer::Operation
|
14
|
-
step
|
12
|
+
step PipetreeTest::Validate()
|
13
|
+
step PipetreeTest::Validate(), name: "VALIDATE!"
|
15
14
|
end
|
16
15
|
|
17
|
-
it { Create["pipetree"].inspect.must_equal %{[>validate
|
16
|
+
it { Create["pipetree"].inspect.must_equal %{[>validate,>VALIDATE!,>operation.new]} }
|
18
17
|
|
19
|
-
# without any options or []
|
20
|
-
# class New < Trailblazer::Operation
|
21
|
-
# step Validate
|
22
|
-
# end
|
23
18
|
|
24
|
-
# it { New["pipetree"].inspect.must_equal %{[>validate,>>operation.new]} }
|
25
19
|
|
26
20
|
# with options
|
27
21
|
class Update < Trailblazer::Operation
|
28
|
-
step
|
22
|
+
step PipetreeTest::Validate(), after: "operation.new"
|
29
23
|
end
|
30
24
|
|
31
|
-
it { Update["pipetree"].inspect.must_equal %{[
|
25
|
+
it { Update["pipetree"].inspect.must_equal %{[>operation.new,>validate]} }
|
32
26
|
|
33
27
|
#---
|
34
28
|
# ::step
|
@@ -63,41 +57,129 @@ class PipetreeTest < Minitest::Spec
|
|
63
57
|
# with proc, method, callable.
|
64
58
|
class Right < Trailblazer::Operation
|
65
59
|
MyProc = ->(*) { }
|
66
|
-
|
60
|
+
success ->(options) { options[">"] = options["params"][:id] }, better_api: true
|
67
61
|
|
68
|
-
|
62
|
+
success :method_name!
|
69
63
|
def method_name!(options); self["method_name!"] = options["params"][:id] end
|
70
64
|
|
71
65
|
class MyCallable
|
72
66
|
include Uber::Callable
|
73
67
|
def call(options); options["callable"] = options["params"][:id] end
|
74
68
|
end
|
75
|
-
|
69
|
+
success MyCallable.new
|
76
70
|
end
|
77
71
|
|
78
72
|
it { Right.( id: 1 ).slice(">", "method_name!", "callable").must_equal [1, 1, 1] }
|
79
|
-
it { Right["pipetree"].inspect.must_equal %{[
|
73
|
+
it { Right["pipetree"].inspect.must_equal %{[>operation.new,>pipetree_test.rb:60,>method_name!,>PipetreeTest::Right::MyCallable]} }
|
80
74
|
|
81
75
|
#---
|
82
76
|
# inheritance
|
83
77
|
class Righter < Right
|
84
|
-
|
78
|
+
success ->(options) { options["righter"] = true }
|
85
79
|
end
|
86
80
|
|
87
81
|
it { Righter.( id: 1 ).slice(">", "method_name!", "callable", "righter").must_equal [1, 1, 1, true] }
|
88
82
|
end
|
89
83
|
|
90
|
-
#---
|
91
|
-
#- kw args
|
92
|
-
class OperationKwArgsTest < Minitest::Spec
|
93
|
-
Song = Struct.new(:id)
|
94
84
|
|
85
|
+
class FailPassFastOptionTest < Minitest::Spec
|
86
|
+
# #failure fails fast.
|
87
|
+
class Create < Trailblazer::Operation
|
88
|
+
step ->(options, *) { options["x"] = options["dont_fail"] }
|
89
|
+
failure ->(options, *) { options["a"] = true; options["fail_fast"] }, fail_fast: true
|
90
|
+
failure ->(options, *) { options["b"] = true }
|
91
|
+
step ->(options, *) { options["y"] = true }
|
92
|
+
end
|
93
|
+
|
94
|
+
it { Create.({}, "fail_fast" => true, "dont_fail" => true ).inspect("x", "a", "b", "y").must_equal %{<Result:true [true, nil, nil, true] >} }
|
95
|
+
it { Create.({}, "fail_fast" => true ).inspect("x", "a", "b", "y").must_equal %{<Result:false [nil, true, nil, nil] >} }
|
96
|
+
it { Create.({}, "fail_fast" => false ).inspect("x", "a", "b", "y").must_equal %{<Result:false [nil, true, nil, nil] >} }
|
97
|
+
|
98
|
+
# #success passes fast.
|
99
|
+
class Retrieve < Trailblazer::Operation
|
100
|
+
success ->(options, *) { options["x"] = options["dont_fail"] }, pass_fast: true
|
101
|
+
failure ->(options, *) { options["b"] = true }
|
102
|
+
step ->(options, *) { options["y"] = true }
|
103
|
+
end
|
104
|
+
|
105
|
+
it { Retrieve.({}, "dont_fail" => true ).inspect("x", "b", "y").must_equal %{<Result:true [true, nil, nil] >} }
|
106
|
+
it { Retrieve.({}, "dont_fail" => false ).inspect("x", "b", "y").must_equal %{<Result:true [false, nil, nil] >} }
|
107
|
+
|
108
|
+
# #step fails fast if option set and returns false.
|
109
|
+
class Update < Trailblazer::Operation
|
110
|
+
step ->(options, *) { options["x"] = true }
|
111
|
+
step ->(options, *) { options["a"] = options["dont_fail"] }, fail_fast: true # only on false.
|
112
|
+
failure ->(options, *) { options["b"] = true }
|
113
|
+
step ->(options, *) { options["y"] = true }
|
114
|
+
end
|
115
|
+
|
116
|
+
it { Update.({}, "dont_fail" => true).inspect("x", "a", "b", "y").must_equal %{<Result:true [true, true, nil, true] >} }
|
117
|
+
it { Update.({} ).inspect("x", "a", "b", "y").must_equal %{<Result:false [true, nil, nil, nil] >} }
|
118
|
+
|
119
|
+
# #step passes fast if option set and returns true.
|
120
|
+
class Delete < Trailblazer::Operation
|
121
|
+
step ->(options, *) { options["x"] = true }
|
122
|
+
step ->(options, *) { options["a"] = options["dont_fail"] }, pass_fast: true # only on true.
|
123
|
+
failure ->(options, *) { options["b"] = true }
|
124
|
+
step ->(options, *) { options["y"] = true }
|
125
|
+
end
|
126
|
+
|
127
|
+
it { Delete.({}, "dont_fail" => true).inspect("x", "a", "b", "y").must_equal %{<Result:true [true, true, nil, nil] >} }
|
128
|
+
it { Delete.({} ).inspect("x", "a", "b", "y").must_equal %{<Result:false [true, nil, true, nil] >} }
|
129
|
+
end
|
130
|
+
|
131
|
+
class FailBangTest < Minitest::Spec
|
132
|
+
class Create < Trailblazer::Operation
|
133
|
+
step ->(options, *) { options["x"] = true; Railway.fail! }
|
134
|
+
step ->(options, *) { options["y"] = true }
|
135
|
+
failure ->(options, *) { options["a"] = true }
|
136
|
+
end
|
137
|
+
|
138
|
+
it { Create.().inspect("x", "y", "a").must_equal %{<Result:false [true, nil, true] >} }
|
139
|
+
end
|
140
|
+
|
141
|
+
class PassBangTest < Minitest::Spec
|
142
|
+
class Create < Trailblazer::Operation
|
143
|
+
step ->(options, *) { options["x"] = true; Railway.pass! }
|
144
|
+
step ->(options, *) { options["y"] = true }
|
145
|
+
failure ->(options, *) { options["a"] = true }
|
146
|
+
end
|
147
|
+
|
148
|
+
it { Create.().inspect("x", "y", "a").must_equal %{<Result:true [true, true, nil] >} }
|
149
|
+
end
|
150
|
+
|
151
|
+
class FailFastBangTest < Minitest::Spec
|
95
152
|
class Create < Trailblazer::Operation
|
96
|
-
|
97
|
-
|
153
|
+
step ->(options, *) { options["x"] = true; Railway.fail_fast! }
|
154
|
+
step ->(options, *) { options["y"] = true }
|
155
|
+
failure ->(options, *) { options["a"] = true }
|
156
|
+
end
|
157
|
+
|
158
|
+
it { Create.().inspect("x", "y", "a").must_equal %{<Result:false [true, nil, nil] >} }
|
159
|
+
end
|
160
|
+
|
161
|
+
class PassFastBangTest < Minitest::Spec
|
162
|
+
class Create < Trailblazer::Operation
|
163
|
+
step ->(options, *) { options["x"] = true; Railway.pass_fast! }
|
164
|
+
step ->(options, *) { options["y"] = true }
|
165
|
+
failure ->(options, *) { options["a"] = true }
|
166
|
+
end
|
167
|
+
|
168
|
+
it { Create.().inspect("x", "y", "a").must_equal %{<Result:true [true, nil, nil] >} }
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
class OverrideTest < Minitest::Spec
|
173
|
+
class Create < Trailblazer::Operation
|
174
|
+
step :a
|
175
|
+
step :b
|
176
|
+
end
|
177
|
+
|
178
|
+
class Update < Create
|
179
|
+
step :a, override: true
|
98
180
|
end
|
99
181
|
|
100
|
-
|
101
|
-
|
102
|
-
|
182
|
+
# FIXME: also test Macro
|
183
|
+
it { Create["pipetree"].inspect.must_equal %{[>operation.new,>a,>b]} }
|
184
|
+
it { Update["pipetree"].inspect.must_equal %{[>operation.new,>a,>b]} }
|
103
185
|
end
|
data/test/result_test.rb
CHANGED
@@ -17,7 +17,7 @@ class ResultTest < Minitest::Spec
|
|
17
17
|
it { Result.new(true, "x"=> true, "y"=>1, "z"=>2).inspect("z", "y").must_equal %{<Result:true [2, 1] >} }
|
18
18
|
|
19
19
|
class Create < Trailblazer::Operation
|
20
|
-
|
20
|
+
success :call
|
21
21
|
|
22
22
|
def call(*)
|
23
23
|
self[:message] = "Result objects are actually quite handy!"
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ["Nick Sutterer"]
|
9
9
|
spec.email = ["apotonick@gmail.com"]
|
10
10
|
spec.description = %q{Trailblazer's operation object.}
|
11
|
-
spec.summary = %q{Trailblazer's operation object with
|
11
|
+
spec.summary = %q{Trailblazer's operation object with railway flow and integrated error handling.}
|
12
12
|
spec.homepage = "http://trailblazer.to"
|
13
13
|
spec.license = "MIT"
|
14
14
|
|
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency "uber", ">= 0.1.0", "< 0.2.0"
|
22
22
|
spec.add_dependency "declarative"
|
23
|
-
spec.add_dependency "pipetree", ">= 0.0
|
23
|
+
spec.add_dependency "pipetree", ">= 0.1.0", "< 0.2.0"
|
24
24
|
|
25
25
|
spec.add_development_dependency "bundler"
|
26
26
|
spec.add_development_dependency "rake"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer-operation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: uber
|
@@ -50,20 +50,20 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 0.0
|
53
|
+
version: 0.1.0
|
54
54
|
- - "<"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: 0.
|
56
|
+
version: 0.2.0
|
57
57
|
type: :runtime
|
58
58
|
prerelease: false
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
61
|
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: 0.0
|
63
|
+
version: 0.1.0
|
64
64
|
- - "<"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: 0.
|
66
|
+
version: 0.2.0
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: bundler
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,11 +129,14 @@ files:
|
|
129
129
|
- lib/trailblazer/operation/version.rb
|
130
130
|
- lib/trailblazer/skill.rb
|
131
131
|
- test/2.0.0-pipetree_test.rb
|
132
|
+
- test/2.1.0-pipetree_test.rb
|
132
133
|
- test/benchmark/skill_resolver_benchmark.rb
|
133
134
|
- test/call_test.rb
|
134
135
|
- test/dry_container_test.rb
|
135
136
|
- test/gemfiles/Gemfile.ruby-1.9
|
136
137
|
- test/gemfiles/Gemfile.ruby-1.9.lock
|
138
|
+
- test/gemfiles/Gemfile.ruby-2.0
|
139
|
+
- test/gemfiles/Gemfile.ruby-2.0.lock
|
137
140
|
- test/gemfiles/Gemfile.ruby-2.3
|
138
141
|
- test/gemfiles/Gemfile.ruby-2.3.lock
|
139
142
|
- test/inheritance_test.rb
|
@@ -166,14 +169,17 @@ rubyforge_project:
|
|
166
169
|
rubygems_version: 2.6.3
|
167
170
|
signing_key:
|
168
171
|
specification_version: 4
|
169
|
-
summary: Trailblazer's operation object with
|
172
|
+
summary: Trailblazer's operation object with railway flow and integrated error handling.
|
170
173
|
test_files:
|
171
174
|
- test/2.0.0-pipetree_test.rb
|
175
|
+
- test/2.1.0-pipetree_test.rb
|
172
176
|
- test/benchmark/skill_resolver_benchmark.rb
|
173
177
|
- test/call_test.rb
|
174
178
|
- test/dry_container_test.rb
|
175
179
|
- test/gemfiles/Gemfile.ruby-1.9
|
176
180
|
- test/gemfiles/Gemfile.ruby-1.9.lock
|
181
|
+
- test/gemfiles/Gemfile.ruby-2.0
|
182
|
+
- test/gemfiles/Gemfile.ruby-2.0.lock
|
177
183
|
- test/gemfiles/Gemfile.ruby-2.3
|
178
184
|
- test/gemfiles/Gemfile.ruby-2.3.lock
|
179
185
|
- test/inheritance_test.rb
|