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 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