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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d379cb34eaae932a9888c234de9f852e4793f035
4
- data.tar.gz: a3ff9e0af8fe676ea60713c7c6946efdad2c3863
3
+ metadata.gz: d0302dd8b35ae2d64dd482bbc330604e5c55b65c
4
+ data.tar.gz: fb8028b158f2be7fff5778ceffe5f2730dbccad5
5
5
  SHA512:
6
- metadata.gz: e1f26f8663297e30f8bd36893d8d853c15232841eb47f36e265683b787203672f714af0ae84096da7a3ec75f91ed4427fadce578a4ee31333d3377dd631e81c0
7
- data.tar.gz: 7be6cb8f3e07df31a64c3f3b9e2f11cfc4be6a020641b0cf0a4bf0ecccb654dbd92845fce8f015cc28575e7fe67056d465fd54fffb56c3dc310497b41217a408
6
+ metadata.gz: 7f55c1460c22161d1ab7c75461da57476b6aee19abb8041132ac6b33893b9711616852cd553aa606929e732b8584f296b5df92d049905c609d2a2599df991ad9
7
+ data.tar.gz: 0f7dc9a51dbf361dc519e47c1fdb0fd1161326ace7fd78a253813c751ec007c8c07641967bf497d24e4e0fd535cb23d51a7af62d3abf57bf1704a8a26b75b65d
@@ -5,7 +5,11 @@ matrix:
5
5
  include:
6
6
  - rvm: 1.9.3
7
7
  gemfile: "test/gemfiles/Gemfile.ruby-1.9"
8
+ # - rvm: 2.0.0
9
+ # gemfile: "test/gemfiles/Gemfile.ruby-2.0"
8
10
  - rvm: 2.1
9
11
  gemfile: Gemfile
12
+ - rvm: 2.2.3
13
+ gemfile: Gemfile
10
14
  - rvm: 2.3.1
11
15
  gemfile: Gemfile
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
@@ -10,3 +10,4 @@ gem "dry-auto_inject"
10
10
  gem "minitest-line"
11
11
  gem "benchmark-ips"
12
12
  # gem "pipetree", path: "../pipetree"
13
+ # gem "pipetree", github: "apotonick/pipetree"
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 RUBY_VERSION == "1.9.3"
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 # ::[] and ::[]=
13
+ extend Skill::Accessors # ::[] and ::[]=
14
14
 
15
- include Pipetree # ::call, ::|
15
+ include Pipetree # ::call, ::step, ...
16
16
  # we want the skill dependency-mechanism.
17
- extend Skill::Call # ::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 # #initialize, #call, #process.
21
+ include Generic # #initialize, #call, #process.
22
22
  end
23
23
  end
@@ -1,5 +1,5 @@
1
1
  require "pipetree"
2
- require "pipetree/flow"
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
- # http://trailblazer.to/gems/operation/2.0/pipetree.html
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
- # The reason the Result wraps the Skill object (`options`), not the operation
36
- # itself is because the op should be irrelevant, plus when stopping the pipe
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
- self["pipetree"] = ::Pipetree::Flow.new
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
- module DSL
50
- # They all inherit.
51
- def >(*args); _insert(:>, *args) end
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
- alias_method :step, :|
61
- alias_method :failure, :<
62
- alias_method :success, :>
63
- alias_method :override, :|
64
- alias_method :~, :override
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
- # :private:
67
- # High-level user step API that allows ->(options) procs.
68
- def _insert(operator, proc, options={})
69
- heritage.record(:_insert, operator, proc, options)
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
- DSL.insert(self["pipetree"], operator, proc, options, definer_name: self.name)
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
- # :public:
75
- # Wrap the step into a proc that only passes `options` to the step.
76
- # This is pure convenience for the developer and will be the default
77
- # API for steps. ATM, we also automatically generate a step `:name`.
78
- def self.insert(pipe, operator, proc, options={}, kws={}) # TODO: definer_name is a hack for debugging, only.
79
- _proc =
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
- # note: does not calls heritage.record
94
- def self.import(operation, pipe, cfg, user_options={})
95
- # a normal step is added as "consider"/"may deviate", so its result matters.
96
- return insert(pipe, :&, cfg, user_options, {}) unless cfg.is_a?(Array)
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
- # e.g. from Contract::Validate
99
- mod, args, block = cfg
93
+ pipe.add(track, strut_class.new(_proc, strut_options), options)
94
+ end
100
95
 
