bumbleworks 0.0.9 → 0.0.10
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/doc/GUIDE.md +3 -3
- data/doc/QUICKSTART.md +3 -3
- data/lib/bumbleworks.rb +27 -1
- data/lib/bumbleworks/configuration.rb +26 -5
- data/lib/bumbleworks/support.rb +16 -0
- data/lib/bumbleworks/task.rb +59 -5
- data/lib/bumbleworks/tasks/base.rb +11 -0
- data/lib/bumbleworks/version.rb +1 -1
- data/spec/fixtures/apps/with_default_directories/full_initializer.rb +1 -0
- data/spec/fixtures/apps/with_default_directories/{app → lib/bumbleworks}/participants/honey_participant.rb +0 -0
- data/spec/fixtures/apps/with_default_directories/{app → lib/bumbleworks}/participants/molasses_participant.rb +0 -0
- data/spec/fixtures/apps/with_default_directories/lib/{process_definitions → bumbleworks/process_definitions}/garbage_collector.rb +0 -0
- data/spec/fixtures/apps/with_default_directories/lib/{process_definitions → bumbleworks/process_definitions}/make_honey.rb +0 -0
- data/spec/fixtures/apps/with_default_directories/lib/{process_definitions → bumbleworks/process_definitions}/make_molasses.rb +0 -0
- data/spec/fixtures/apps/with_default_directories/lib/bumbleworks/tasks/make_some_honey_task.rb +5 -0
- data/spec/fixtures/apps/with_default_directories/lib/bumbleworks/tasks/taste_that_molasses_task.rb +2 -0
- data/spec/integration/configuration_spec.rb +2 -8
- data/spec/integration/sample_application_spec.rb +22 -10
- data/spec/lib/bumbleworks/configuration_spec.rb +35 -5
- data/spec/lib/bumbleworks/participant_registration_spec.rb +2 -2
- data/spec/lib/bumbleworks/support_spec.rb +28 -0
- data/spec/lib/bumbleworks/task_spec.rb +119 -2
- data/spec/lib/bumbleworks_spec.rb +45 -0
- metadata +18 -18
data/doc/GUIDE.md
CHANGED
@@ -98,9 +98,9 @@ When we're developing locally or running tests, though, running a separate worke
|
|
98
98
|
|
99
99
|
## Writing our First Process Definition
|
100
100
|
|
101
|
-
Bumbleworks, by default, will load all files in `lib/process_definitions
|
101
|
+
Bumbleworks, by default, will load all files in `lib/bumbleworks/process_definitions`, or `lib/bumbleworks/processes` if you prefer. Go ahead and create that directory, and we'll put our first process definition in there.
|
102
102
|
|
103
|
-
In Bumbleworks, our plan above (for now, we'll ignore steps 5 and 6, mostly because we're in denial) might look something like the following (save this to `lib/process_definitions/build_zen_clock.rb`):
|
103
|
+
In Bumbleworks, our plan above (for now, we'll ignore steps 5 and 6, mostly because we're in denial) might look something like the following (save this to `lib/bumbleworks/process_definitions/build_zen_clock.rb`):
|
104
104
|
|
105
105
|
```ruby
|
106
106
|
Bumbleworks.define_process 'build_zen_clock' do
|
@@ -205,7 +205,7 @@ We're doing great! We received an order, and we placed a box of parts on the co
|
|
205
205
|
|
206
206
|
## Hiring the Staff
|
207
207
|
|
208
|
-
The Zen Clock, being at once a highly technical affair and a pseudo-spiritual scam, will require both robots and real humans to build it. Let's flesh out the `build_zen_clock` process by expanding our previous `make` step (go ahead and change your `lib/process_definitions/build_zen_clock.rb` file to look like this):
|
208
|
+
The Zen Clock, being at once a highly technical affair and a pseudo-spiritual scam, will require both robots and real humans to build it. Let's flesh out the `build_zen_clock` process by expanding our previous `make` step (go ahead and change your `lib/bumbleworks/process_definitions/build_zen_clock.rb` file to look like this):
|
209
209
|
|
210
210
|
```ruby
|
211
211
|
Bumbleworks.define_process 'build_zen_clock' do
|
data/doc/QUICKSTART.md
CHANGED
@@ -31,7 +31,7 @@
|
|
31
31
|
Bumbleworks.load_definitions!
|
32
32
|
```
|
33
33
|
|
34
|
-
1. Add your first process definition at `lib/process_definitions
|
34
|
+
1. Add your first process definition at `lib/bumbleworks/process_definitions/` (or `lib/bumbleworks/processes`):
|
35
35
|
|
36
36
|
```ruby
|
37
37
|
Bumbleworks.define_process do
|
@@ -41,7 +41,7 @@
|
|
41
41
|
|
42
42
|
Process definitions follow the same syntax as [ruote](http://ruote.rubyforge.org/definitions.html), but are defined using `Bumbleworks.define_process` instead of `Ruote.define`.
|
43
43
|
|
44
|
-
1. (*optional*) Put any [custom participants](http://ruote.rubyforge.org/implementing_participants.html) in `
|
44
|
+
1. (*optional*) Put any [custom participants](http://ruote.rubyforge.org/implementing_participants.html) in `lib/bumbleworks/participants`.
|
45
45
|
|
46
46
|
1. Create an initializer file (e.g. `config/initializers/bumbleworks.rb` for Rails) with the following:
|
47
47
|
|
@@ -62,6 +62,6 @@
|
|
62
62
|
Bumbleworks.start_worker!
|
63
63
|
```
|
64
64
|
|
65
|
-
1. You can now launch processes using `Bumbleworks.launch!('process_definition_name')`.
|
65
|
+
1. You can now launch processes using `Bumbleworks.launch!('process_definition_name')`. `#launch!` takes a hash as an optional second argument - anything set here will become workitem fields. A special key, `:entity`, can be used to specify a persistent business entity for the process, which will be retrievable from process tasks (using `Task#entity`).
|
66
66
|
|
67
67
|
1. Any expressions of the form `[role] :task => [task_name]` will be turned into tasks retrievable at `Bumbleworks::Task.all`; you can get tasks specific to a role or roles using `Bumbleworks::Task.for_roles([role1, role2, ...])`.
|
data/lib/bumbleworks.rb
CHANGED
@@ -12,6 +12,7 @@ module Bumbleworks
|
|
12
12
|
class UnsupportedMode < StandardError; end
|
13
13
|
class UndefinedSetting < StandardError; end
|
14
14
|
class InvalidSetting < StandardError; end
|
15
|
+
class InvalidEntity < StandardError; end
|
15
16
|
|
16
17
|
class << self
|
17
18
|
extend Forwardable
|
@@ -85,6 +86,13 @@ module Bumbleworks
|
|
85
86
|
Bumbleworks::Ruote.register_participants(&block)
|
86
87
|
end
|
87
88
|
|
89
|
+
# @public
|
90
|
+
# Autoloads all files in the configured tasks_directory.
|
91
|
+
#
|
92
|
+
def register_tasks(&block)
|
93
|
+
Bumbleworks::Task.autoload_all
|
94
|
+
end
|
95
|
+
|
88
96
|
# @public
|
89
97
|
# Registers all process_definitions in the configured definitions_directory
|
90
98
|
# with the Ruote engine.
|
@@ -105,10 +113,28 @@ module Bumbleworks
|
|
105
113
|
|
106
114
|
# @public
|
107
115
|
# Launches the process definition with the given process name, as long as
|
108
|
-
# that definition name is already registered with Bumbleworks.
|
116
|
+
# that definition name is already registered with Bumbleworks. If options
|
117
|
+
# has an :entity key, attempts to extract the id and class name before
|
118
|
+
# sending it, so it can be properly stored in workitem fields (and
|
119
|
+
# re-instantiated later).
|
109
120
|
#
|
110
121
|
def launch!(process_definition_name, options = {})
|
122
|
+
extract_entity_from_options!(options)
|
111
123
|
Bumbleworks::Ruote.launch(process_definition_name, options)
|
112
124
|
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def extract_entity_from_options!(options)
|
129
|
+
begin
|
130
|
+
if entity = options.delete(:entity)
|
131
|
+
options[:entity_id] = entity.respond_to?(:identifier) ? entity.identifier : entity.id
|
132
|
+
options[:entity_type] = entity.class.name
|
133
|
+
raise InvalidEntity, "Entity#id must be non-null" unless options[:entity_id]
|
134
|
+
end
|
135
|
+
rescue NoMethodError => e
|
136
|
+
raise InvalidEntity, "Entity must respond to #id and #class.name"
|
137
|
+
end
|
138
|
+
end
|
113
139
|
end
|
114
140
|
end
|
@@ -42,16 +42,24 @@ module Bumbleworks
|
|
42
42
|
# will load all definition files by recursively traversing the directory
|
43
43
|
# tree under this folder. No specific loading order is guaranteed
|
44
44
|
#
|
45
|
-
# default: ${Bumbleworks.root}/lib/process_definitions
|
45
|
+
# default: ${Bumbleworks.root}/lib/bumbleworks/process_definitions then ${Bumbleworks.root}/lib/bumbleworks/processes
|
46
46
|
define_setting :definitions_directory
|
47
47
|
|
48
48
|
# Path to the folder which holds the ruote participant files. Bumbleworks
|
49
49
|
# will recursively traverse the directory tree under this folder and ensure
|
50
50
|
# that all found files are autoloaded before registration of participants.
|
51
51
|
#
|
52
|
-
# default: ${Bumbleworks.root}/
|
52
|
+
# default: ${Bumbleworks.root}/lib/bumbleworks/participants
|
53
53
|
define_setting :participants_directory
|
54
54
|
|
55
|
+
# Path to the folder which holds the optional task module files, which are
|
56
|
+
# used to dynamically extend tasks (to override methods, or implement
|
57
|
+
# callbacks). Bumbleworks will recursively traverse the directory tree under
|
58
|
+
# this folder and ensure that all found files are autoloaded.
|
59
|
+
#
|
60
|
+
# default: ${Bumbleworks.root}/lib/bumbleworks/tasks
|
61
|
+
define_setting :tasks_directory
|
62
|
+
|
55
63
|
# Bumbleworks requires a dedicated key-value storage for process information. Three
|
56
64
|
# storage solutions are currently supported: Hash, Redis and Sequel. The latter
|
57
65
|
# two require the bumbleworks-redis and bumbleworks-sequel gems, respectively.
|
@@ -85,6 +93,14 @@ module Bumbleworks
|
|
85
93
|
@participants_folder ||= default_participant_directory
|
86
94
|
end
|
87
95
|
|
96
|
+
# Path where Bumbleworks will look for task modules to load.
|
97
|
+
# The path can be relative or absolute. Relative paths are
|
98
|
+
# relative to Bumbleworks.root.
|
99
|
+
#
|
100
|
+
def tasks_directory
|
101
|
+
@tasks_folder ||= default_tasks_directory
|
102
|
+
end
|
103
|
+
|
88
104
|
# Root folder where Bumbleworks looks for ruote assets (participants,
|
89
105
|
# process_definitions, etc.) The root path must be absolute.
|
90
106
|
# It can be defined through a configuration block:
|
@@ -123,7 +139,7 @@ module Bumbleworks
|
|
123
139
|
def clear!
|
124
140
|
defined_settings.each {|setting| instance_variable_set("@#{setting}", nil)}
|
125
141
|
@storage_adapters = []
|
126
|
-
@definitions_folder = @participants_folder = nil
|
142
|
+
@definitions_folder = @participants_folder = @tasks_folder = nil
|
127
143
|
end
|
128
144
|
|
129
145
|
private
|
@@ -132,15 +148,20 @@ module Bumbleworks
|
|
132
148
|
end
|
133
149
|
|
134
150
|
def default_definition_directory
|
135
|
-
default_folders = ['lib/process_definitions']
|
151
|
+
default_folders = ['lib/bumbleworks/process_definitions', 'lib/bumbleworks/processes']
|
136
152
|
find_folder(default_folders, @definitions_directory, "Definitions folder not found")
|
137
153
|
end
|
138
154
|
|
139
155
|
def default_participant_directory
|
140
|
-
default_folders = ['
|
156
|
+
default_folders = ['lib/bumbleworks/participants']
|
141
157
|
find_folder(default_folders, @participants_directory, "Participants folder not found")
|
142
158
|
end
|
143
159
|
|
160
|
+
def default_tasks_directory
|
161
|
+
default_folders = ['lib/bumbleworks/tasks']
|
162
|
+
find_folder(default_folders, @tasks_directory, "Tasks folder not found")
|
163
|
+
end
|
164
|
+
|
144
165
|
def find_folder(default_directories, defined_directory, message)
|
145
166
|
# use defined directory structure if defined
|
146
167
|
if defined_directory
|
data/lib/bumbleworks/support.rb
CHANGED
@@ -17,5 +17,21 @@ module Bumbleworks
|
|
17
17
|
memo
|
18
18
|
end
|
19
19
|
end
|
20
|
+
|
21
|
+
def constantize(name)
|
22
|
+
name_parts = name.split('::')
|
23
|
+
name_parts.shift if name_parts.first.empty?
|
24
|
+
constant = Object
|
25
|
+
|
26
|
+
name_parts.each do |name_part|
|
27
|
+
constant_defined = if Module.method(:const_defined?).arity == 1
|
28
|
+
constant.const_defined?(name_part)
|
29
|
+
else
|
30
|
+
constant.const_defined?(name_part, false)
|
31
|
+
end
|
32
|
+
constant = constant_defined ? constant.const_get(name_part) : constant.const_missing(name_part)
|
33
|
+
end
|
34
|
+
constant
|
35
|
+
end
|
20
36
|
end
|
21
37
|
end
|
data/lib/bumbleworks/task.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
require "bumbleworks/tasks/base"
|
2
|
+
|
1
3
|
module Bumbleworks
|
2
4
|
class Task
|
3
5
|
class AlreadyClaimed < StandardError; end
|
4
6
|
class MissingWorkitem < StandardError; end
|
7
|
+
class EntityNotFound < StandardError; end
|
5
8
|
|
6
9
|
extend Forwardable
|
7
10
|
delegate [:sid, :fei, :fields, :params, :participant_name, :wfid, :wf_name] => :@workitem
|
@@ -9,6 +12,20 @@ module Bumbleworks
|
|
9
12
|
alias_method :id, :sid
|
10
13
|
|
11
14
|
class << self
|
15
|
+
# @public
|
16
|
+
# Autoload all task modules defined in files in the
|
17
|
+
# tasks_directory. The symbol for autoload comes from the
|
18
|
+
# camelized version of the filename, so this method is dependent on
|
19
|
+
# following that convention. For example, file `chew_cud_task.rb`
|
20
|
+
# should define `ChewCudTask`.
|
21
|
+
#
|
22
|
+
def autoload_all(options = {})
|
23
|
+
options[:directory] ||= Bumbleworks.tasks_directory
|
24
|
+
Bumbleworks::Support.all_files(options[:directory], :camelize => true).each do |path, name|
|
25
|
+
Object.autoload name.to_sym, path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
12
29
|
def for_role(identifier)
|
13
30
|
for_roles([identifier])
|
14
31
|
end
|
@@ -50,6 +67,20 @@ module Bumbleworks
|
|
50
67
|
raise ArgumentError, "Not a valid workitem"
|
51
68
|
end
|
52
69
|
@nickname = params['task']
|
70
|
+
extend_module
|
71
|
+
end
|
72
|
+
|
73
|
+
def entity
|
74
|
+
if has_entity_fields?
|
75
|
+
klass = Bumbleworks::Support.constantize(fields['entity_type'])
|
76
|
+
entity = klass.first_by_identifier(fields['entity_id'])
|
77
|
+
end
|
78
|
+
raise EntityNotFound unless entity
|
79
|
+
entity
|
80
|
+
end
|
81
|
+
|
82
|
+
def has_entity_fields?
|
83
|
+
fields['entity_id'] && fields['entity_type']
|
53
84
|
end
|
54
85
|
|
55
86
|
# alias for fields[] (fields delegated to workitem)
|
@@ -66,14 +97,28 @@ module Bumbleworks
|
|
66
97
|
participant_name
|
67
98
|
end
|
68
99
|
|
100
|
+
def extend_module
|
101
|
+
extend Bumbleworks::Tasks::Base
|
102
|
+
extend task_module if nickname
|
103
|
+
rescue NameError
|
104
|
+
end
|
105
|
+
|
106
|
+
def task_module
|
107
|
+
return nil unless nickname
|
108
|
+
klass_name = Bumbleworks::Support.camelize(nickname)
|
109
|
+
klass = Bumbleworks::Support.constantize("#{klass_name}Task")
|
110
|
+
end
|
111
|
+
|
69
112
|
# update workitem with changes to fields & params
|
70
|
-
def
|
71
|
-
|
113
|
+
def update(params = {})
|
114
|
+
before_update(params)
|
115
|
+
update_workitem
|
116
|
+
after_update(params)
|
72
117
|
end
|
73
118
|
|
74
119
|
# proceed workitem (saving changes to fields)
|
75
|
-
def complete
|
76
|
-
|
120
|
+
def complete(params = {})
|
121
|
+
proceed_workitem
|
77
122
|
end
|
78
123
|
|
79
124
|
# Token used to claim task, nil if not claimed
|
@@ -96,11 +141,20 @@ module Bumbleworks
|
|
96
141
|
set_claimant(nil)
|
97
142
|
end
|
98
143
|
|
99
|
-
|
144
|
+
private
|
145
|
+
|
100
146
|
def storage_participant
|
101
147
|
self.class.storage_participant
|
102
148
|
end
|
103
149
|
|
150
|
+
def update_workitem
|
151
|
+
storage_participant.update(@workitem)
|
152
|
+
end
|
153
|
+
|
154
|
+
def proceed_workitem
|
155
|
+
storage_participant.proceed(@workitem)
|
156
|
+
end
|
157
|
+
|
104
158
|
def set_claimant(token)
|
105
159
|
if token && claimant && token != claimant
|
106
160
|
raise AlreadyClaimed, "Already claimed by #{claimant}"
|
data/lib/bumbleworks/version.rb
CHANGED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -19,7 +19,7 @@ describe Bumbleworks::Configuration do
|
|
19
19
|
|
20
20
|
it 'returns default directory when not set in configuration' do
|
21
21
|
load default_initializer
|
22
|
-
Bumbleworks.definitions_directory.should == File.join(default_app_path, 'lib', 'process_definitions')
|
22
|
+
Bumbleworks.definitions_directory.should == File.join(default_app_path, 'lib', 'bumbleworks', 'process_definitions')
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -31,13 +31,7 @@ describe Bumbleworks::Configuration do
|
|
31
31
|
|
32
32
|
it 'returns default directory when not set in configuration' do
|
33
33
|
load default_initializer
|
34
|
-
Bumbleworks.participants_directory.should == File.join(default_app_path, '
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'returns second default directory if first not found' do
|
38
|
-
load default_initializer
|
39
|
-
Bumbleworks.root = File.join(default_app_path, 'app')
|
40
|
-
Bumbleworks.participants_directory.should == File.join(default_app_path, 'app', 'participants')
|
34
|
+
Bumbleworks.participants_directory.should == File.join(default_app_path, 'lib', 'bumbleworks', 'participants')
|
41
35
|
end
|
42
36
|
end
|
43
37
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
describe 'Bumbleworks Sample Application' do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
}
|
2
|
+
let(:app_root) {
|
3
|
+
File.expand_path(File.join(fixtures_path, 'apps', 'with_default_directories'))
|
4
|
+
}
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
before :each do
|
7
|
+
Bumbleworks.reset!
|
8
|
+
load File.join(app_root, 'full_initializer.rb')
|
9
|
+
end
|
11
10
|
|
11
|
+
describe 'initializer' do
|
12
12
|
it 'registers participants' do
|
13
13
|
Bumbleworks.dashboard.participant_list.should have(3).items
|
14
14
|
Bumbleworks.dashboard.participant_list.map(&:classname).should =~ ['HoneyParticipant', 'MolassesParticipant', 'Ruote::StorageParticipant']
|
@@ -26,14 +26,16 @@ describe 'Bumbleworks Sample Application' do
|
|
26
26
|
{},
|
27
27
|
[["dave", {"task"=>"make_some_molasses"}, []], ["sam", {"task"=>"taste_that_molasses"}, []]]]]]
|
28
28
|
end
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
+
describe 'launching process' do
|
32
|
+
it 'waits for first task in catchall participant' do
|
31
33
|
Bumbleworks.launch!('make_honey')
|
32
34
|
Bumbleworks.dashboard.wait_for(:dave)
|
33
35
|
Bumbleworks::Task.all.should have(1).item
|
34
36
|
end
|
35
37
|
|
36
|
-
it '
|
38
|
+
it 'creates tasks for concurrent workflows' do
|
37
39
|
Bumbleworks.launch!('make_molasses')
|
38
40
|
Bumbleworks.dashboard.wait_for(:dave)
|
39
41
|
Bumbleworks::Task.all.should have(2).item
|
@@ -41,5 +43,15 @@ describe 'Bumbleworks Sample Application' do
|
|
41
43
|
Bumbleworks::Task.for_role('sam').should have(1).item
|
42
44
|
end
|
43
45
|
end
|
46
|
+
|
47
|
+
describe 'updating a task' do
|
48
|
+
it 'calls callbacks' do
|
49
|
+
Bumbleworks.launch!('make_honey')
|
50
|
+
Bumbleworks.dashboard.wait_for(:dave)
|
51
|
+
task = Bumbleworks::Task.for_role('dave').first
|
52
|
+
task.update('happening' => 'update')
|
53
|
+
task['what_happened'].should == 'update'
|
54
|
+
end
|
55
|
+
end
|
44
56
|
end
|
45
57
|
|
@@ -60,7 +60,14 @@ describe Bumbleworks::Configuration do
|
|
60
60
|
it 'returns the default folder if not set by client app' do
|
61
61
|
File.stub(:directory? => true)
|
62
62
|
configuration.root = '/Root'
|
63
|
-
configuration.definitions_directory.should == '/Root/lib/process_definitions'
|
63
|
+
configuration.definitions_directory.should == '/Root/lib/bumbleworks/process_definitions'
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'returns the second default folder if first does not exist' do
|
67
|
+
File.stub(:directory?).with('/Root/lib/bumbleworks/process_definitions').and_return(false)
|
68
|
+
File.stub(:directory?).with('/Root/lib/bumbleworks/processes').and_return(true)
|
69
|
+
configuration.root = '/Root'
|
70
|
+
configuration.definitions_directory.should == '/Root/lib/bumbleworks/processes'
|
64
71
|
end
|
65
72
|
|
66
73
|
it 'raises an error if default folder not found' do
|
@@ -82,10 +89,9 @@ describe Bumbleworks::Configuration do
|
|
82
89
|
end
|
83
90
|
|
84
91
|
it 'returns the default folder if not set by client app' do
|
85
|
-
File.stub(:directory?
|
86
|
-
File.stub(:directory?).with('/Root/app/participants').and_return(true)
|
92
|
+
File.stub(:directory?).with('/Root/lib/bumbleworks/participants').and_return(true)
|
87
93
|
configuration.root = '/Root'
|
88
|
-
configuration.participants_directory.should == '/Root/
|
94
|
+
configuration.participants_directory.should == '/Root/lib/bumbleworks/participants'
|
89
95
|
end
|
90
96
|
|
91
97
|
it 'raises an error if default folder not found' do
|
@@ -99,6 +105,30 @@ describe Bumbleworks::Configuration do
|
|
99
105
|
end
|
100
106
|
end
|
101
107
|
|
108
|
+
describe "#tasks_directory" do
|
109
|
+
it 'returns the folder which was set by the client app' do
|
110
|
+
File.stub(:directory?).with('/dog/ate/my/homework').and_return(true)
|
111
|
+
configuration.tasks_directory = '/dog/ate/my/homework'
|
112
|
+
configuration.tasks_directory.should == '/dog/ate/my/homework'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns the default folder if not set by client app' do
|
116
|
+
File.stub(:directory?).with('/Root/lib/bumbleworks/tasks').and_return(true)
|
117
|
+
configuration.root = '/Root'
|
118
|
+
configuration.tasks_directory.should == '/Root/lib/bumbleworks/tasks'
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'raises an error if default folder not found' do
|
122
|
+
configuration.root = '/Root'
|
123
|
+
expect{configuration.tasks_directory}.to raise_error Bumbleworks::InvalidSetting
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'raises an error if specific folder not found' do
|
127
|
+
configuration.tasks_directory = '/mumbo/jumbo'
|
128
|
+
expect{configuration.tasks_directory}.to raise_error Bumbleworks::InvalidSetting
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
102
132
|
describe "#storage" do
|
103
133
|
it 'can set storage directly' do
|
104
134
|
storage = double("Storage")
|
@@ -141,7 +171,7 @@ describe Bumbleworks::Configuration do
|
|
141
171
|
configuration.clear!
|
142
172
|
|
143
173
|
configuration.root = '/Root'
|
144
|
-
configuration.definitions_directory.should == '/Root/lib/process_definitions'
|
174
|
+
configuration.definitions_directory.should == '/Root/lib/bumbleworks/process_definitions'
|
145
175
|
end
|
146
176
|
end
|
147
177
|
end
|
@@ -4,9 +4,9 @@ describe Bumbleworks::ParticipantRegistration do
|
|
4
4
|
Bumbleworks.reset!
|
5
5
|
Bumbleworks.root = File.join(fixtures_path, 'apps', 'with_default_directories')
|
6
6
|
Object.should_receive(:autoload).with(:HoneyParticipant,
|
7
|
-
File.join(Bumbleworks.root, '
|
7
|
+
File.join(Bumbleworks.root, 'lib', 'bumbleworks', 'participants', 'honey_participant.rb'))
|
8
8
|
Object.should_receive(:autoload).with(:MolassesParticipant,
|
9
|
-
File.join(Bumbleworks.root, '
|
9
|
+
File.join(Bumbleworks.root, 'lib', 'bumbleworks', 'participants', 'molasses_participant.rb'))
|
10
10
|
Bumbleworks::ParticipantRegistration.autoload_all
|
11
11
|
end
|
12
12
|
end
|
@@ -31,4 +31,32 @@ describe Bumbleworks::Support do
|
|
31
31
|
'TestNestedProcess'
|
32
32
|
end
|
33
33
|
end
|
34
|
+
|
35
|
+
describe '.constantize' do
|
36
|
+
before :each do
|
37
|
+
class Whatever
|
38
|
+
Smoothies = 'tasty'
|
39
|
+
end
|
40
|
+
class Boojus
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
after :each do
|
45
|
+
Object.send(:remove_const, :Whatever)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns value of constant with given name' do
|
49
|
+
described_class.constantize('Whatever')::Smoothies.should == 'tasty'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'works with nested constants' do
|
53
|
+
described_class.constantize('Whatever::Smoothies').should == 'tasty'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'does not check inheritance tree' do
|
57
|
+
expect {
|
58
|
+
described_class.constantize('Whatever::Boojus')
|
59
|
+
}.to raise_error(NameError)
|
60
|
+
end
|
61
|
+
end
|
34
62
|
end
|
@@ -8,6 +8,17 @@ describe Bumbleworks::Task do
|
|
8
8
|
Bumbleworks.start_worker!
|
9
9
|
end
|
10
10
|
|
11
|
+
describe '.autoload_all' do
|
12
|
+
it 'autoloads all task modules in directory' do
|
13
|
+
Bumbleworks.root = File.join(fixtures_path, 'apps', 'with_default_directories')
|
14
|
+
Object.should_receive(:autoload).with(:MakeSomeHoneyTask,
|
15
|
+
File.join(Bumbleworks.root, 'lib', 'bumbleworks', 'tasks', 'make_some_honey_task.rb'))
|
16
|
+
Object.should_receive(:autoload).with(:TasteThatMolassesTask,
|
17
|
+
File.join(Bumbleworks.root, 'lib', 'bumbleworks', 'tasks', 'taste_that_molasses_task.rb'))
|
18
|
+
Bumbleworks::Task.autoload_all
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
11
22
|
describe '.new' do
|
12
23
|
it 'raises an error if workitem is nil' do
|
13
24
|
expect {
|
@@ -26,6 +37,53 @@ describe Bumbleworks::Task do
|
|
26
37
|
described_class.new(workflow_item)
|
27
38
|
}.not_to raise_error
|
28
39
|
end
|
40
|
+
|
41
|
+
it 'extends new object with task module' do
|
42
|
+
described_class.any_instance.should_receive(:extend_module)
|
43
|
+
described_class.new(workflow_item)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#extend_module' do
|
48
|
+
it 'extends with base module and task module' do
|
49
|
+
task = described_class.new(workflow_item)
|
50
|
+
task.should_receive(:task_module).and_return(:task_module_double)
|
51
|
+
task.should_receive(:extend).with(Bumbleworks::Tasks::Base).ordered
|
52
|
+
task.should_receive(:extend).with(:task_module_double).ordered
|
53
|
+
task.extend_module
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'extends only with base module if no nickname' do
|
57
|
+
task = described_class.new(workflow_item)
|
58
|
+
task.stub(:nickname).and_return(nil)
|
59
|
+
task.should_receive(:extend).with(Bumbleworks::Tasks::Base)
|
60
|
+
task.extend_module
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'extends only with base module if task module does not exist' do
|
64
|
+
task = described_class.new(workflow_item)
|
65
|
+
task.should_receive(:extend).with(Bumbleworks::Tasks::Base)
|
66
|
+
task.extend_module
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#task_module' do
|
71
|
+
# def task_module
|
72
|
+
# return nil unless nickname
|
73
|
+
# klass_name = Bumbleworks::Support.camelize(nickname)
|
74
|
+
# klass = Bumbleworks::Support.constantize("Bumbleworks::Tasks::#{klass_name}")
|
75
|
+
# end
|
76
|
+
it 'returns nil if no nickname' do
|
77
|
+
task = described_class.new(workflow_item)
|
78
|
+
task.stub(:nickname).and_return(nil)
|
79
|
+
task.task_module.should be_nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns constantized task nickname with "Task" appended' do
|
83
|
+
task = described_class.new(workflow_item)
|
84
|
+
Bumbleworks::Support.stub(:constantize).with("GoToWorkTask").and_return(:the_task_module)
|
85
|
+
task.task_module.should == :the_task_module
|
86
|
+
end
|
29
87
|
end
|
30
88
|
|
31
89
|
describe '#id' do
|
@@ -243,17 +301,25 @@ describe Bumbleworks::Task do
|
|
243
301
|
Bumbleworks.launch!('dog-lifecycle')
|
244
302
|
end
|
245
303
|
|
246
|
-
describe '#
|
304
|
+
describe '#update' do
|
247
305
|
it 'saves fields and params, but does not proceed process' do
|
248
306
|
event = Bumbleworks.dashboard.wait_for :dog_mouth
|
249
307
|
task = described_class.for_role('dog_mouth').first
|
250
308
|
task.params['state'] = 'is ready'
|
251
309
|
task.fields['meal'] = 'salted_rhubarb'
|
252
|
-
task.
|
310
|
+
task.update
|
253
311
|
task = described_class.for_role('dog_mouth').first
|
254
312
|
task.params['state'].should == 'is ready'
|
255
313
|
task.fields['meal'].should == 'salted_rhubarb'
|
256
314
|
end
|
315
|
+
|
316
|
+
it 'calls before_update and after_update callbacks' do
|
317
|
+
task = described_class.new(workflow_item)
|
318
|
+
task.should_receive(:before_update).with(:argue_mints).ordered
|
319
|
+
task.should_receive(:update_workitem).ordered
|
320
|
+
task.should_receive(:after_update).with(:argue_mints).ordered
|
321
|
+
task.update(:argue_mints)
|
322
|
+
end
|
257
323
|
end
|
258
324
|
|
259
325
|
describe '#complete' do
|
@@ -270,5 +336,56 @@ describe Bumbleworks::Task do
|
|
270
336
|
task.fields['meal'].should == 'root beer and a kite'
|
271
337
|
end
|
272
338
|
end
|
339
|
+
|
340
|
+
describe '#has_entity_fields?' do
|
341
|
+
it 'returns true if workitem fields include entity fields' do
|
342
|
+
task = described_class.new(workflow_item)
|
343
|
+
task['entity_id'] = '1'
|
344
|
+
task['entity_type'] = 'SomeEntity'
|
345
|
+
task.should have_entity_fields
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'returns false if workitem fields do not include entity fields' do
|
349
|
+
task = described_class.new(workflow_item)
|
350
|
+
task.should_not have_entity_fields
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe '#entity' do
|
355
|
+
class LovelyEntity
|
356
|
+
def self.first_by_identifier(identifier)
|
357
|
+
return nil unless identifier
|
358
|
+
"Object #{identifier}"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
let(:entitied_workflow_item) {
|
363
|
+
Ruote::Workitem.new('fields' => {
|
364
|
+
'entity_id' => '15',
|
365
|
+
'entity_type' => 'LovelyEntity',
|
366
|
+
'params' => {'task' => 'go_to_work'}
|
367
|
+
})
|
368
|
+
}
|
369
|
+
|
370
|
+
it 'attempts to instantiate business entity from _id and _type fields' do
|
371
|
+
task = described_class.new(entitied_workflow_item)
|
372
|
+
task.entity.should == 'Object 15'
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'throw exception if entity fields not present' do
|
376
|
+
task = described_class.new(workflow_item)
|
377
|
+
expect {
|
378
|
+
task.entity
|
379
|
+
}.to raise_error Bumbleworks::Task::EntityNotFound
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'throw exception if entity returns nil' do
|
383
|
+
task = described_class.new(entitied_workflow_item)
|
384
|
+
task['entity_id'] = nil
|
385
|
+
expect {
|
386
|
+
task.entity
|
387
|
+
}.to raise_error Bumbleworks::Task::EntityNotFound
|
388
|
+
end
|
389
|
+
end
|
273
390
|
end
|
274
391
|
end
|
@@ -51,6 +51,13 @@ describe Bumbleworks do
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
describe '.register_tasks' do
|
55
|
+
it 'autoloads task modules' do
|
56
|
+
Bumbleworks::Task.should_receive(:autoload_all)
|
57
|
+
described_class.register_tasks
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
54
61
|
describe '.register_participants' do
|
55
62
|
it 'autoloads and registers participants' do
|
56
63
|
the_block = lambda { }
|
@@ -105,4 +112,42 @@ describe Bumbleworks do
|
|
105
112
|
Bumbleworks.start_worker!.should == :lets_do_it
|
106
113
|
end
|
107
114
|
end
|
115
|
+
|
116
|
+
describe '.launch!' do
|
117
|
+
class LovelyEntity
|
118
|
+
attr_accessor :id
|
119
|
+
def initialize(id)
|
120
|
+
@id = id
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'delegates to Bumbleworks::Ruote.launch' do
|
125
|
+
Bumbleworks::Ruote.should_receive(:launch).with(:amazing_process, :hugs => :love)
|
126
|
+
Bumbleworks.launch!(:amazing_process, :hugs => :love)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'expands entity params when entity object provided' do
|
130
|
+
Bumbleworks::Ruote.should_receive(:launch).with(:amazing_process, :entity_id => :wiley_e_coyote, :entity_type => 'LovelyEntity')
|
131
|
+
Bumbleworks.launch!(:amazing_process, :entity => LovelyEntity.new(:wiley_e_coyote))
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'uses "identifier" method instead of id, if entity has one' do
|
135
|
+
entity = LovelyEntity.new(5)
|
136
|
+
entity.stub(:identifier).and_return(:five)
|
137
|
+
Bumbleworks::Ruote.should_receive(:launch).with(:amazing_process, :entity_id => :five, :entity_type => 'LovelyEntity')
|
138
|
+
Bumbleworks.launch!(:amazing_process, :entity => entity)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'throws exception if entity has nil id' do
|
142
|
+
expect {
|
143
|
+
Bumbleworks.launch!(:amazing_process, :entity => LovelyEntity.new(nil))
|
144
|
+
}.to raise_error(Bumbleworks::InvalidEntity)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'throws exception if entity is invalid object' do
|
148
|
+
expect {
|
149
|
+
Bumbleworks.launch!(:amazing_process, :entity => :give_me_a_break)
|
150
|
+
}.to raise_error(Bumbleworks::InvalidEntity)
|
151
|
+
end
|
152
|
+
end
|
108
153
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bumbleworks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2013-06-
|
15
|
+
date: 2013-06-28 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: ruote
|
@@ -175,16 +175,19 @@ files:
|
|
175
175
|
- lib/bumbleworks/storage_adapter.rb
|
176
176
|
- lib/bumbleworks/support.rb
|
177
177
|
- lib/bumbleworks/task.rb
|
178
|
+
- lib/bumbleworks/tasks/base.rb
|
178
179
|
- lib/bumbleworks/tree_builder.rb
|
179
180
|
- lib/bumbleworks/version.rb
|
180
181
|
- lib/tasks/bumbleworks.rake
|
181
|
-
- spec/fixtures/apps/with_default_directories/app/participants/honey_participant.rb
|
182
|
-
- spec/fixtures/apps/with_default_directories/app/participants/molasses_participant.rb
|
183
182
|
- spec/fixtures/apps/with_default_directories/config_initializer.rb
|
184
183
|
- spec/fixtures/apps/with_default_directories/full_initializer.rb
|
185
|
-
- spec/fixtures/apps/with_default_directories/lib/
|
186
|
-
- spec/fixtures/apps/with_default_directories/lib/
|
187
|
-
- spec/fixtures/apps/with_default_directories/lib/process_definitions/
|
184
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/participants/honey_participant.rb
|
185
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/participants/molasses_participant.rb
|
186
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/process_definitions/garbage_collector.rb
|
187
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/process_definitions/make_honey.rb
|
188
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/process_definitions/make_molasses.rb
|
189
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/tasks/make_some_honey_task.rb
|
190
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/tasks/taste_that_molasses_task.rb
|
188
191
|
- spec/fixtures/apps/with_specified_directories/config_initializer.rb
|
189
192
|
- spec/fixtures/apps/with_specified_directories/specific_directory/definitions/.gitkeep
|
190
193
|
- spec/fixtures/apps/with_specified_directories/specific_directory/participants/.gitkeep
|
@@ -217,18 +220,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
217
220
|
- - ! '>='
|
218
221
|
- !ruby/object:Gem::Version
|
219
222
|
version: '0'
|
220
|
-
segments:
|
221
|
-
- 0
|
222
|
-
hash: -2621503672602724538
|
223
223
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
224
224
|
none: false
|
225
225
|
requirements:
|
226
226
|
- - ! '>='
|
227
227
|
- !ruby/object:Gem::Version
|
228
228
|
version: '0'
|
229
|
-
segments:
|
230
|
-
- 0
|
231
|
-
hash: -2621503672602724538
|
232
229
|
requirements: []
|
233
230
|
rubyforge_project:
|
234
231
|
rubygems_version: 1.8.23
|
@@ -236,13 +233,15 @@ signing_key:
|
|
236
233
|
specification_version: 3
|
237
234
|
summary: Framework around ruote[http://github.com/jmettraux/ruote] workflow engine
|
238
235
|
test_files:
|
239
|
-
- spec/fixtures/apps/with_default_directories/app/participants/honey_participant.rb
|
240
|
-
- spec/fixtures/apps/with_default_directories/app/participants/molasses_participant.rb
|
241
236
|
- spec/fixtures/apps/with_default_directories/config_initializer.rb
|
242
237
|
- spec/fixtures/apps/with_default_directories/full_initializer.rb
|
243
|
-
- spec/fixtures/apps/with_default_directories/lib/
|
244
|
-
- spec/fixtures/apps/with_default_directories/lib/
|
245
|
-
- spec/fixtures/apps/with_default_directories/lib/process_definitions/
|
238
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/participants/honey_participant.rb
|
239
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/participants/molasses_participant.rb
|
240
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/process_definitions/garbage_collector.rb
|
241
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/process_definitions/make_honey.rb
|
242
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/process_definitions/make_molasses.rb
|
243
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/tasks/make_some_honey_task.rb
|
244
|
+
- spec/fixtures/apps/with_default_directories/lib/bumbleworks/tasks/taste_that_molasses_task.rb
|
246
245
|
- spec/fixtures/apps/with_specified_directories/config_initializer.rb
|
247
246
|
- spec/fixtures/apps/with_specified_directories/specific_directory/definitions/.gitkeep
|
248
247
|
- spec/fixtures/apps/with_specified_directories/specific_directory/participants/.gitkeep
|
@@ -262,3 +261,4 @@ test_files:
|
|
262
261
|
- spec/lib/bumbleworks_spec.rb
|
263
262
|
- spec/spec_helper.rb
|
264
263
|
- spec/support/path_helpers.rb
|
264
|
+
has_rdoc:
|