trailblazer-operation 0.0.13 → 0.1.1

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -3
  3. data/CHANGES.md +44 -0
  4. data/Gemfile +13 -2
  5. data/Rakefile +8 -6
  6. data/lib/trailblazer/operation.rb +86 -12
  7. data/lib/trailblazer/operation/deprecated_macro.rb +19 -0
  8. data/lib/trailblazer/operation/inject.rb +34 -0
  9. data/lib/trailblazer/operation/inspect.rb +80 -0
  10. data/lib/trailblazer/operation/public_call.rb +62 -0
  11. data/lib/trailblazer/operation/railway.rb +32 -0
  12. data/lib/trailblazer/operation/railway/fast_track.rb +13 -0
  13. data/lib/trailblazer/operation/railway/normalizer.rb +73 -0
  14. data/lib/trailblazer/operation/railway/task_builder.rb +44 -0
  15. data/lib/trailblazer/operation/result.rb +6 -4
  16. data/lib/trailblazer/operation/skill.rb +8 -24
  17. data/lib/trailblazer/operation/task_wrap.rb +68 -0
  18. data/lib/trailblazer/operation/trace.rb +49 -0
  19. data/lib/trailblazer/operation/variable_mapping.rb +91 -0
  20. data/lib/trailblazer/operation/version.rb +1 -1
  21. data/test/call_test.rb +27 -8
  22. data/test/class_dependencies_test.rb +16 -0
  23. data/test/docs/doormat_test.rb +189 -0
  24. data/test/docs/wiring_test.rb +421 -0
  25. data/test/dry_container_test.rb +4 -0
  26. data/test/fast_track_test.rb +197 -0
  27. data/test/gemfiles/Gemfile.ruby-2.0 +1 -2
  28. data/test/gemfiles/Gemfile.ruby-2.0.lock +40 -0
  29. data/test/inheritance_test.rb +1 -1
  30. data/test/inspect_test.rb +43 -0
  31. data/test/introspect_test.rb +50 -0
  32. data/test/macro_test.rb +61 -0
  33. data/test/operation_test.rb +94 -0
  34. data/test/result_test.rb +14 -8
  35. data/test/ruby-2.0.0/operation_test.rb +73 -0
  36. data/test/ruby-2.0.0/step_test.rb +136 -0
  37. data/test/skill_test.rb +66 -48
  38. data/test/step_test.rb +228 -0
  39. data/test/task_wrap_test.rb +91 -0
  40. data/test/test_helper.rb +37 -0
  41. data/test/trace_test.rb +62 -0
  42. data/test/variable_mapping_test.rb +66 -0
  43. data/test/wire_test.rb +113 -0
  44. data/test/wiring/defaults_test.rb +197 -0
  45. data/test/wiring/subprocess_test.rb +70 -0
  46. data/trailblazer-operation.gemspec +3 -5
  47. metadata +62 -36
  48. data/lib/trailblazer/operation/1.9.3/option.rb +0 -36
  49. data/lib/trailblazer/operation/generic.rb +0 -12
  50. data/lib/trailblazer/operation/option.rb +0 -54
  51. data/lib/trailblazer/operation/pipetree.rb +0 -142
  52. data/lib/trailblazer/skill.rb +0 -70
  53. data/test/2.0.0-pipetree_test.rb +0 -100
  54. data/test/2.1.0-pipetree_test.rb +0 -100
  55. data/test/operation_skill_test.rb +0 -89
  56. data/test/pipetree_test.rb +0 -185