101
- import = Import.new(pipe, user_options) # API object.
96
+ def self.macro!(proc, options)
97
+ _proc, macro_options = proc
102
98
 
103
- mod.import!(operation, import, *args, &block)
99
+ [ _proc, macro_options.merge(options) ]
104
100
  end
105
101
 
106
- # Try to abstract as much as possible from the imported module. This is for
107
- # forward-compatibility.
108
- # Note that Import#call will push the step directly on the pipetree which gives it the
109
- # low-level (input, options) interface.
110
- Import = Struct.new(:pipetree, :user_options) do
111
- def call(operator, step, options)
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
- Macros = Module.new
125
- # create a class method on `target`, e.g. Contract::Validate() for step macros.
126
- def self.macro!(name, constant, target=Macros)
127
- target.send :define_method, name do |*args, &block|
128
- [constant, args, block]
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
- extend Pipetree::DSL::Macros
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
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Operation
3
- VERSION = "0.0.9"
3
+ VERSION = "0.0.10"
4
4
  end
5
5
  end
@@ -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:, **) { params["run"] } # only test kws.
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
@@ -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\"=>[>>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]}> >} }
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
- self.& ->(options) { options["params"] }, after: "operation.new"
21
+ step ->(options) { options["params"] }, after: "operation.new"
22
22
  end
23
23
 
24
24
  it { Update.(true).success?.must_equal true }
@@ -2,4 +2,4 @@ source 'https://rubygems.org'
2
2
  gemspec path: "../../"
3
3
 
4
4
  gem "declarative"
5
- gem "pipetree"
5
+ # gem "pipetree", github: "apotonick/pipetree"
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: ../../
3
3
  specs:
4
- trailblazer-operation (0.0.8)
4
+ trailblazer-operation (0.0.10)
5
5
  declarative
6
- pipetree (>= 0.0.5, < 0.1.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.5)
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,6 @@
1
+ source 'https://rubygems.org'
2
+ gemspec path: "../../"
3
+
4
+ gem "declarative"
5
+ gem "pipetree"
6
+ gem "dry-container"
@@ -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
- self.> :call
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
- self.> :call
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
- self.> :call
59
+ success :call
60
60
 
61
61
  self["drink"] = "Beer"
62
62
 
@@ -1,34 +1,28 @@
1
1
  require "test_helper"
2
2
 
3
3
  class PipetreeTest < Minitest::Spec
4
- module Validate
5
- def self.import!(operation, pipe)
6
- pipe.(:>, ->{ snippet }, name: "validate", before: "operation.new")
7
- end
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 [Validate]
12
+ step PipetreeTest::Validate()
13
+ step PipetreeTest::Validate(), name: "VALIDATE!"
15
14
  end
16
15
 
17
- it { Create["pipetree"].inspect.must_equal %{[>validate,>>operation.new]} }
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 [Validate], after: "operation.new"
22
+ step PipetreeTest::Validate(), after: "operation.new"
29
23
  end
30
24
 
31
- it { Update["pipetree"].inspect.must_equal %{[>>operation.new,>validate]} }
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
- self.> ->(options) { options[">"] = options["params"][:id] }, better_api: true
60
+ success ->(options) { options[">"] = options["params"][:id] }, better_api: true
67
61
 
68
- self.> :method_name!
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
- self.> MyCallable.new
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 %{[>>operation.new,>PipetreeTest::Right:66,>method_name!,>PipetreeTest::Right::MyCallable]} }
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
- self.> ->(options) { options["righter"] = true }
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
- self.> ->(options) { options["model"] = "Object" }
97
- # self.> ->(model:) { snippet }
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
- it {
101
- skip
102
- Create.() }
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
@@ -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
- self.> :call
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 dependency management and pipetree flow.}
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.5", "< 0.1.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.9
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: 2016-12-13 00:00:00.000000000 Z
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.5
53
+ version: 0.1.0
54
54
  - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: 0.1.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.5
63
+ version: 0.1.0
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: 0.1.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 dependency management and pipetree flow.
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