bumbleworks 0.0.69 → 0.0.70
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/lib/bumbleworks.rb +1 -0
- data/lib/bumbleworks/configuration.rb +4 -4
- data/lib/bumbleworks/ruote/exp/broadcast_event_expression.rb +4 -1
- data/lib/bumbleworks/ruote/exp/wait_for_event_expression.rb +4 -1
- data/lib/bumbleworks/support/flow_expression.rb +18 -0
- data/lib/bumbleworks/user.rb +91 -0
- data/lib/bumbleworks/version.rb +1 -1
- data/spec/lib/bumbleworks/ruote/exp/broadcast_event_expression_spec.rb +16 -0
- data/spec/lib/bumbleworks/ruote/exp/wait_for_event_expression_spec.rb +17 -0
- data/spec/lib/bumbleworks/user_spec.rb +152 -0
- metadata +8 -4
data/lib/bumbleworks.rb
CHANGED
|
@@ -299,10 +299,10 @@ module Bumbleworks
|
|
|
299
299
|
|
|
300
300
|
def framework_root
|
|
301
301
|
case
|
|
302
|
-
when defined?(Rails) then Rails.root
|
|
303
|
-
when defined?(Rory) then Rory.root
|
|
304
|
-
when defined?(Padrino) then Padrino.root
|
|
305
|
-
when defined?(Sinatra::Application) then Sinatra::Application.root
|
|
302
|
+
when defined?(::Rails) then ::Rails.root
|
|
303
|
+
when defined?(::Rory) then ::Rory.root
|
|
304
|
+
when defined?(::Padrino) then ::Padrino.root
|
|
305
|
+
when defined?(::Sinatra::Application) then ::Sinatra::Application.root
|
|
306
306
|
end
|
|
307
307
|
end
|
|
308
308
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
require 'ruote/exp/flow_expression'
|
|
2
|
+
require 'bumbleworks/support/flow_expression'
|
|
2
3
|
|
|
3
4
|
module Ruote::Exp
|
|
4
5
|
class BroadcastEventExpression < FlowExpression
|
|
6
|
+
include Bumbleworks::Support::FlowExpression
|
|
7
|
+
|
|
5
8
|
names :broadcast_event
|
|
6
9
|
|
|
7
10
|
def consider_tag
|
|
8
11
|
update_tree
|
|
9
|
-
h.updated_tree[1]['tag'] =
|
|
12
|
+
h.updated_tree[1]['tag'] = tag_from_attribute
|
|
10
13
|
super
|
|
11
14
|
end
|
|
12
15
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
require 'ruote/exp/flow_expression'
|
|
2
2
|
require 'ruote/exp/fe_await'
|
|
3
|
+
require 'bumbleworks/support/flow_expression'
|
|
3
4
|
|
|
4
5
|
module Ruote::Exp
|
|
5
6
|
class WaitForEventExpression < AwaitExpression
|
|
7
|
+
include Bumbleworks::Support::FlowExpression
|
|
8
|
+
|
|
6
9
|
names :wait_for_event
|
|
7
10
|
|
|
8
11
|
# This does the same as the base AwaitExpression#apply, except that this
|
|
@@ -13,7 +16,7 @@ module Ruote::Exp
|
|
|
13
16
|
def apply
|
|
14
17
|
update_tree
|
|
15
18
|
h.updated_tree[1]['global'] = true
|
|
16
|
-
h.updated_tree[1]['left_tag'] =
|
|
19
|
+
h.updated_tree[1]['left_tag'] = tag_from_attribute
|
|
17
20
|
h.updated_tree[1]['merge'] = 'drop'
|
|
18
21
|
super
|
|
19
22
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Bumbleworks
|
|
2
|
+
module Support
|
|
3
|
+
module FlowExpression
|
|
4
|
+
def tag_from_attribute
|
|
5
|
+
tag = attribute_text.to_s
|
|
6
|
+
if h.updated_tree[1]['for_entity'].to_s == 'true'
|
|
7
|
+
workitem_fields = h.applied_workitem['fields']
|
|
8
|
+
entity_type, entity_id = workitem_fields.values_at('entity_type', 'entity_id')
|
|
9
|
+
if entity_type && entity_id
|
|
10
|
+
entity_tag = "#{Bumbleworks::Support.tokenize(entity_type)}_#{entity_id}"
|
|
11
|
+
tag += "__for_entity__#{entity_tag}"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
tag
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Bumbleworks
|
|
2
|
+
module User
|
|
3
|
+
class NoRoleIdentifiersMethodDefined < StandardError; end
|
|
4
|
+
class NoClaimTokenMethodDefined < StandardError; end
|
|
5
|
+
class MissingRoleQueryMethod < StandardError; end
|
|
6
|
+
class AmbiguousRoleQueryMethod < StandardError; end
|
|
7
|
+
class UnauthorizedClaimAttempt < StandardError; end
|
|
8
|
+
class UnauthorizedReleaseAttempt < StandardError; end
|
|
9
|
+
|
|
10
|
+
# The return value from this method is used as the "claimant" token on
|
|
11
|
+
# tasks, both for claiming a task and for checking if the user is the
|
|
12
|
+
# current claimant.
|
|
13
|
+
#
|
|
14
|
+
# By default, claim_token will first check for `username`, then `email`, and
|
|
15
|
+
# finally raise an exception if neither method exists. Including classes
|
|
16
|
+
# should override this method if using something other than username or
|
|
17
|
+
# email (or if both respond, but email should be preferred).
|
|
18
|
+
def claim_token
|
|
19
|
+
[:username, :email].each do |token_method|
|
|
20
|
+
return send(token_method) if respond_to?(token_method)
|
|
21
|
+
end
|
|
22
|
+
raise NoClaimTokenMethodDefined,
|
|
23
|
+
"If your user class does not respond to :username or :email, define a `claim_token` method"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The return value from this method is used when determining which tasks in
|
|
27
|
+
# the queue this user should be authorized for. Must return an array of
|
|
28
|
+
# strings.
|
|
29
|
+
def role_identifiers
|
|
30
|
+
raise NoRoleIdentifiersMethodDefined,
|
|
31
|
+
"Define a `role_identifiers` method that returns an array of role names"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns true if the array returned by #role_identifiers includes the given
|
|
35
|
+
# name. Can be used to determine authority to perform actions on a task,
|
|
36
|
+
# for example.
|
|
37
|
+
def has_role?(role_name)
|
|
38
|
+
role_identifiers.include? role_name
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Attempts to set self as the claimant of the given task. If not authorized
|
|
42
|
+
# to claim the task, raises exception. Also bubbles exception from Task
|
|
43
|
+
# when task is already claimed by a different claimant.
|
|
44
|
+
def claim(task)
|
|
45
|
+
raise UnauthorizedClaimAttempt unless has_role?(task.role)
|
|
46
|
+
task.claim(claim_token)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Same as #claim, but first releases (by force) the task, to avoid any
|
|
50
|
+
# possible UnauthorizedClaimAttempt or AlreadyClaimed exceptions. Should
|
|
51
|
+
# only be made available to supervisory roles.
|
|
52
|
+
def claim!(task)
|
|
53
|
+
release!(task)
|
|
54
|
+
claim(task)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# If we are the current claimant of the given task, release the task. Does
|
|
58
|
+
# nothing if the task is not claimed, but raises exception if the task is
|
|
59
|
+
# currently claimed by someone else.
|
|
60
|
+
def release(task, force = false)
|
|
61
|
+
return unless task.claimed?
|
|
62
|
+
raise UnauthorizedReleaseAttempt unless force || task.claimant == claim_token
|
|
63
|
+
task.release
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Same as #release, but releases the task even if we're not the current
|
|
67
|
+
# claimant. Allows an administrator, for example, to wrench a task away
|
|
68
|
+
# from an employee who is lagging. Should only be made available to
|
|
69
|
+
# supervisory roles.
|
|
70
|
+
def release!(task)
|
|
71
|
+
release(task, true)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns Task::Finder instance filtered by roles assigned to this user.
|
|
75
|
+
def authorized_tasks
|
|
76
|
+
Bumbleworks::Task.for_roles(role_identifiers)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns Task::Finder instance filtered by user roles and availability
|
|
80
|
+
# (unclaimed and completable).
|
|
81
|
+
def available_tasks
|
|
82
|
+
authorized_tasks.available
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Returns Task::Finder instance filtered by claimant - only tasks this user
|
|
86
|
+
# has claimed (and not released or completed).
|
|
87
|
+
def claimed_tasks
|
|
88
|
+
Bumbleworks::Task.for_claimant(claim_token)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/bumbleworks/version.rb
CHANGED
|
@@ -21,4 +21,20 @@ describe Ruote::Exp::BroadcastEventExpression do
|
|
|
21
21
|
Bumbleworks.dashboard.wait_for(waiter.wfid)
|
|
22
22
|
@tracer.should == ['amazing']
|
|
23
23
|
end
|
|
24
|
+
|
|
25
|
+
it 'appends entity info to tag when :for_entity is true' do
|
|
26
|
+
Bumbleworks.define_process 'waiter' do
|
|
27
|
+
await :left_tag => :the_event__for_entity__pig_widget_15, :global => true
|
|
28
|
+
echo 'amazing'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Bumbleworks.define_process 'sender' do
|
|
32
|
+
broadcast_event :the_event, :for_entity => true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
waiter = Bumbleworks.launch!('waiter')
|
|
36
|
+
sender = Bumbleworks.launch!('sender', :entity_type => 'PigWidget', :entity_id => 15)
|
|
37
|
+
Bumbleworks.dashboard.wait_for(waiter.wfid)
|
|
38
|
+
@tracer.should == ['amazing']
|
|
39
|
+
end
|
|
24
40
|
end
|
|
@@ -76,4 +76,21 @@ describe Ruote::Exp::WaitForEventExpression do
|
|
|
76
76
|
Bumbleworks.dashboard.wait_for(waiter3.wfid)
|
|
77
77
|
@tracer.should == ['entities! Rhubarb,spitpickle-4boof']
|
|
78
78
|
end
|
|
79
|
+
|
|
80
|
+
it 'appends entity info to expected tag when :for_entity is true' do
|
|
81
|
+
Bumbleworks.define_process 'waiter' do
|
|
82
|
+
wait_for_event :the_event, :for_entity => true
|
|
83
|
+
echo 'i found your tag, sucka'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
Bumbleworks.define_process 'sender' do
|
|
87
|
+
noop :tag => 'the_event__for_entity__fun_face_yellow5'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
waiter = Bumbleworks.launch!('waiter', 'entity_type' => 'FunFace', 'entity_id' => 'yellow5')
|
|
91
|
+
sender = Bumbleworks.launch!('sender')
|
|
92
|
+
|
|
93
|
+
Bumbleworks.dashboard.wait_for(waiter.wfid)
|
|
94
|
+
@tracer.should == ['i found your tag, sucka']
|
|
95
|
+
end
|
|
79
96
|
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
describe Bumbleworks::User do
|
|
2
|
+
let(:user_class) { Class.new { include Bumbleworks::User } }
|
|
3
|
+
let(:subject) { user_class.new }
|
|
4
|
+
|
|
5
|
+
describe '#claim_token' do
|
|
6
|
+
it 'returns username by default' do
|
|
7
|
+
subject.stub(:username => 'nerfobot')
|
|
8
|
+
subject.claim_token.should == 'nerfobot'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'returns email if no username' do
|
|
12
|
+
subject.stub(:email => 'fromp@nougatcountry.com')
|
|
13
|
+
subject.claim_token.should == 'fromp@nougatcountry.com'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'prefers username to email when both respond' do
|
|
17
|
+
subject.stub(:username => 'dumb', :email => 'moar dumb')
|
|
18
|
+
subject.claim_token.should == 'dumb'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'returns nil if method defined' do
|
|
22
|
+
subject.stub(:username)
|
|
23
|
+
subject.claim_token.should be_nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'raises exception if neither username nor email defined' do
|
|
27
|
+
expect {
|
|
28
|
+
subject.claim_token
|
|
29
|
+
}.to raise_error(Bumbleworks::User::NoClaimTokenMethodDefined)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#claim' do
|
|
34
|
+
before(:each) do
|
|
35
|
+
subject.stub(:role_identifiers => ['snoogat'])
|
|
36
|
+
subject.stub(:claim_token => "the umpire of snorts")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'claims a task if authorized' do
|
|
40
|
+
task = double('task', :role => 'snoogat')
|
|
41
|
+
task.should_receive(:claim).with("the umpire of snorts")
|
|
42
|
+
subject.claim(task)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'raises exception if unauthorized' do
|
|
46
|
+
task = double('task', :role => 'fashbone')
|
|
47
|
+
task.should_receive(:claim).never
|
|
48
|
+
expect {
|
|
49
|
+
subject.claim(task)
|
|
50
|
+
}.to raise_error(Bumbleworks::User::UnauthorizedClaimAttempt)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'raises exception if already claimed by another' do
|
|
54
|
+
task = double('task', :role => 'snoogat')
|
|
55
|
+
task.should_receive(:claim).and_raise(Bumbleworks::Task::AlreadyClaimed)
|
|
56
|
+
expect {
|
|
57
|
+
subject.claim(task)
|
|
58
|
+
}.to raise_error(Bumbleworks::Task::AlreadyClaimed)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe '!' do
|
|
62
|
+
it 'resets even if claimed by another' do
|
|
63
|
+
task = double('task', :role => 'snoogat', :claimed? => true, :claimant => 'fumbo the monfey')
|
|
64
|
+
task.should_receive(:release).ordered
|
|
65
|
+
task.should_receive(:claim).with("the umpire of snorts").ordered
|
|
66
|
+
subject.claim!(task)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe '#release' do
|
|
72
|
+
before(:each) do
|
|
73
|
+
subject.stub(:claim_token => "the umpire of snorts")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'releases a claimed task if claimant' do
|
|
77
|
+
task = double('task', :claimed? => true, :claimant => "the umpire of snorts")
|
|
78
|
+
task.should_receive(:release)
|
|
79
|
+
subject.release(task)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'does nothing if task not claimed' do
|
|
83
|
+
task = double('task', :claimed? => false)
|
|
84
|
+
task.should_receive(:release).never
|
|
85
|
+
subject.release(task)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'raises exception if not claimant' do
|
|
89
|
+
task = double('task', :claimed? => true, :claimant => 'a castanet expert')
|
|
90
|
+
task.should_receive(:release).never
|
|
91
|
+
expect {
|
|
92
|
+
subject.release(task)
|
|
93
|
+
}.to raise_error(Bumbleworks::User::UnauthorizedReleaseAttempt)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe '!' do
|
|
97
|
+
it 'releases even if not claimant' do
|
|
98
|
+
task = double('task', :claimed? => true, :claimant => 'a castanet expert')
|
|
99
|
+
task.should_receive(:release)
|
|
100
|
+
subject.release!(task)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe '#role_identifiers' do
|
|
106
|
+
it 'raises exception by default' do
|
|
107
|
+
expect {
|
|
108
|
+
subject.role_identifiers
|
|
109
|
+
}.to raise_error(Bumbleworks::User::NoRoleIdentifiersMethodDefined)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#has_role?' do
|
|
114
|
+
before(:each) do
|
|
115
|
+
subject.stub(:role_identifiers => ['role1', 'role2'])
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'returns true if role_identifiers includes given role' do
|
|
119
|
+
subject.has_role?('role1').should be_true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'returns false if role_identifiers does not include given role' do
|
|
123
|
+
subject.has_role?('role3').should be_false
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe '#authorized_tasks' do
|
|
128
|
+
it 'returns task query for all tasks for user roles' do
|
|
129
|
+
subject.stub(:role_identifiers => ['goose', 'midget'])
|
|
130
|
+
Bumbleworks::Task.stub(:for_roles).with(['goose', 'midget']).and_return(:all_the_tasks)
|
|
131
|
+
subject.authorized_tasks.should == :all_the_tasks
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
describe '#claimed_tasks' do
|
|
136
|
+
it 'returns task query for all user claimed tasks' do
|
|
137
|
+
subject.stub(:claim_token => :yay_its_me)
|
|
138
|
+
Bumbleworks::Task.stub(:for_claimant).with(:yay_its_me).and_return(:my_tasks)
|
|
139
|
+
subject.claimed_tasks.should == :my_tasks
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe '#available_tasks' do
|
|
144
|
+
it 'returns authorized tasks filtered by available' do
|
|
145
|
+
subject.stub(:role_identifiers => ['goose', 'midget'])
|
|
146
|
+
task_finder = double('task_finder')
|
|
147
|
+
task_finder.stub(:available => :only_the_available_tasks)
|
|
148
|
+
Bumbleworks::Task.stub(:for_roles).with(['goose', 'midget']).and_return(task_finder)
|
|
149
|
+
subject.available_tasks.should == :only_the_available_tasks
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
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.70
|
|
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: 2014-02-
|
|
15
|
+
date: 2014-02-19 00:00:00.000000000 Z
|
|
16
16
|
dependencies:
|
|
17
17
|
- !ruby/object:Gem::Dependency
|
|
18
18
|
name: ruote
|
|
@@ -187,11 +187,13 @@ files:
|
|
|
187
187
|
- lib/bumbleworks/simple_logger.rb
|
|
188
188
|
- lib/bumbleworks/storage_adapter.rb
|
|
189
189
|
- lib/bumbleworks/support.rb
|
|
190
|
+
- lib/bumbleworks/support/flow_expression.rb
|
|
190
191
|
- lib/bumbleworks/task.rb
|
|
191
192
|
- lib/bumbleworks/task/base.rb
|
|
192
193
|
- lib/bumbleworks/task/finder.rb
|
|
193
194
|
- lib/bumbleworks/tracker.rb
|
|
194
195
|
- lib/bumbleworks/tree_builder.rb
|
|
196
|
+
- lib/bumbleworks/user.rb
|
|
195
197
|
- lib/bumbleworks/version.rb
|
|
196
198
|
- lib/bumbleworks/workitem.rb
|
|
197
199
|
- lib/bumbleworks/workitem_entity_storage.rb
|
|
@@ -240,6 +242,7 @@ files:
|
|
|
240
242
|
- spec/lib/bumbleworks/task_spec.rb
|
|
241
243
|
- spec/lib/bumbleworks/tracker_spec.rb
|
|
242
244
|
- spec/lib/bumbleworks/tree_builder_spec.rb
|
|
245
|
+
- spec/lib/bumbleworks/user_spec.rb
|
|
243
246
|
- spec/lib/bumbleworks/workitem_entity_storage_spec.rb
|
|
244
247
|
- spec/lib/bumbleworks/workitem_spec.rb
|
|
245
248
|
- spec/lib/bumbleworks_spec.rb
|
|
@@ -263,7 +266,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
263
266
|
version: '0'
|
|
264
267
|
segments:
|
|
265
268
|
- 0
|
|
266
|
-
hash:
|
|
269
|
+
hash: -1442108325624690777
|
|
267
270
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
271
|
none: false
|
|
269
272
|
requirements:
|
|
@@ -272,7 +275,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
272
275
|
version: '0'
|
|
273
276
|
segments:
|
|
274
277
|
- 0
|
|
275
|
-
hash:
|
|
278
|
+
hash: -1442108325624690777
|
|
276
279
|
requirements: []
|
|
277
280
|
rubyforge_project:
|
|
278
281
|
rubygems_version: 1.8.23
|
|
@@ -324,6 +327,7 @@ test_files:
|
|
|
324
327
|
- spec/lib/bumbleworks/task_spec.rb
|
|
325
328
|
- spec/lib/bumbleworks/tracker_spec.rb
|
|
326
329
|
- spec/lib/bumbleworks/tree_builder_spec.rb
|
|
330
|
+
- spec/lib/bumbleworks/user_spec.rb
|
|
327
331
|
- spec/lib/bumbleworks/workitem_entity_storage_spec.rb
|
|
328
332
|
- spec/lib/bumbleworks/workitem_spec.rb
|
|
329
333
|
- spec/lib/bumbleworks_spec.rb
|