trailblazer-activity 0.10.0 → 0.11.4

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
  SHA256:
3
- metadata.gz: c7bfa65d9c33030f9512d1d465cbb47862336c4cff11be999ea64845bc8d8dff
4
- data.tar.gz: a28d2c3bd407fbc5db2cf0d250e68e13f169ae62297d50c51e0507bb743d641b
3
+ metadata.gz: 05447b3e4a4957abae7548719bc6b33500621fc6801860c8aaeaa06c33532c21
4
+ data.tar.gz: d7f55a424263aaffb829a9772fd8cff3d2e93e15691ae091a1eb72570d2136b3
5
5
  SHA512:
6
- metadata.gz: 64a06dd8ff913d4ad708de7bcca3e846f81333eb1c70aaf1a2de132de6ac4044cee55fb6715865fd8580989ce5f944bb4e331e3e758ab925102330bb09397cd6
7
- data.tar.gz: 1e00a9ee8523d21901f7a912a02afc8e25038c13111775b367226cf792796da2edbbef6853e5a4ead885858021b92a4eb9d48e466bf94a84126956b716a3397a
6
+ metadata.gz: 43fc2e829e8c1c3de9493a6b1ef8dd4bacc77a1136d8efbb1f9c08d2260e381debc733df7f6d5e4e1b53bb0cefaebf76d54caceeaf786b6056f56878adc1cc6d
7
+ data.tar.gz: 98ff3aca3a7b3fe102ce9bd368cdfc962cc68687c432c087750f64a36d34ef258a338abc32dc2d3d228d8982a0211b5c6de8efe051dbb4576bbe740bc860d13a
@@ -1,10 +1,12 @@
1
- sudo: false
2
1
  language: ruby
3
- rvm:
4
- - 2.6.0
5
- - 2.5.1
6
- - 2.4.4
7
- # - 2.3.7
8
- # - 2.2.10
9
- # - 2.1.10
10
2
  before_install: gem install bundler
3
+ cache: bundler
4
+ rvm:
5
+ - ruby-head
6
+ - 2.7
7
+ - 2.6
8
+ - 2.5
9
+ - 2.4
10
+ jobs:
11
+ allow_failures:
12
+ - rvm: ruby-head
data/CHANGES.md CHANGED
@@ -1,3 +1,27 @@
1
+ # 0.11.4
2
+
3
+ * Introduce the `config_wrap:` option in `Intermediate.call(intermediate, implementation, config_merge: {})` to allow injecting data into the activity's `:config` field.
4
+
5
+ # 0.11.3
6
+
7
+ * Allow `Testing.def_task` & `Testing.def_tasks` to return custom signals
8
+
9
+ # 0.11.2
10
+
11
+ * Updrading `trailblazer-context` version :drum:
12
+
13
+ # 0.11.1
14
+
15
+ * Internal warning fixes.
16
+
17
+ # 0.11.0
18
+
19
+ * Support for Ruby 2.7. Most warnings are gone.
20
+
21
+ # 0.10.1
22
+
23
+ * Update IllegalSignalError exception for more clarity
24
+
1
25
  # 0.10.0
2
26
 
3
27
  * Require `developer` >= 0.0.7.
data/Gemfile CHANGED
@@ -6,8 +6,5 @@ gemspec
6
6
  gem "benchmark-ips"
7
7
  gem "minitest-line"
8
8
 
9
- gem "rubocop", require: false
10
-
11
9
  # gem "trailblazer-context", path: "../trailblazer-context"
12
10
  # gem "trailblazer-developer", path: "../trailblazer-developer"
13
- # gem "trailblazer-developer", github: "trailblazer/trailblazer-developer", branch: "exception-tracing"
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2018 Trailblazer GmbH
1
+ Copyright (c) 2018-2020 Trailblazer GmbH
2
2
 
3
3
  Trailblazer is an Open Source project licensed under the terms of
4
4
  the LGPLv3 license. Please see <http://www.gnu.org/licenses/lgpl-3.0.html>
data/README.md CHANGED
@@ -1,22 +1,22 @@
1
1
  # Activity
2
2
 
3
- implements Intermediate, Implementation, compiler and `Activity::Implementation`, Activity::Interface
3
+ Implements Intermediate, Implementation and compiler
4
4
 
5
5
  The `activity` gem brings a light-weight DSL to define business processes, and the runtime logic to run those activities.
6
6
 