@@ -0,0 +1,70 @@
1
+ # require "test_helper"
2
+
3
+ # class WiringWithNestedTest < Minitest::Spec
4
+ # MyEnd = Class.new(Trailblazer::Circuit::End)
5
+
6
+ # class Create < Trailblazer::Operation
7
+ # class Edit < Trailblazer::Operation
8
+ # step :a
9
+
10
+ # def a(options, a_return:, **)
11
+ # options["a"] = 1
12
+ # a_return
13
+ # end
14
+
15
+ # # operations should expose their outputs with proper naming.
16
+ # #
17
+ # # {#<Trailblazer::Operation::Railway::End::Success:0x00000001b22310
18
+ # # @name=:success,
19
+ # # @options={}>=>{:role=>:success},
20
+ # # #<Trailblazer::Operation::Railway::End::Failure:0x00000001b222c0
21
+ # # @name=:failure,
22
+ # # @options={}>=>{:role=>:unauthorized}}
23
+ # def self.outputs
24
+ # # Outputs::Only(super, :success, :failure)
25
+ # # Outputs::Update( outputs, failure: { role: :unauthorized} )
26
+
27
+ # outs = super.to_a
28
+
29
+ # outs = Hash[*(outs[0]+outs[1])]
30
+
31
+ # outs.merge( outs.keys[1] => { role: :unauthorized } )
32
+ # end
33
+ # end
34
+
35
+ # attach MyEnd.new(:myend), id: "End.unauthorized"
36
+
37
+ # step ( { task: Trailblazer::Activity::Subprocess( Edit, call: :__call__ ),
38
+ # node_data: { id: "Nested/" },
39
+ # outputs: Edit.outputs, # THIS SHOULD OVERRIDE.
40
+ # connect_to: Merge({ unauthorized: "End.unauthorized" }) # connects :success automatically.
41
+ # }), fast_track: true
42
+
43
+ # step :b
44
+ # fail :f
45
+
46
+ # def b(options, a:, **)
47
+ # options["b"] = a+1
48
+ # end
49
+
50
+ # def f(options, a:, **)
51
+ # options["f"] = a+2
52
+ # end
53
+ # end
54
+
55
+ # # Edit ==> true, will run :b
56
+ # it do
57
+ # result = Create.({}, a_return: true )
58
+ # result.inspect("a", "b", "f").must_equal %{<Result:true [1, 2, nil] >}
59
+ # result.event.must_be_instance_of Trailblazer::Operation::Railway::End::Success
60
+ # end
61
+
62
+ # # Edit ==> false, will end on End.unauthorized.
63
+ # it do
64
+ # result = Create.({}, a_return: false )
65
+ # result.inspect("a", "b", "f").must_equal %{<Result:false [1, nil, nil] >}
66
+ # result.event.must_be_instance_of MyEnd
67
+
68
+ # # Create.trace({}, a_return: false ).wtf
69
+ # end
70
+ # end
@@ -17,14 +17,12 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
-
21
- spec.add_dependency "uber" # Uber::Callable is all we need.
22
- spec.add_dependency "declarative"
23
- spec.add_dependency "pipetree", ">= 0.1.1", "< 0.2.0"
20
+ spec.add_dependency "trailblazer-activity", ">= 0.3.0", "< 0.4.0"
21
+ spec.add_dependency "trailblazer-context", ">= 0.1.1", "< 0.3.0"
24
22
 
25
23
  spec.add_development_dependency "bundler"
26
24
  spec.add_development_dependency "rake"
27
25
  spec.add_development_dependency "minitest"
28
26
 
29
- spec.required_ruby_version = '>= 1.9.3'
27
+ spec.required_ruby_version = '>= 2.0.0'
30
28
  end
metadata CHANGED
@@ -1,45 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer-operation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-12 00:00:00.000000000 Z
11
+ date: 2017-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: uber
14
+ name: trailblazer-activity
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: declarative
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
19
+ version: 0.3.0
20
+ - - "<"
32
21
  - !ruby/object:Gem::Version
33
- version: '0'
22
+ version: 0.4.0
34
23
  type: :runtime
35
24
  prerelease: false
36
25
  version_requirements: !ruby/object:Gem::Requirement
37
26
  requirements:
38
27
  - - ">="
39
28
  - !ruby/object:Gem::Version
40
- version: '0'
29
+ version: 0.3.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 0.4.0
41
33
  - !ruby/object:Gem::Dependency
42
- name: pipetree
34
+ name: trailblazer-context
43
35
  requirement: !ruby/object:Gem::Requirement
44
36
  requirements:
45
37
  - - ">="
@@ -47,7 +39,7 @@ dependencies:
47
39
  version: 0.1.1
48
40
  - - "<"
49
41
  - !ruby/object:Gem::Version
50
- version: 0.2.0
42
+ version: 0.3.0
51
43
  type: :runtime
52
44
  prerelease: false
53
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -57,7 +49,7 @@ dependencies:
57
49
  version: 0.1.1
58
50
  - - "<"
59
51
  - !ruby/object:Gem::Version
60
- version: 0.2.0
52
+ version: 0.3.0
61
53
  - !ruby/object:Gem::Dependency
62
54
  name: bundler
63
55
  requirement: !ruby/object:Gem::Requirement
@@ -114,28 +106,48 @@ files:
114
106
  - README.md
115
107
  - Rakefile
116
108
  - lib/trailblazer/operation.rb
