bumbleworks 0.0.48 → 0.0.50
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.
- data/.gitignore +4 -1
- data/lib/bumbleworks.rb +9 -8
- data/lib/bumbleworks/configuration.rb +8 -5
- data/lib/bumbleworks/entity.rb +88 -0
- data/lib/bumbleworks/participant.rb +6 -7
- data/lib/bumbleworks/participant/base.rb +11 -0
- data/lib/bumbleworks/participant/entity_interactor.rb +29 -0
- data/lib/bumbleworks/participant/error_handler.rb +14 -0
- data/lib/bumbleworks/participant/local_participant.rb +11 -0
- data/lib/bumbleworks/participant/storage_participant.rb +11 -0
- data/lib/bumbleworks/process.rb +64 -0
- data/lib/bumbleworks/ruote.rb +12 -4
- data/lib/bumbleworks/support.rb +3 -5
- data/lib/bumbleworks/task.rb +6 -4
- data/lib/bumbleworks/{tasks → task}/base.rb +1 -1
- data/lib/bumbleworks/task/finder.rb +39 -3
- data/lib/bumbleworks/version.rb +1 -1
- data/spec/fixtures/entities/furby.rb +30 -0
- data/spec/integration/entity_spec.rb +49 -0
- data/spec/integration/history_storage_spec.rb +4 -4
- data/spec/integration/sample_application_spec.rb +8 -2
- data/spec/lib/bumbleworks/entity_spec.rb +208 -0
- data/spec/lib/bumbleworks/{participant_spec.rb → participant/base_spec.rb} +3 -3
- data/spec/lib/bumbleworks/participant/entity_interactor_spec.rb +91 -0
- data/spec/lib/bumbleworks/{error_handler_participant_spec.rb → participant/error_handler_spec.rb} +1 -1
- data/spec/lib/bumbleworks/{local_participant_spec.rb → participant/local_participant_spec.rb} +1 -1
- data/spec/lib/bumbleworks/process_spec.rb +147 -0
- data/spec/lib/bumbleworks/ruote/exp/broadcast_event_expression_spec.rb +1 -1
- data/spec/lib/bumbleworks/ruote/exp/wait_for_event_expression_spec.rb +3 -3
- data/spec/lib/bumbleworks/ruote_spec.rb +28 -21
- data/spec/lib/bumbleworks/task/finder_spec.rb +9 -0
- data/spec/lib/bumbleworks/task_spec.rb +98 -3
- data/spec/lib/bumbleworks_spec.rb +14 -7
- data/spec/spec_helper.rb +0 -1
- data/spec/support/process_testing_helpers.rb +9 -0
- metadata +34 -13
- data/lib/bumbleworks/error_handler_participant.rb +0 -12
- data/lib/bumbleworks/local_participant.rb +0 -9
- data/lib/bumbleworks/storage_participant.rb +0 -9
data/.gitignore
CHANGED
data/lib/bumbleworks.rb
CHANGED
@@ -3,17 +3,18 @@ require "bumbleworks/version"
|
|
3
3
|
require "bumbleworks/configuration"
|
4
4
|
require "bumbleworks/support"
|
5
5
|
require "bumbleworks/process_definition"
|
6
|
+
require "bumbleworks/process"
|
6
7
|
require "bumbleworks/task"
|
7
8
|
require "bumbleworks/participant_registration"
|
8
9
|
require "bumbleworks/ruote"
|
9
10
|
require "bumbleworks/hash_storage"
|
10
|
-
require "bumbleworks/simple_logger"
|
11
|
-
require "bumbleworks/storage_participant"
|
12
|
-
require "bumbleworks/local_participant"
|
13
|
-
require "bumbleworks/participant"
|
14
11
|
require "bumbleworks/error_handler"
|
12
|
+
require "bumbleworks/entity"
|
13
|
+
require "bumbleworks/participant"
|
14
|
+
|
15
|
+
# default implementations
|
16
|
+
require "bumbleworks/simple_logger"
|
15
17
|
require "bumbleworks/error_logger"
|
16
|
-
require "bumbleworks/error_handler_participant"
|
17
18
|
|
18
19
|
module Bumbleworks
|
19
20
|
class UnsupportedMode < StandardError; end
|
@@ -23,7 +24,6 @@ module Bumbleworks
|
|
23
24
|
|
24
25
|
class << self
|
25
26
|
extend Forwardable
|
26
|
-
attr_accessor :env
|
27
27
|
|
28
28
|
Configuration.defined_settings.each do |setting|
|
29
29
|
def_delegators :configuration, setting, "#{setting.to_s}="
|
@@ -136,7 +136,8 @@ module Bumbleworks
|
|
136
136
|
#
|
137
137
|
def launch!(process_definition_name, *args)
|
138
138
|
extract_entity_from_fields!(args.first || {})
|
139
|
-
Bumbleworks::Ruote.launch(process_definition_name, *args)
|
139
|
+
pid = Bumbleworks::Ruote.launch(process_definition_name, *args)
|
140
|
+
Bumbleworks::Process.new(pid)
|
140
141
|
end
|
141
142
|
|
142
143
|
private
|
@@ -149,7 +150,7 @@ module Bumbleworks
|
|
149
150
|
raise InvalidEntity, "Entity#id must be non-null" unless fields[:entity_id]
|
150
151
|
end
|
151
152
|
rescue NoMethodError => e
|
152
|
-
raise InvalidEntity, "Entity must respond to #id and #class.name"
|
153
|
+
raise InvalidEntity, "Entity must respond to #identifier (or #id) and #class.name"
|
153
154
|
end
|
154
155
|
end
|
155
156
|
end
|
@@ -115,9 +115,11 @@ module Bumbleworks
|
|
115
115
|
# default: 5 seconds
|
116
116
|
define_setting :timeout
|
117
117
|
|
118
|
-
# When errors occur during the
|
119
|
-
# the
|
120
|
-
#
|
118
|
+
# When errors occur during the execution of a process, errors are captured and dispatched to
|
119
|
+
# the registered error handlers. An error handler must take a single initialization argument
|
120
|
+
# (the workitem at the point of error), and implement the #on_error method. You can subclass the
|
121
|
+
# Bumbleworks::ErrorHandler class for the initializer and workitem entity storage. The default
|
122
|
+
# handler (Bumbleworks::ErrorLogger) will simply send the configured logger an ERROR log entry.
|
121
123
|
#
|
122
124
|
# class MySpecialHandler < Bumbleworks::ErrorHandler
|
123
125
|
# def on_error
|
@@ -126,11 +128,12 @@ module Bumbleworks
|
|
126
128
|
# end
|
127
129
|
#
|
128
130
|
# For exclusive use:
|
129
|
-
# Bumbleworks.error_handlers = [
|
131
|
+
# Bumbleworks.error_handlers = [MySpecialHandler, MySpecialHandler2]
|
130
132
|
#
|
131
133
|
# To append to exisiting handlers:
|
132
|
-
# Bumbleworks.error_handlers <<
|
134
|
+
# Bumbleworks.error_handlers << MySpecialHandler
|
133
135
|
#
|
136
|
+
# default: Bumbleworks::ErrorLogger
|
134
137
|
define_setting :error_handlers
|
135
138
|
|
136
139
|
# If #store_history is true, all messages will be logged in the storage under a special
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Bumbleworks
|
2
|
+
module Entity
|
3
|
+
def self.included(klass)
|
4
|
+
klass.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
def launch_process(process_name, options = {})
|
8
|
+
identifier_column = self.class.processes[process_name.to_sym][:column]
|
9
|
+
if (options[:force] == true || (process_identifier = self.send(identifier_column)).nil?)
|
10
|
+
workitem_fields = process_fields(process_name.to_sym)
|
11
|
+
variables = process_variables(process_name.to_sym)
|
12
|
+
process_identifier = Bumbleworks.launch!(process_name.to_s, workitem_fields, variables).wfid
|
13
|
+
persist_process_identifier(identifier_column.to_sym, process_identifier)
|
14
|
+
end
|
15
|
+
Bumbleworks::Process.new(process_identifier)
|
16
|
+
end
|
17
|
+
|
18
|
+
def persist_process_identifier(identifier_column, process_identifier)
|
19
|
+
if self.respond_to?(:update)
|
20
|
+
update(identifier_column => process_identifier)
|
21
|
+
else
|
22
|
+
raise "Entity must define #persist_process_identifier method if missing #update method."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def processes_by_name
|
27
|
+
return {} unless self.class.processes
|
28
|
+
process_names = self.class.processes.keys
|
29
|
+
process_names.inject({}) do |memo, name|
|
30
|
+
pid = self.send(self.class.processes[name][:column])
|
31
|
+
memo[name] = pid ? Bumbleworks::Process.new(pid) : nil
|
32
|
+
memo
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def processes
|
37
|
+
processes_by_name.values.compact
|
38
|
+
end
|
39
|
+
|
40
|
+
def cancel_all_processes!
|
41
|
+
processes.each do |process|
|
42
|
+
process.cancel!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def tasks(nickname = nil)
|
47
|
+
finder = Bumbleworks::Task.for_entity(self)
|
48
|
+
finder = finder.by_nickname(nickname) if nickname
|
49
|
+
finder
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_fields(process_name = nil)
|
53
|
+
{ :entity => self }
|
54
|
+
end
|
55
|
+
|
56
|
+
def process_variables(process_name = nil)
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
|
60
|
+
def subscribed_events
|
61
|
+
processes.values.compact.map(&:subscribed_events).flatten.uniq
|
62
|
+
end
|
63
|
+
|
64
|
+
def is_waiting_for?(event)
|
65
|
+
subscribed_events.include? event.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
attr_reader :processes
|
70
|
+
|
71
|
+
def process(process_name, options = {})
|
72
|
+
options[:column] ||= process_identifier_column(process_name)
|
73
|
+
(@processes ||= {})[process_name.to_sym] = options
|
74
|
+
end
|
75
|
+
|
76
|
+
def entity_type
|
77
|
+
Bumbleworks::Support.tokenize(name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def process_identifier_column(process_name)
|
81
|
+
identifier_column = "#{process_name}_process_identifier"
|
82
|
+
identifier_column.gsub!(/^#{entity_type}_/, '')
|
83
|
+
identifier_column.gsub!(/process_process/, 'process')
|
84
|
+
identifier_column.to_sym
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
|
-
require "bumbleworks/
|
1
|
+
require "bumbleworks/participant/storage_participant"
|
2
|
+
require "bumbleworks/participant/local_participant"
|
3
|
+
require "bumbleworks/participant/base"
|
4
|
+
require "bumbleworks/participant/error_handler"
|
5
|
+
require "bumbleworks/participant/entity_interactor"
|
2
6
|
|
3
7
|
module Bumbleworks
|
4
|
-
|
5
|
-
include LocalParticipant
|
6
|
-
|
7
|
-
def on_cancel
|
8
|
-
# default no-op method
|
9
|
-
end
|
8
|
+
module Participant
|
10
9
|
end
|
11
10
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Bumbleworks
|
2
|
+
module Participant
|
3
|
+
class EntityInteractor < Bumbleworks::Participant::Base
|
4
|
+
def on_workitem
|
5
|
+
method_name = workitem.fields['params']['method'] ||
|
6
|
+
workitem.fields['params']['to'] ||
|
7
|
+
workitem.fields['params']['for']
|
8
|
+
result_field = workitem.fields['params']['and_save_as']
|
9
|
+
arguments = workitem.fields['params']['arguments'] ||
|
10
|
+
workitem.fields['params']['with']
|
11
|
+
result = call_method(method_name, :save_as => result_field, :args => arguments)
|
12
|
+
reply
|
13
|
+
end
|
14
|
+
|
15
|
+
def call_method(method_name, options = {})
|
16
|
+
result = if options[:args]
|
17
|
+
options[:args] = [options[:args]] if options[:args].is_a?(Hash)
|
18
|
+
entity.send(method_name, *options[:args])
|
19
|
+
else
|
20
|
+
entity.send(method_name)
|
21
|
+
end
|
22
|
+
if result && options[:save_as]
|
23
|
+
workitem.fields[options[:save_as]] = result
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Bumbleworks
|
2
|
+
module Participant
|
3
|
+
class ErrorHandler < Bumbleworks::Participant::Base
|
4
|
+
def on_workitem
|
5
|
+
return if (error_handlers = Bumbleworks.error_handlers).empty?
|
6
|
+
|
7
|
+
error_handlers.each do |error_handler|
|
8
|
+
error_handler.new(workitem).on_error
|
9
|
+
end
|
10
|
+
reply
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Bumbleworks
|
2
|
+
class Process
|
3
|
+
attr_reader :id
|
4
|
+
|
5
|
+
def initialize(wfid)
|
6
|
+
@id = wfid
|
7
|
+
end
|
8
|
+
|
9
|
+
alias_method :wfid, :id
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
wfid == other.wfid
|
13
|
+
end
|
14
|
+
|
15
|
+
def tasks
|
16
|
+
Bumbleworks::Task.for_process(wfid)
|
17
|
+
end
|
18
|
+
|
19
|
+
def trackers
|
20
|
+
Bumbleworks.dashboard.get_trackers.values.select { |attrs|
|
21
|
+
attrs['msg']['fei'] && attrs['msg']['fei']['wfid'] == id
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def all_subscribed_tags
|
26
|
+
events = trackers.inject({}) do |memo, t|
|
27
|
+
if t['wfid'].nil?
|
28
|
+
(memo[:global] ||= []).concat t['conditions']['tag']
|
29
|
+
else
|
30
|
+
(memo[t['wfid']] ||= []).concat t['conditions']['tag']
|
31
|
+
end
|
32
|
+
memo
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def subscribed_events
|
37
|
+
all_subscribed_tags[:global]
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_waiting_for?(event)
|
41
|
+
subscribed_events.include? event.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def kill!
|
45
|
+
Bumbleworks.kill_process!(wfid)
|
46
|
+
end
|
47
|
+
|
48
|
+
def cancel!
|
49
|
+
Bumbleworks.cancel_process!(wfid)
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_status
|
53
|
+
Bumbleworks.dashboard.process(id)
|
54
|
+
end
|
55
|
+
|
56
|
+
def method_missing(method, *args)
|
57
|
+
ps = process_status
|
58
|
+
if ps.respond_to?(method)
|
59
|
+
return ps.send(method, *args)
|
60
|
+
end
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/bumbleworks/ruote.rb
CHANGED
@@ -56,7 +56,7 @@ module Bumbleworks
|
|
56
56
|
while dashboard.process(wfid)
|
57
57
|
if (Time.now - start_time) > options[:timeout]
|
58
58
|
error_type = options[:method] == :cancel ? CancelTimeout : KillTimeout
|
59
|
-
raise error_type, "Process #{options[:method]}
|
59
|
+
raise error_type, "Process #{options[:method]} for wfid '#{wfid}' taking too long. Errors: #{dashboard.errors(wfid)}"
|
60
60
|
end
|
61
61
|
sleep 0.1
|
62
62
|
end
|
@@ -100,14 +100,22 @@ module Bumbleworks
|
|
100
100
|
|
101
101
|
def register_participants(&block)
|
102
102
|
dashboard.register(&block) if block
|
103
|
+
register_entity_interactor
|
103
104
|
register_error_handler
|
104
105
|
set_catchall_if_needed
|
105
106
|
dashboard.participant_list
|
106
107
|
end
|
107
108
|
|
109
|
+
def register_entity_interactor
|
110
|
+
unless dashboard.participant_list.any? { |part| part.regex == "^(ask|tell)_entity$" }
|
111
|
+
entity_interactor = ::Ruote::ParticipantEntry.new(["^(ask|tell)_entity$", ["Bumbleworks::Participant::EntityInteractor", {}]])
|
112
|
+
dashboard.participant_list = dashboard.participant_list.unshift(entity_interactor)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
108
116
|
def register_error_handler
|
109
117
|
unless dashboard.participant_list.any? { |part| part.regex == '^error_handler_participant$' }
|
110
|
-
error_handler_participant = ::Ruote::ParticipantEntry.new(['^error_handler_participant$', ["Bumbleworks::
|
118
|
+
error_handler_participant = ::Ruote::ParticipantEntry.new(['^error_handler_participant$', ["Bumbleworks::Participant::ErrorHandler", {}]])
|
111
119
|
dashboard.participant_list = dashboard.participant_list.unshift(error_handler_participant)
|
112
120
|
dashboard.on_error = 'error_handler_participant'
|
113
121
|
end
|
@@ -122,8 +130,8 @@ module Bumbleworks
|
|
122
130
|
def set_catchall_if_needed
|
123
131
|
last_participant = dashboard.participant_list.last
|
124
132
|
unless last_participant && last_participant.regex == "^.+$" &&
|
125
|
-
["Ruote::StorageParticipant", "Bumbleworks::StorageParticipant"].include?(last_participant.classname)
|
126
|
-
catchall = ::Ruote::ParticipantEntry.new(["^.+$", ["Bumbleworks::StorageParticipant", {}]])
|
133
|
+
["Ruote::StorageParticipant", "Bumbleworks::Participant::StorageParticipant"].include?(last_participant.classname)
|
134
|
+
catchall = ::Ruote::ParticipantEntry.new(["^.+$", ["Bumbleworks::Participant::StorageParticipant", {}]])
|
127
135
|
dashboard.participant_list = dashboard.participant_list.push(catchall)
|
128
136
|
end
|
129
137
|
end
|
data/lib/bumbleworks/support.rb
CHANGED
@@ -24,11 +24,9 @@ module Bumbleworks
|
|
24
24
|
constant = Object
|
25
25
|
|
26
26
|
name_parts.each do |name_part|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
constant.const_defined?(name_part, false)
|
31
|
-
end
|
27
|
+
const_defined_args = [name_part]
|
28
|
+
const_defined_args << false unless Module.method(:const_defined?).arity == 1
|
29
|
+
constant_defined = constant.const_defined?(*const_defined_args)
|
32
30
|
constant = constant_defined ? constant.const_get(name_part) : constant.const_missing(name_part)
|
33
31
|
end
|
34
32
|
constant
|
data/lib/bumbleworks/task.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require "bumbleworks/tasks/base"
|
2
1
|
require "bumbleworks/workitem_entity_storage"
|
2
|
+
require "bumbleworks/task/base"
|
3
3
|
require "bumbleworks/task/finder"
|
4
4
|
|
5
5
|
module Bumbleworks
|
@@ -80,9 +80,11 @@ module Bumbleworks
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def extend_module
|
83
|
-
extend Bumbleworks::
|
84
|
-
|
85
|
-
|
83
|
+
extend Bumbleworks::Task::Base
|
84
|
+
begin
|
85
|
+
extend task_module if nickname
|
86
|
+
rescue NameError
|
87
|
+
end
|
86
88
|
end
|
87
89
|
|
88
90
|
def task_module
|
@@ -5,7 +5,14 @@ module Bumbleworks
|
|
5
5
|
|
6
6
|
def initialize(queries = [], task_class = Bumbleworks::Task)
|
7
7
|
@queries = queries
|
8
|
+
@queries << proc { |wi| wi['fields']['params']['task'] }
|
8
9
|
@task_class = task_class
|
10
|
+
@task_filters = []
|
11
|
+
@wfids = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def available
|
15
|
+
unclaimed.completable
|
9
16
|
end
|
10
17
|
|
11
18
|
def by_nickname(nickname)
|
@@ -46,13 +53,33 @@ module Bumbleworks
|
|
46
53
|
self
|
47
54
|
end
|
48
55
|
|
56
|
+
def for_processes(processes)
|
57
|
+
process_ids = (processes || []).map { |p|
|
58
|
+
p.is_a?(Bumbleworks::Process) ? p.wfid : p
|
59
|
+
}
|
60
|
+
@wfids = process_ids
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def for_process(process)
|
65
|
+
for_processes([process])
|
66
|
+
end
|
67
|
+
|
49
68
|
def all
|
50
|
-
|
69
|
+
return [] if @wfids == []
|
70
|
+
workitems = Bumbleworks.dashboard.context.storage.get_many('workitems', @wfids).select { |wi|
|
51
71
|
@queries.all? { |q| q.call(wi) }
|
72
|
+
}.collect { |wi|
|
73
|
+
::Ruote::Workitem.new(wi)
|
52
74
|
}
|
53
75
|
from_workitems(workitems)
|
54
76
|
end
|
55
77
|
|
78
|
+
def completable
|
79
|
+
@task_filters << proc { |task| task.completable? }
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
56
83
|
def each(&block)
|
57
84
|
all.each(&block)
|
58
85
|
end
|
@@ -76,10 +103,19 @@ module Bumbleworks
|
|
76
103
|
|
77
104
|
private
|
78
105
|
|
106
|
+
def filter_tasks(tasks)
|
107
|
+
@task_filters.empty? ? tasks :
|
108
|
+
tasks.select { |task|
|
109
|
+
@task_filters.all? { |f| f.call(task) }
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
79
113
|
def from_workitems(workitems)
|
80
|
-
workitems.map { |wi|
|
81
|
-
@task_class.new(wi)
|
114
|
+
tasks = workitems.map { |wi|
|
115
|
+
@task_class.new(wi)
|
82
116
|
}.compact
|
117
|
+
|
118
|
+
filter_tasks(tasks)
|
83
119
|
end
|
84
120
|
end
|
85
121
|
end
|