7
7
  A process is a set of arbitrary pieces of logic you define, chained together and put into a meaningful context by an activity. Activity lets you focus on the implementation of steps while Trailblazer takes care of the control flow.
8
8
 
9
- Please find the [full documentation on the Trailblazer website](http://trailblazer.to/api-docs). [Note that the docs are WIP.]
9
+ Please find the [full documentation on the Trailblazer website](https://2019.trailblazer.to/2.1/docs/activity.html). [Note that the docs are WIP.]
10
10
 
11
11
  ## Example
12
12
 
13
- The `activity` gem provides three default patterns to model processes: `Path`, `Railway` and `FastTrack`. Here's an example what a railway activity could look like, along with some more complex connections.
13
+ In conjunction with [`dsl-linear`](https://github.com/trailblazer/trailblazer-activity-dsl-linear), the `activity` gem provides three default patterns to model processes: `Path`, `Railway` and `FastTrack`. Here's an example of what a railway activity could look like, along with some more complex connections (you can read more about Railway strategy in the [docs](https://2019.trailblazer.to/2.1/docs/activity.html#activity-strategy-railway)).
14
14
 
15
15
  ```ruby
16
- module Memo::Update
17
- extend Trailblazer::Activity::Railway()
18
- module_function
16
+ require "trailblazer-activity"
17
+ require "trailblazer-activity-dsl-linear"
19
18
 
19
+ class Memo::Update < Trailblazer::Activity::Railway
20
20
  # here goes your business logic
21
21
  #
22
22
  def find_model(ctx, id:, **)
@@ -39,10 +39,10 @@ module Memo::Update
39
39
 
40
40
  # here comes the DSL describing the layout of the activity
41
41
  #
42
- step method(:find_model)
43
- step method(:validate), Output(:failure) => End(:validation_error)
44
- step method(:save)
45
- fail method(:log_error)
42
+ step :find_model
43
+ step :validate, Output(:failure) => End(:validation_error)
44
+ step :save
45
+ fail :log_error
46
46
  end
47
47
  ```
48
48
 
@@ -71,17 +71,22 @@ With Activity, modeling business processes turns out to be ridiculously simple:
71
71
  ## Features
72
72
 
73
73
  * Activities can model any process with arbitrary flow and connections.
74
- * Nesting and compositions are allowed and encouraged.
74
+ * Nesting and compositions are allowed and encouraged (via Trailblazer's [`dsl-linear`](https://github.com/trailblazer/trailblazer-activity-dsl-linear) gem).
75
75
  * Different step interfaces, manual processing of DSL options, etc is all possible.
76
76
  * Steps can be any kind of callable objects.
77
- * Tracing!
77
+ * Tracing! (via Trailblazer's [`developer`](https://github.com/trailblazer/trailblazer-developer) gem)
78
78
 
79
79
  ## Operation
80
80
 
81
- Trailblazer's [`Operation`](http://trailblazer.to/2.1/operation) internally uses an activity to model the processes.
81
+ Trailblazer's [`Operation`](https://2019.trailblazer.to/2.1/docs/operation.html#operation-overview) internally uses an activity to model the processes.
82
+
83
+ ## Workflow
84
+ Activities can be formed into bigger compounds and using workflow, you can build long-running processes such as a moderated blog post or a parcel delivery. Also, you don't have to use the DSL but can use the [`editor`](https://2019.trailblazer.to/2.1/docs/pro.html#pro-editor)instead(cool for more complex, long-running flows). Here comes a sample screenshot.
85
+
86
+ <img src="http://2019.trailblazer.to/2.1/dist/img/flow.png">
82
87
 
83
88
  ## License
84
89
 
85
90
  © Copyright 2018, Trailblazer GmbH
86
91
 
87
- Licensed under the LGPLv3 license. We also offer a [commercial-friendly license](http://trailblazer.to/pro).
92
+ Licensed under the LGPLv3 license. We also offer a commercial-friendly [license](https://2019.trailblazer.to/2.1/docs/pro.html#pro-license).
data/Rakefile CHANGED
@@ -1,13 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
- require "rubocop/rake_task"
4
3
 
5
4
  Rake::TestTask.new(:test) do |t|
6
5
  t.libs << "test"
7
6
  t.libs << "lib"
8
- t.test_files = FileList["**/*_test.rb"] - FileList["test/docs/*"] + ["test/docs/activity_test.rb"]
7
+ t.test_files = FileList["test/**/*_test.rb"] - FileList["test/docs/*"] + ["test/docs/activity_test.rb"]
9
8
  end
10
9
 
11
- RuboCop::RakeTask.new
12
-
13
- task :default => :test
10
+ task default: %i[test]
@@ -7,10 +7,10 @@ module Trailblazer
7
7
  @schema = schema
8
8
  end
9
9
 
10
- def call(args, circuit_options={})
10
+ def call(args, **circuit_options)
11
11
  @schema[:circuit].(
12
12
  args,
13
- circuit_options.merge(activity: self)
13
+ **(circuit_options.merge(activity: self))
14
14
  )
15
15
  end
16
16
 
@@ -34,24 +34,15 @@ module Trailblazer
34
34
  end # Activity
35
35
  end
36
36
 
37
- # require "trailblazer/activity/interface"
38
37
  require "trailblazer/activity/structures"
39
38
  require "trailblazer/activity/schema"
40
- require "trailblazer/activity/schema/implementation"
41
- require "trailblazer/activity/schema/intermediate"
42
39
  require "trailblazer/activity/circuit"
43
40
  require "trailblazer/activity/config"
44
41
  require "trailblazer/activity/introspect"
45
-
46
42
  require "trailblazer/activity/task_wrap"
47
- require "trailblazer/activity/task_wrap/pipeline"
48
- require "trailblazer/activity/task_wrap/call_task"
49
- require "trailblazer/activity/task_wrap/runner"
50
- require "trailblazer/activity/task_wrap/variable_mapping"
51
- require "trailblazer/activity/task_wrap/inject"
43
+ require "trailblazer/activity/task_builder"
52
44
 
53
45
  require "trailblazer/option"
54
46
  require "trailblazer/context"
55
- require "trailblazer/activity/task_builder"
56
47
 
57
48
 
@@ -45,13 +45,22 @@ module Trailblazer
45
45
  last_signal, args, _discarded_circuit_options = runner.(
46
46
  task,
47
47
  args,
48
- circuit_options
48
+ **circuit_options
49
49
  )
50
50
 
51
51
  # Stop execution of the circuit when we hit a stop event (< End). This could be an task's End or Suspend.
52
52
  return [ last_signal, args ] if @stop_events.include?(task) # DISCUSS: return circuit_options here?
53
53
 
54
- task = next_for(task, last_signal) or raise IllegalSignalError.new("<#{@name}>[#{task}][ #{last_signal.inspect} ]")
54
+ if (next_task = next_for(task, last_signal))
55
+ task = next_task
56
+ else
57
+ raise IllegalSignalError.new(
58
+ task,
59
+ signal: last_signal,
60
+ outputs: @map[task],
61
+ exec_context: circuit_options[:exec_context], # passed at run-time from DSL
62
+ )
63
+ end
55
64
  end
56
65
  end
57
66
 
@@ -67,7 +76,26 @@ module Trailblazer
67
76
  outputs[signal]
68
77
  end
69
78
 
79
+ # Common reasons to raise IllegalSignalError are
80
+ # * Returning invalid signal from custom Macros
81
+ # * Returning invalid signal from steps which are not taskWrapped, for example: `step task: method(:validate)`
82
+ #
83
+ # Rest assured, it won't be raised in case of below scenarios where they can return any value,
84
+ # * Steps with instance method signature, for example, `step :load_user`
85
+ # * Steps with proc signature, for example `step ->(ctx, **){}`
70
86
  class IllegalSignalError < RuntimeError
87
+ attr_reader :task, :signal
88
+
89
+ def initialize(task, signal:, outputs:, exec_context:)
90
+ @task = task
91
+ @signal = signal
92
+
93
+ message = "#{exec_context.class}: \n\t" \
94
+ "\sUnrecognized Signal `#{signal.inspect}` returned from #{task.inspect}. Registered signals are, \n" \
95
+ "- #{outputs.keys.join("\n- ")}"
96
+
97
+ super(message)
98
+ end
71
99
  end
72
100
  end
73
101
  end
@@ -28,6 +28,7 @@ module Trailblazer
28
28
 
29
29
  return state[directive] if args.size == 1
30
30
  return state[directive][key] if state.key?(directive)
31
+
31
32
  nil
32
33
  end
33
34
  end
@@ -4,10 +4,6 @@ module Trailblazer
4
4
  # It abstracts internals about circuits and provides a convenient API to third-parties such as
5
5
  # tracing, rendering an activity, or finding particular tasks.
6
6
  module Introspect
7
- def self.Graph(*args)
8
- Graph.new(*args)
9
- end
10
-
11
7
  # TODO: order of step/fail/pass in Node would be cool to have
12
8
 
13
9
  # @private This API is still under construction.
@@ -20,13 +16,14 @@ module Trailblazer
20
16
  @configs = @schema[:nodes]
21
17
  end
22
18
 
23
- def find(id=nil, &block)
19
+ def find(id = nil, &block)
24
20
  return find_by_id(id) unless block_given?
21
+
25
22
  find_with_block(&block)
26
23
  end
27
24
 
28
- def collect(strategy: :circuit, &block)
29
- @map.keys.each_with_index.collect { |task, i| yield find_with_block { |node| node.task==task }, i }
25
+ def collect(strategy: :circuit)
26
+ @map.keys.each_with_index.collect { |task, i| yield find_with_block { |node| node.task == task }, i }
30
27
  end
31
28
 
32
29
  def stop_events
@@ -36,11 +33,11 @@ module Trailblazer
36
33
  private
37
34
 
38
35
  def find_by_id(id)
39
- node = @configs.find { |node| node.id == id } or return
36
+ node = @configs.find { |_node| _node.id == id } or return
40
37
  node_for(node)
41
38
  end
42
39
 
43
- def find_with_block(&block)
40
+ def find_with_block
44
41
  existing = @configs.find { |node| yield Node(node.task, node.id, node.outputs, node.data) } or return
45
42
 
46
43
  node_for(existing)
@@ -67,6 +64,10 @@ module Trailblazer
67
64
  end
68
65
  end
69
66
  end
70
- end #Introspect
67
+
68
+ def self.Graph(*args)
69
+ Graph.new(*args)
70
+ end
71
+ end # Introspect
71
72
  end
72
73
  end
@@ -11,3 +11,5 @@ module Trailblazer
11
11
  end # Schema
12
12
  end
13
13
  end
14
+ require "trailblazer/activity/schema/implementation"
15
+ require "trailblazer/activity/schema/intermediate"
@@ -12,15 +12,17 @@ class Trailblazer::Activity
12
12
  # Compiles a {Schema} instance from an {intermediate} structure and
13
13
  # the {implementation} object references.
14
14
  #
15
- # Intermediate structure, Implementation, calls extensions, passes {}config # TODO
16
- def self.call(intermediate, implementation)
17
- config_default = {wrap_static: Hash.new(TaskWrap.initial_wrap_static)}.freeze # DISCUSS: this really doesn't have to be here, but works for now and we want it in 99%.
15
+ # Intermediate structure, Implementation, calls extensions, passes {config} # TODO
16
+ def self.call(intermediate, implementation, config_merge: {})
17
+ config_default = {wrap_static: Hash.new(TaskWrap.initial_wrap_static)} # DISCUSS: this really doesn't have to be here, but works for now and we want it in 99%.
18
+ config = config_default.merge(config_merge)
19
+ config.freeze
18
20
 
19
21
  circuit = circuit(intermediate, implementation)
20
22
  nodes = node_attributes(intermediate, implementation)
21
23
  outputs = outputs(intermediate.stop_task_ids, nodes)
22
- config = config(implementation, config: config_default)
23
- schema = Schema.new(circuit, outputs, nodes, config)
24
+ config = config(implementation, config: config)
25
+ Schema.new(circuit, outputs, nodes, config)
24
26
  end
25
27
 
26
28
  # From the intermediate "template" and the actual implementation, compile a {Circuit} instance.
@@ -83,10 +85,10 @@ class Trailblazer::Activity
83
85
  config
84
86
  end
85
87
 
86
- private
88
+
87
89
 
88
90
  # Apply to any array.
89
- def self.for_semantic(outputs, semantic)
91
+ private_class_method def self.for_semantic(outputs, semantic)
90
92
  outputs.find { |out| out.semantic == semantic } or raise "`#{semantic}` not found"
91
93
  end
92
94
  end # Intermediate
@@ -2,13 +2,7 @@ module Trailblazer
2
2
  class Activity
3
3
  # Generic run-time structures that are built via the DSL.
4
4
 
5
- # Builds an {Activity::End} instance.
6
- def self.End(semantic)
7
- End.new(semantic: semantic)
8
- end
9
-
10
5
  # Any instance of subclass of End will halt the circuit's execution when hit.
11
-
12
6
  # An End event is a simple structure typically found as the last task invoked
13
7
  # in an activity. The special behavior is that it
14
8
  # a) maintains a semantic that is used to further connect that very event
@@ -18,8 +12,8 @@ module Trailblazer
18
12
  @options = options.merge(semantic: semantic)
19
13
  end
20
14
 
21
- def call(args, circuit_options)
22
- return self, args, circuit_options
15
+ def call(args, **circuit_options)
16
+ return self, args, **circuit_options
23
17
  end
24
18
 
25
19
  def to_h
@@ -27,15 +21,15 @@ module Trailblazer
27
21
  end
28
22
 
29
23
  def to_s
30
- %{#<#{self.class.name} #{@options.collect{ |k,v| "#{k}=#{v.inspect}" }.join(" ")}>}
24
+ %{#<#{self.class.name} #{@options.collect { |k, v| "#{k}=#{v.inspect}" }.join(" ")}>}
31
25
  end
32
26
 
33
- alias_method :inspect, :to_s
27
+ alias inspect to_s
34
28
  end
35
29
 
36
30
  class Start < End
37
- def call(args, circuit_options)
38
- return Activity::Right, args, circuit_options
31
+ def call(args, **circuit_options)
32
+ return Activity::Right, args, **circuit_options
39
33
  end
40
34
  end
41
35
 
@@ -53,5 +47,10 @@ module Trailblazer
53
47
  def self.Output(signal, semantic)
54
48
  Output.new(signal, semantic).freeze
55
49
  end
50
+
51
+ # Builds an {Activity::End} instance.
52
+ def self.End(semantic)
53
+ End.new(semantic: semantic)
54
+ end
56
55
  end
57
56
  end
@@ -11,7 +11,11 @@ module Trailblazer
11
11
  # Translates the return value of the user step into a valid signal.
12
12
  # Note that it passes through subclasses of {Signal}.
13
13
  def self.binary_signal_for(result, on_true, on_false)
14
- result.is_a?(Class) && result < Activity::Signal ? result : (result ? on_true : on_false)
14
+ if result.is_a?(Class) && result < Activity::Signal
15
+ result
16
+ else
17
+ result ? on_true : on_false
18
+ end
15
19
  end
16
20
 
17
21
  class Task
@@ -3,12 +3,12 @@ module Trailblazer
3
3
  #
4
4
  # Example with tracing:
5
5
  #
6
- # Call the task_wrap circuit:
7
- # |-- Start
8
- # |-- Trace.capture_args [optional]
9
- # |-- Call (call actual task) id: "task_wrap.call_task"
10
- # |-- Trace.capture_return [optional]
11
- # |-- Wrap::End
6
+ # Call the task_wrap circuit:
7
+ # |-- Start
8
+ # |-- Trace.capture_args [optional]
9
+ # |-- Call (call actual task) id: "task_wrap.call_task"
10
+ # |-- Trace.capture_return [optional]
11
+ # |-- Wrap::End
12
12
  module TaskWrap
13
13
  module_function
14
14
 
@@ -21,7 +21,7 @@ module Trailblazer
21
21
  )
22
22
 
23
23
  # signal, (ctx, flow), circuit_options =
24
- Runner.(activity, args, circuit_options)
24
+ Runner.(activity, args, **circuit_options)
25
25
  end
26
26
 
27
27
  # {:extension} API
@@ -29,7 +29,8 @@ module Trailblazer
29
29
  # Gets executed in {Intermediate.call} which also provides {config}.
30
30
 
31
31
  def initial_wrap_static(*)
32
- initial_sequence = TaskWrap::Pipeline.new([["task_wrap.call_task", TaskWrap.method(:call_task)]])
32
+ # return initial_sequence
33
+ TaskWrap::Pipeline.new([["task_wrap.call_task", TaskWrap.method(:call_task)]])
33
34
  end
34
35
 
35
36
  # Use this in your macros if you want to extend the {taskWrap}.
@@ -51,3 +52,8 @@ module Trailblazer
51
52
  end # TaskWrap
52
53
  end
53
54
  end
55
+ require "trailblazer/activity/task_wrap/pipeline"
56
+ require "trailblazer/activity/task_wrap/call_task"
57
+ require "trailblazer/activity/task_wrap/runner"
58
+ require "trailblazer/activity/task_wrap/variable_mapping"
59
+ require "trailblazer/activity/task_wrap/inject"