trailblazer-operation 0.0.9 → 0.0.10
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/.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
|