117
- - lib/trailblazer/operation/1.9.3/option.rb
118
- - lib/trailblazer/operation/generic.rb
119
- - lib/trailblazer/operation/option.rb
120
- - lib/trailblazer/operation/pipetree.rb
109
+ - lib/trailblazer/operation/deprecated_macro.rb
110
+ - lib/trailblazer/operation/inject.rb
111
+ - lib/trailblazer/operation/inspect.rb
112
+ - lib/trailblazer/operation/public_call.rb
113
+ - lib/trailblazer/operation/railway.rb
114
+ - lib/trailblazer/operation/railway/fast_track.rb
115
+ - lib/trailblazer/operation/railway/normalizer.rb
116
+ - lib/trailblazer/operation/railway/task_builder.rb
121
117
  - lib/trailblazer/operation/result.rb
122
118
  - lib/trailblazer/operation/skill.rb
119
+ - lib/trailblazer/operation/task_wrap.rb
120
+ - lib/trailblazer/operation/trace.rb
121
+ - lib/trailblazer/operation/variable_mapping.rb
123
122
  - lib/trailblazer/operation/version.rb
124
- - lib/trailblazer/skill.rb
125
- - test/2.0.0-pipetree_test.rb
126
- - test/2.1.0-pipetree_test.rb
127
123
  - test/benchmark/skill_resolver_benchmark.rb
128
124
  - test/call_test.rb
125
+ - test/class_dependencies_test.rb
126
+ - test/docs/doormat_test.rb
127
+ - test/docs/wiring_test.rb
129
128
  - test/dry_container_test.rb
129
+ - test/fast_track_test.rb
130
130
  - test/gemfiles/Gemfile.ruby-1.9
131
131
  - test/gemfiles/Gemfile.ruby-2.0
132
+ - test/gemfiles/Gemfile.ruby-2.0.lock
132
133
  - test/gemfiles/Gemfile.ruby-2.3
133
134
  - test/inheritance_test.rb
134
- - test/operation_skill_test.rb
135
- - test/pipetree_test.rb
135
+ - test/inspect_test.rb
136
+ - test/introspect_test.rb
137
+ - test/macro_test.rb
138
+ - test/operation_test.rb
136
139
  - test/result_test.rb
140
+ - test/ruby-2.0.0/operation_test.rb
141
+ - test/ruby-2.0.0/step_test.rb
137
142
  - test/skill_test.rb
143
+ - test/step_test.rb
144
+ - test/task_wrap_test.rb
138
145
  - test/test_helper.rb
146
+ - test/trace_test.rb
147
+ - test/variable_mapping_test.rb
148
+ - test/wire_test.rb
149
+ - test/wiring/defaults_test.rb
150
+ - test/wiring/subprocess_test.rb
139
151
  - trailblazer-operation.gemspec
140
152
  homepage: http://trailblazer.to
141
153
  licenses:
@@ -149,7 +161,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
149
161
  requirements:
150
162
  - - ">="
151
163
  - !ruby/object:Gem::Version
152
- version: 1.9.3
164
+ version: 2.0.0
153
165
  required_rubygems_version: !ruby/object:Gem::Requirement
154
166
  requirements:
155
167
  - - ">="
@@ -157,22 +169,36 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
169
  version: '0'
158
170
  requirements: []
159
171
  rubyforge_project:
160
- rubygems_version: 2.6.3
172
+ rubygems_version: 2.6.12
161
173
  signing_key:
162
174
  specification_version: 4
163
175
  summary: Trailblazer's operation object with railway flow and integrated error handling.
164
176
  test_files:
165
- - test/2.0.0-pipetree_test.rb
166
- - test/2.1.0-pipetree_test.rb
167
177
  - test/benchmark/skill_resolver_benchmark.rb
168
178
  - test/call_test.rb
179
+ - test/class_dependencies_test.rb
180
+ - test/docs/doormat_test.rb
181
+ - test/docs/wiring_test.rb
169
182
  - test/dry_container_test.rb
183
+ - test/fast_track_test.rb
170
184
  - test/gemfiles/Gemfile.ruby-1.9
171
185
  - test/gemfiles/Gemfile.ruby-2.0
186
+ - test/gemfiles/Gemfile.ruby-2.0.lock
172
187
  - test/gemfiles/Gemfile.ruby-2.3
173
188
  - test/inheritance_test.rb
174
- - test/operation_skill_test.rb
175
- - test/pipetree_test.rb
189
+ - test/inspect_test.rb
190
+ - test/introspect_test.rb
191
+ - test/macro_test.rb
192
+ - test/operation_test.rb
176
193
  - test/result_test.rb
194
+ - test/ruby-2.0.0/operation_test.rb
195
+ - test/ruby-2.0.0/step_test.rb
177
196
  - test/skill_test.rb
