trailblazer-activity 0.9.4 → 0.11.3

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
  SHA256:
3
- metadata.gz: 0b971b7f8960520b05bcc99a2fe0561056d9ec244cf9faeb072e131604e0bc12
4
- data.tar.gz: ec6e1d2c4b2de4d1edfce435c08029a061df61b1b770ad174c005733238269cb
3
+ metadata.gz: 380d2d51d03b55dbd76b0501b67d9f16d782d1c68a62a6fe8b87ef0074480206
4
+ data.tar.gz: 5b5b4ebea2d846793d8ad925ac18a3c6b458857c7189894c7cd61924030b5632
5
5
  SHA512:
6
- metadata.gz: 5de05a662519033f02c6e58595d502e80970b08abe7bd87caa7ff23c6acae6490498453bcf8584b8ea6dcba14ef6f00298b1aeca75c5405f8e281e4d206e44e9
7
- data.tar.gz: 35c9f6d275c0da422e0597529424f005af4e44a344c63c0ca8595d87a44ad8c474be21c5e9c985d1f1a01b2d1ff0b2020da06edcc29e9871af9c174c1766d7cb
6
+ metadata.gz: 2e4e0b531018c3eae43d943b21a583807a494ea00e93e26f6fb3bdb13da04cdc6b7bc7b431a0bf0508b02c1571ab4e249864d77832d32405c46ee3704092770a
7
+ data.tar.gz: 34b9a5957cc8e59ac3862c2bdf4e4cd5b4028101f2205bcb0566a598a6539396fdd33f1c25ed161f2bc03d1da1cbb6bfd9f01e78a3d1bd3eb5573a7efb93c2f3
@@ -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,18 +1,46 @@
1
+ # 0.11.3
2
+
3
+ * Allow `Testing.def_task` & `Testing.def_tasks` to return custom signals
4
+
5
+ # 0.11.2
6
+
7
+ * Updrading `trailblazer-context` version :drum:
8
+
9
+ # 0.11.1
10
+
11
+ * Internal warning fixes.
12
+
13
+ # 0.11.0
14
+
15
+ * Support for Ruby 2.7. Most warnings are gone.
16
+
17
+ # 0.10.1
18
+
19
+ * Update IllegalSignalError exception for more clarity
20
+
21
+ # 0.10.0
22
+
23
+ * Require `developer` >= 0.0.7.
24
+ * Move `Activity::Introspect` back to this very gem.
25
+ * This is hopefully the last release before 2.1.0. :trollface:
26
+
1
27
  # 0.9.4
2
28
 
3
- Move some test helpers to `Activity::Testing` to export them to other gems
29
+ * Move some test helpers to `Activity::Testing` to export them to other gems
30
+ * Remove introspection modules, it'll also be part of the `Dev` tools now.
31
+ * Remove tracing modules, it'll be part of the `Dev` tools now.
4
32
 
5
33
  # 0.9.3
6
34
 
7
- Remove introspection modules, it'll also be part of the `Dev` tools now.
35
+ Unreleased.
8
36
 
9
37
  # 0.9.2
10
38
 
11
- Remove tracing modules, it'll be part of the `Dev` tools now.
39
+ Unreleased.
12
40
 
13
41
  # 0.9.1
14
42
 
15
- Use `context-0.9.1`.
43
+ * Use `context-0.9.1`.
16
44
 
17
45
  # 0.9.0
18
46
 
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,23 +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
42
  require "trailblazer/activity/task_wrap"
46
- require "trailblazer/activity/task_wrap/pipeline"
47
- require "trailblazer/activity/task_wrap/call_task"
48
- require "trailblazer/activity/task_wrap/runner"
49
- require "trailblazer/activity/task_wrap/variable_mapping"
50
- require "trailblazer/activity/task_wrap/inject"
43
+ require "trailblazer/activity/task_builder"
51
44
 
52
45
  require "trailblazer/option"
53
46
  require "trailblazer/context"
54
- require "trailblazer/activity/task_builder"
55
47
 
56
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
@@ -0,0 +1,73 @@
1
+ module Trailblazer
2
+ class Activity
3
+ # The Introspect API provides inflections for `Activity` instances.
4
+ # It abstracts internals about circuits and provides a convenient API to third-parties such as
5
+ # tracing, rendering an activity, or finding particular tasks.
6
+ module Introspect
7
+ # TODO: order of step/fail/pass in Node would be cool to have
8
+
9
+ # @private This API is still under construction.
10
+ class Graph
11
+ def initialize(activity)
12
+ @activity = activity
13
+ @schema = activity.to_h or raise
14
+ @circuit = @schema[:circuit]
15
+ @map = @circuit.to_h[:map]
16
+ @configs = @schema[:nodes]
17
+ end
18
+
19
+ def find(id = nil, &block)
20
+ return find_by_id(id) unless block_given?
21
+
22
+ find_with_block(&block)
23
+ end
24
+
25
+ def collect(strategy: :circuit)
26
+ @map.keys.each_with_index.collect { |task, i| yield find_with_block { |node| node.task == task }, i }
27
+ end
28
+
29
+ def stop_events
30
+ @circuit.to_h[:end_events]
31
+ end
32
+
33
+ private
34
+
35
+ def find_by_id(id)
36
+ node = @configs.find { |_node| _node.id == id } or return
37
+ node_for(node)
38
+ end
39
+
40
+ def find_with_block
41
+ existing = @configs.find { |node| yield Node(node.task, node.id, node.outputs, node.data) } or return
42
+
43
+ node_for(existing)
44
+ end
45
+
46
+ def node_for(node_attributes)
47
+ Node(node_attributes.task, node_attributes.id, node_attributes.outputs, outgoings_for(node_attributes), node_attributes.data)
48
+ end
49
+
50
+ def Node(*args)
51
+ Node.new(*args).freeze
52
+ end
53
+
54
+ Node = Struct.new(:task, :id, :outputs, :outgoings, :data)
55
+ Outgoing = Struct.new(:output, :task)
56
+
57
+ def outgoings_for(node)
58
+ outputs = node.outputs
59
+ connections = @map[node.task]
60
+
61
+ connections.collect do |signal, target|
62
+ output = outputs.find { |out| out.signal == signal }
63
+ Outgoing.new(output, target)
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.Graph(*args)
69
+ Graph.new(*args)
70
+ end
71
+ end # Introspect
72
+ end
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"
@@ -20,7 +20,7 @@ class Trailblazer::Activity
20
20
  nodes = node_attributes(intermediate, implementation)
21
21
  outputs = outputs(intermediate.stop_task_ids, nodes)
22
22
  config = config(implementation, config: config_default)
23
- schema = Schema.new(circuit, outputs, nodes, config)
23
+ Schema.new(circuit, outputs, nodes, config)
24
24
  end
25
25
 
26
26
  # From the intermediate "template" and the actual implementation, compile a {Circuit} instance.
@@ -83,10 +83,10 @@ class Trailblazer::Activity
83
83
  config
84
84
  end
85
85
 
86
- private
86
+
87
87
 
88
88
  # Apply to any array.
89
- def self.for_semantic(outputs, semantic)
89
+ private_class_method def self.for_semantic(outputs, semantic)
90
90
  outputs.find { |out| out.semantic == semantic } or raise "`#{semantic}` not found"
91
91
  end
92
92
  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