bumbleworks 0.0.48 → 0.0.50
Sign up to get free protection for your applications and to get access to all the features.
- 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
|