197
+ - test/step_test.rb
198
+ - test/task_wrap_test.rb
178
199
  - test/test_helper.rb
200
+ - test/trace_test.rb
201
+ - test/variable_mapping_test.rb
202
+ - test/wire_test.rb
203
+ - test/wiring/defaults_test.rb
204
+ - test/wiring/subprocess_test.rb
@@ -1,36 +0,0 @@
1
- class Trailblazer::Operation
2
- # :private:
3
- module Option
4
- def self.call(proc, &block)
5
- type = :proc
6
-
7
- option =
8
- if proc.is_a? Symbol
9
- type = :symbol
10
- ->(input, _options) { call_method(proc, input, _options) }
11
- elsif proc.is_a? Proc
12
- ->(input, _options) { call_proc(proc, input, _options) }
13
- elsif proc.is_a? Uber::Callable
14
- type = :callable
15
- ->(input, _options) { call_callable(proc, input, _options) }
16
- end
17
-
18
- yield type if block_given?
19
- option
20
- end
21
-
22
- def self.call_proc(proc, input, options)
23
- proc.(options)
24
- end
25
-
26
- def self.call_method(proc, input, options)
27
- input.send(proc, options)
28
- end
29
-
30
- def self.call_callable(callable, input, options)
31
- callable.(options)
32
- end
33
-
34
- KW = Option
35
- end # Option
36
- end
@@ -1,12 +0,0 @@
1
- module Trailblazer
2
- # Generic initializer for the operation.
3
- module Operation::Generic
4
- def initialize(skills={})
5
- @skills = skills
6
- end
7
-
8
- # dependency interface
9
- extend Forwardable
10
- def_delegators :@skills, :[], :[]=
11
- end
12
- end
@@ -1,54 +0,0 @@
1
- class Trailblazer::Operation
2
- # :private:
3
- # This code is not beautiful, but could also be worse.
4
- # I'm expecting some of this to go to Uber, as we use this pattern everywhere.
5
- class Option
6
- def self.call(proc, &block)
7
- type = :proc
8
-
9
- option =
10
- if proc.is_a? Symbol
11
- type = :symbol
12
- ->(input, *_options) { call_method(proc, input, *_options) }
13
- elsif proc.is_a? Proc
14
- ->(input, *_options) { call_proc(proc, input, *_options) }
15
- elsif proc.is_a? Uber::Callable
16
- type = :callable
17
- ->(input, *_options) { call_callable(proc, input, *_options) }
18
- end
19
-
20
- yield type if block_given?
21
- option
22
- end
23
-
24
- def self.call_proc(proc, input, *options)
25
- proc.(*options)
26
- end
27
-
28
- def self.call_method(proc, input, *options)
29
- input.send(proc, *options)
30
- end
31
-
32
- def self.call_callable(callable, input, *options)
33
- callable.(*options)
34
- end
35
-
36
- # Call the option with keyword arguments. Ruby <= 2.0.
37
- class KW < Option
38
- def self.call_proc(proc, input, options, tmp_options={})
39
- return proc.(options) if proc.arity == 1
40
- proc.(options, **options.to_hash(tmp_options))
41
- end
42
-
43
- def self.call_method(proc, input, options, tmp_options={})
44
- return input.send(proc, options) if input.method(proc).arity == 1 # TODO: remove this
45
- input.send(proc, options, **options.to_hash(tmp_options))
46
- end
47
-
48
- def self.call_callable(callable, input, options, tmp_options={})
49
- return callable.(options) if callable.method(:call).arity == 1
50
- callable.(options, **options.to_hash(tmp_options))
51
- end
52
- end
53
- end
54
- end
@@ -1,142 +0,0 @@
1
- require "pipetree"
2
- require "pipetree/railway"
3
- require "trailblazer/operation/result"
4
-
5
- if RUBY_VERSION == "1.9.3"
6
- require "trailblazer/operation/1.9.3/option" # TODO: rename to something better.
7
- else
8
- require "trailblazer/operation/option" # TODO: rename to something better.
9
- end
10
-
11
- class Trailblazer::Operation
12
- Instantiate = ->(klass, options) { klass.new(options) } # returns operation instance.
13
-
14
- # Implements the API to populate the operation's pipetree and
15
- # `Operation::call` to invoke the latter.
16
- # Learn more about the Pipetree gem here: https://github.com/apotonick/pipetree
17
- module Pipetree
18
- def self.included(includer)
19
- includer.extend ClassMethods # ::call, ::inititalize_pipetree!
20
- includer.extend DSL # ::|, ::> and friends.
21
-
22
- includer.initialize_pipetree!
23
- end
24
-
25
- module ClassMethods
26
- # Top-level, this method is called when you do Create.() and where
27
- # all the fun starts, ends, and hopefully starts again.
28
- def call(options)
29
- pipe = self["pipetree"] # TODO: injectable? WTF? how cool is that?
30
-
31
- last, operation = pipe.(self, options)
32
-
33
- # Any subclass of Right will be interpreted as successful.
34
- Result.new(!!(last <= Railway::Right), options)
35
- end
36
-
37
- # This method would be redundant if Ruby had a Class::finalize! method the way
38
- # Dry.RB provides it. It has to be executed with every subclassing.
39
- def initialize_pipetree!
40
- heritage.record :initialize_pipetree!
41
-
42
- self["pipetree"] = Railway.new
43
-
44
- strut = ->(last, input, options) { [last, Instantiate.(input, options)] } # first step in pipe.
45
- self["pipetree"].add(Railway::Right, strut, name: "operation.new") # DISCUSS: using pipe API directly here. clever?
46
- end
47
- end
48
-
49
- class Railway < ::Pipetree::Railway
50
- FailFast = Class.new(Left)
51
- PassFast = Class.new(Right)
52
-
53
- def self.fail! ; Left end
54
- def self.fail_fast!; FailFast end
55
- def self.pass! ; Right end
56
- def self.pass_fast!; PassFast end
57
- end
58
-
59
- # The Strut wrapping each step. Makes sure that Track signals are returned immediately.
60
- class Switch < ::Pipetree::Railway::Strut
61
- Decider = ->(result, config, *args) do
62
- return result if result.is_a?(Class) && result <= Railway::Track # this might be pretty slow?
63
-
64
- config[:decider_class].(result, config, *args) # e.g. And::Decider.(result, ..)
65
- end
66
- end
67
-
68
- # Strut that doesn't evaluate the step's result but stays on `last` or configured :signal.
69
- class Stay < ::Pipetree::Railway::Strut
70
- Decider = ->(result, config, last, *) { config[:signal] || last }
71
- end
72
-
73
- module DSL
74
- def success(*args); add(Railway::Right, Stay::Decider, *args) end
75
- def failure(*args); add(Railway::Left, Stay::Decider, *args) end
76
- def step(*args) ; add(Railway::Right, Railway::And::Decider, *args) end
77
-
78
- private
79
- # Operation-level entry point.
80
- def add(track, decider_class, proc, options={})
81
- heritage.record(:add, track, decider_class, proc, options)
82
-
83
- DSL.insert(self["pipetree"], track, decider_class, proc, options)
84
- end
85
-
86
- def self.insert(pipe, track, decider_class, proc, options={}) # TODO: make :name required arg.
87
- _proc, options = proc.is_a?(Array) ? macro!(proc, options) : step!(proc, options)
88
-
89
- options = options.merge(replace: options[:name]) if options[:override] # :override
90
- strut_class, strut_options = AddOptions.(decider_class, options) # :fail_fast and friends.
91
-
92
- pipe.add(track, strut_class.new(_proc, strut_options), options)
93
- end
94
-
95
- def self.macro!(proc, options)
96
- _proc, macro_options = proc
97
-
98
- [ _proc, macro_options.merge(options) ]
99
- end
100
-
101
- def self.step!(proc, options)
102
- name = ""
103
- _proc = Option::KW.(proc) do |type|
104
- name = proc if type == :symbol
105
- name = "#{proc.source_location[0].split("/").last}:#{proc.source_location.last}" if proc.is_a? Proc if type == :proc
106
- name = proc.class if type == :callable
107
- end
108
-
109
- [ _proc, { name: name }.merge(options) ]
110
- end
111
-
112
- AddOptions = ->(decider_class, options) do
113
- # for #failure and #success:
114
- if decider_class == Stay::Decider
115
- return [Stay, signal: Railway::FailFast] if options[:fail_fast]
116
- return [Stay, signal: Railway::PassFast] if options[:pass_fast]
117
- return [Stay, {}]
118
- else # for #step:
119
- return [Switch, decider_class: decider_class, on_false: Railway::FailFast] if options[:fail_fast]
120
- return [Switch, decider_class: decider_class, on_true: Railway::PassFast] if options[:pass_fast]
121
- return [Switch, decider_class: decider_class]
122
- end
123
- end
124
- end # DSL
125
- end
126
-
127
- require "uber/callable"
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
142
- end