qabot 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/dbclasses.rb +499 -0
- data/lib/qabot.rb +249 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1a4a77bc1c0e5d1a1667b935df168d08e7d34e71
|
4
|
+
data.tar.gz: d892ff34caba0b887d45396ac3aa6f49c5e28e21
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 004704ab96d13f2ed72231a711d837be00bea93c7d341fbe5d2dc640d577620cd518e12f595636d7b9ecd55d199d95a297b2257e91062b9b5380d41d20c69579
|
7
|
+
data.tar.gz: dbdff29081158455c92ed69be4ab107869e8744463ee2094d9d14cd303cf0969514c096808bc87f3b71822b26ed4988c98eb836d64b6e918a3ed3101b3d21fbd
|
data/lib/dbclasses.rb
ADDED
@@ -0,0 +1,499 @@
|
|
1
|
+
# sequel class fro the qacategory table
|
2
|
+
module BlackStack
|
3
|
+
class User < Sequel::Model(:user)
|
4
|
+
one_to_many :boolflags, :class=>:'BlackStack::QABot::BoolFlag', :key=>:id_user
|
5
|
+
one_to_many :intflags, :class=>:'BlackStack::QABot::IntFlag', :key=>:id_user
|
6
|
+
one_to_many :floatflags, :class=>:'BlackStack::QABot::FloatFlag', :key=>:id_user
|
7
|
+
one_to_many :categories, :class=>:'BlackStack::QABot::Category', :key=>:id_user
|
8
|
+
|
9
|
+
# return all flags of this user
|
10
|
+
def flags()
|
11
|
+
return self.boolflags + self.intflags + self.floatflags
|
12
|
+
end
|
13
|
+
|
14
|
+
end # class User
|
15
|
+
|
16
|
+
class Client < Sequel::Model(:client)
|
17
|
+
# return all flags of all the users of this client
|
18
|
+
def flags()
|
19
|
+
return self.users.map{|u| u.flags}.flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
# return all categories of all the users of this client
|
23
|
+
def categories()
|
24
|
+
return self.users.map{|u| u.categories}.flatten
|
25
|
+
end
|
26
|
+
|
27
|
+
end # class Client
|
28
|
+
|
29
|
+
module QABot
|
30
|
+
#
|
31
|
+
class Category < Sequel::Model(:qacategory)
|
32
|
+
BlackStack::QABot::Category.dataset = BlackStack::QABot::Category.dataset.disable_insert_output
|
33
|
+
many_to_one :user, :class=>:'BlackStack::User', :key=>:id_user
|
34
|
+
one_to_many :boolflags, :class=>:'BlackStack::BoolFlag', :key=>:id_qacategory
|
35
|
+
one_to_many :intflags, :class=>:'BlackStack::IntFlag', :key=>:id_qacategory
|
36
|
+
one_to_many :floatflags, :class=>:'BlackStack::FloatFlag', :key=>:id_qacategory
|
37
|
+
end # class Category
|
38
|
+
|
39
|
+
#
|
40
|
+
class Alert < Sequel::Model(:qaalert)
|
41
|
+
many_to_one :user, :class=>:'BlackStack::User', :key=>:id_user
|
42
|
+
many_to_one :receiver, :class=>:'BlackStack::User', :key=>:id_receiver
|
43
|
+
|
44
|
+
end # class Alert
|
45
|
+
|
46
|
+
#
|
47
|
+
class BoolFlag < Sequel::Model(:qaboolflag)
|
48
|
+
BlackStack::QABot::BoolFlag.dataset = BlackStack::QABot::BoolFlag.dataset.disable_insert_output
|
49
|
+
include BlackStack::QABot::Flag
|
50
|
+
many_to_one :user, :class=>:'BlackStack::User', :key=>:id_user
|
51
|
+
many_to_one :category, :class=>:'BlackStack::QABot::Category', :key=>:id_category
|
52
|
+
one_to_many :alerts, :class=>:'BlackStack::QABot::Alert', :key=>:id_qaxflag
|
53
|
+
|
54
|
+
# map the attributes of this object to a hash.
|
55
|
+
# add custom elements to the hash: `:up_to_date`, `:last_update_description`
|
56
|
+
# return such a hash.
|
57
|
+
def to_hash()
|
58
|
+
{
|
59
|
+
:id => self.id,
|
60
|
+
:type => BlackStack::QABot::BOOL,
|
61
|
+
:id_qacategory => self.id_qacategory,
|
62
|
+
:create_time => self.create_time,
|
63
|
+
:delete_time => self.delete_time,
|
64
|
+
:id_user => self.id_user,
|
65
|
+
:name => self.name,
|
66
|
+
:html_description => self.html_description,
|
67
|
+
# how often run this qa check - what is the frequency to show the timeline -- ss, mi, hh, mm, dd, qq, yy
|
68
|
+
:trace_frequency_period => self.trace_frequency_period,
|
69
|
+
:trace_frequency_units => self.trace_frequency_units,
|
70
|
+
# show this flag public in the web
|
71
|
+
:public => self.public,
|
72
|
+
# what is the latest value reported
|
73
|
+
:value => self.value,
|
74
|
+
# what is the value to show red (true or false).
|
75
|
+
:trigger_red => self.trigger_red,
|
76
|
+
# each time the value is changed, you can show a comment about the last status.
|
77
|
+
:alert_comments => self.alert_comments,
|
78
|
+
# if it is currently red
|
79
|
+
:stat_red => self.stat_red,
|
80
|
+
# order to show it inside the category, in the dashboard
|
81
|
+
:order => self.order,
|
82
|
+
# sharing this with other people, so other people can add my alert on their dashboards.
|
83
|
+
:share => self.share,
|
84
|
+
# activate this flag to show in the flags column of the dashboard
|
85
|
+
:show_as_flag => self.show_as_flag,
|
86
|
+
# activate tis flag to show in the timelines column of the dashboard
|
87
|
+
:show_as_timeline => self.show_as_timeline,
|
88
|
+
# custom elements
|
89
|
+
:up_to_date => self.up_to_date?,
|
90
|
+
:last_update_description => self.last_update_description,
|
91
|
+
}
|
92
|
+
end # def to_hash()
|
93
|
+
|
94
|
+
# it is red if the `value` is the same than the value assigned to `trigger_red`
|
95
|
+
def is_red?
|
96
|
+
self.value == self.trigger_red
|
97
|
+
end
|
98
|
+
|
99
|
+
# map the attributes of the hash `h` to this object.
|
100
|
+
def parse(h)
|
101
|
+
self.value = h[:value] == true
|
102
|
+
self.value_red = h[:value_red] == true
|
103
|
+
q = "
|
104
|
+
UPDATE qaboolflag
|
105
|
+
SET
|
106
|
+
id_qacategory = '#{h[:id_qacategory]}',
|
107
|
+
--create_time = '#{h[:create_time]}',
|
108
|
+
--delete_time = '#{h[:delete_time]}',
|
109
|
+
id_user = '#{h[:id_user]}',
|
110
|
+
name = '#{h[:name].to_s.to_sql}',
|
111
|
+
html_description = '#{h[:html_description].to_s.to_sql}',
|
112
|
+
-- how often run this qa check - what is the frequency to show the timeline -- ss, mi, hh, mm, dd, qq, yy
|
113
|
+
trace_frequency_period = '#{h[:trace_frequency_period]}',
|
114
|
+
trace_frequency_units = #{h[:trace_frequency_units]},
|
115
|
+
-- show this flag public in the web
|
116
|
+
[public] = #{h[:public] == true ? 1 : 0},
|
117
|
+
-- what is the latest value reported
|
118
|
+
[value] = #{h[:value] == true ? 1 : 0},
|
119
|
+
-- what is the value to show red (true or false). Default value is `false`.
|
120
|
+
trigger_red = #{h[:trigger_red] == true ? 1 : 0},
|
121
|
+
-- each time the value is changed, you can show a comment about the last status.
|
122
|
+
alert_comments = '#{h[:alert_comments].to_s.to_sql}',
|
123
|
+
-- if it is currently red
|
124
|
+
stat_red = #{self.is_red? ? 1 : 0},
|
125
|
+
-- order to show it inside the category, in the dashboard
|
126
|
+
[order] = #{h[:order].to_i},
|
127
|
+
-- sharing this with other people, so other people can add my alert on their dashboards.
|
128
|
+
share = #{h[:share] == true ? 1 : 0},
|
129
|
+
-- activate this flag to show in the flags column of the dashboard
|
130
|
+
show_as_flag = #{h[:show_as_flag] == true ? 1 : 0},
|
131
|
+
-- activate tis flag to show in the timelines column of the dashboard
|
132
|
+
show_as_timeline = #{h[:show_as_timeline] == true ? 1 : 0}
|
133
|
+
WHERE
|
134
|
+
id = '#{h[:id]}'
|
135
|
+
"
|
136
|
+
#puts
|
137
|
+
#puts
|
138
|
+
#puts q
|
139
|
+
#puts
|
140
|
+
#puts
|
141
|
+
DB.execute(q)
|
142
|
+
end # def parse(h)
|
143
|
+
|
144
|
+
# create a new object.
|
145
|
+
# map the attributes of the hash `h` to this object.
|
146
|
+
# return such an object.
|
147
|
+
def self.create(client, h)
|
148
|
+
u = client.users.first # TODO: the email of the user should be included in the hash descriptor, and validate the user exists and is belonging this client.
|
149
|
+
g = client.categories.select { |g| g.name == h[:category] }.first
|
150
|
+
if g.nil?
|
151
|
+
g = BlackStack::QABot::Category.new
|
152
|
+
g.id = guid()
|
153
|
+
g.create_time = now()
|
154
|
+
g.id_user = u.id
|
155
|
+
g.name = h[:category]
|
156
|
+
g.save
|
157
|
+
end # g.nil?
|
158
|
+
o = BlackStack::QABot::BoolFlag.new
|
159
|
+
h[:id] = guid()
|
160
|
+
h[:id_qacategory] = g.id
|
161
|
+
h[:id_user] = u.id
|
162
|
+
h[:create_time] = now()
|
163
|
+
o.parse(h)
|
164
|
+
o
|
165
|
+
end # def create(h)
|
166
|
+
|
167
|
+
# return true if the flag has been updated after `dateadd(#{self.trace_frequency_period}, -#{self.trace_frequency_units}, getdate())`
|
168
|
+
def up_to_date?
|
169
|
+
!DB["
|
170
|
+
select top 1 id
|
171
|
+
from qaboolflagtrace with (nolock)
|
172
|
+
where trace_time > dateadd(#{self.trace_frequency_period}, -#{self.trace_frequency_units}, getdate())
|
173
|
+
and id_qaboolflag = '#{self.id}'
|
174
|
+
"].first.nil?
|
175
|
+
end
|
176
|
+
|
177
|
+
# return a label to describe when this flag has been updated the last time
|
178
|
+
def last_update_description
|
179
|
+
row = DB["
|
180
|
+
select top 1 dbo.fnTimeAgoDescription(trace_time, getdate()) as timeago
|
181
|
+
from qaboolflagtrace with (nolock)
|
182
|
+
where id_qaboolflag = '#{self.id}'
|
183
|
+
order by trace_time desc
|
184
|
+
"].first
|
185
|
+
return !row.nil? ? row[:timeago] : 'never'
|
186
|
+
end
|
187
|
+
end # class BoolFlag
|
188
|
+
|
189
|
+
#
|
190
|
+
class IntFlag < Sequel::Model(:qaintflag)
|
191
|
+
BlackStack::QABot::IntFlag.dataset = BlackStack::QABot::IntFlag.dataset.disable_insert_output
|
192
|
+
include BlackStack::QABot::Flag
|
193
|
+
many_to_one :user, :class=>:'BlackStack::User', :key=>:id_user
|
194
|
+
many_to_one :category, :class=>:'BlackStack::QABot::Category', :key=>:id_category
|
195
|
+
one_to_many :alerts, :class=>:'BlackStack::QABot::Alert', :key=>:id_qaxflag
|
196
|
+
|
197
|
+
# map the attributes of this object to a hash.
|
198
|
+
# add custom elements to the hash: `:up_to_date`, `:last_update_description`
|
199
|
+
# return such a hash.
|
200
|
+
def to_hash()
|
201
|
+
{
|
202
|
+
:id => self.id,
|
203
|
+
:type => BlackStack::QABot::INT,
|
204
|
+
:id_qacategory => self.id_qacategory,
|
205
|
+
:create_time => self.create_time,
|
206
|
+
:delete_time => self.delete_time,
|
207
|
+
:id_user => self.id_user,
|
208
|
+
:name => self.name,
|
209
|
+
:html_description => self.html_description,
|
210
|
+
# how often run this qa check - what is the frequency to show the timeline -- ss, mi, hh, mm, dd, qq, yy
|
211
|
+
:trace_frequency_period => self.trace_frequency_period,
|
212
|
+
:trace_frequency_units => self.trace_frequency_units,
|
213
|
+
# show this flag public in the web
|
214
|
+
:public => self.public,
|
215
|
+
# what is the latest value reported
|
216
|
+
:value => self.value,
|
217
|
+
# what is the value to decide to go green or red.
|
218
|
+
value_threshold => self.value_threshold,
|
219
|
+
# what is the value to show red (true or false).
|
220
|
+
:trigger_red => self.trigger_red,
|
221
|
+
# each time the value is changed, you can show a comment about the last status.
|
222
|
+
:alert_comments => self.alert_comments,
|
223
|
+
# if it is currently red
|
224
|
+
:stat_red => self.stat_red,
|
225
|
+
# order to show it inside the category, in the dashboard
|
226
|
+
:order => self.order,
|
227
|
+
# sharing this with other people, so other people can add my alert on their dashboards.
|
228
|
+
:share => self.share,
|
229
|
+
# activate this flag to show in the flags column of the dashboard
|
230
|
+
:show_as_flag => self.show_as_flag,
|
231
|
+
# activate tis flag to show in the timelines column of the dashboard
|
232
|
+
:show_as_timeline => self.show_as_timeline,
|
233
|
+
# custom elements
|
234
|
+
:up_to_date => self.up_to_date?,
|
235
|
+
:last_update_description => self.last_update_description,
|
236
|
+
}
|
237
|
+
end # def to_hash()
|
238
|
+
|
239
|
+
# it is red if the `value` is the same than the value assigned to `trigger_red`
|
240
|
+
def is_red?
|
241
|
+
if self.trigger_red == BlackStack::QABot::LOWER_OR_EQUAL
|
242
|
+
return self.value <= self.value_threshold
|
243
|
+
elsif self.trigger_red == BlackStack::QABot::LOWER
|
244
|
+
return self.value < self.value_threshold
|
245
|
+
elsif self.trigger_red == BlackStack::QABot::EQUAL
|
246
|
+
return self.value == self.value_threshold
|
247
|
+
elsif self.trigger_red == BlackStack::QABot::GREATER
|
248
|
+
return self.value > self.value_threshold
|
249
|
+
elsif self.trigger_red == BlackStack::QABot::GREATER_OR_EQUAL
|
250
|
+
return self.value >= self.value_threshold
|
251
|
+
else
|
252
|
+
return nil
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# map the attributes of the hash `h` to this object.
|
257
|
+
def parse(h)
|
258
|
+
self.value = h[:value].to_i
|
259
|
+
self.value_threshold = h[:value_threshold].to_i
|
260
|
+
self.value_red = h[:value_red].to_i
|
261
|
+
q = "
|
262
|
+
UPDATE qaboolflag
|
263
|
+
SET
|
264
|
+
id_qacategory = '#{h[:id_qacategory]}',
|
265
|
+
--create_time = '#{h[:create_time]}',
|
266
|
+
--delete_time = '#{h[:delete_time]}',
|
267
|
+
id_user = '#{h[:id_user]}',
|
268
|
+
name = '#{h[:name].to_s.to_sql}',
|
269
|
+
html_description = '#{h[:html_description].to_s.to_sql}',
|
270
|
+
-- how often run this qa check - what is the frequency to show the timeline -- ss, mi, hh, mm, dd, qq, yy
|
271
|
+
trace_frequency_period = '#{h[:trace_frequency_period]}',
|
272
|
+
trace_frequency_units = #{h[:trace_frequency_units]},
|
273
|
+
-- show this flag public in the web
|
274
|
+
[public] = #{h[:public] == true ? 1 : 0},
|
275
|
+
-- what is the latest value reported
|
276
|
+
[value] = #{h[:value].to_i.to_s},
|
277
|
+
-- what is the value to decide to go green or red.
|
278
|
+
value_threshold = #{h[:value_threshold].to_i.to_s},
|
279
|
+
-- what is the value to show red (true or false). Default value is `false`.
|
280
|
+
trigger_red = #{h[:trigger_red].to_i.to_s},
|
281
|
+
-- each time the value is changed, you can show a comment about the last status.
|
282
|
+
alert_comments = '#{h[:alert_comments].to_s.to_sql}',
|
283
|
+
-- if it is currently red
|
284
|
+
stat_red = #{self.is_red? ? 1 : 0},
|
285
|
+
-- order to show it inside the category, in the dashboard
|
286
|
+
[order] = #{h[:order].to_i},
|
287
|
+
-- sharing this with other people, so other people can add my alert on their dashboards.
|
288
|
+
share = #{h[:share] == true ? 1 : 0},
|
289
|
+
-- activate this flag to show in the flags column of the dashboard
|
290
|
+
show_as_flag = #{h[:show_as_flag] == true ? 1 : 0},
|
291
|
+
-- activate tis flag to show in the timelines column of the dashboard
|
292
|
+
show_as_timeline = #{h[:show_as_timeline] == true ? 1 : 0}
|
293
|
+
WHERE
|
294
|
+
id = '#{h[:id]}'
|
295
|
+
"
|
296
|
+
DB.execute(q)
|
297
|
+
end # def parse(h)
|
298
|
+
|
299
|
+
# create a new object.
|
300
|
+
# map the attributes of the hash `h` to this object.
|
301
|
+
# return such an object.
|
302
|
+
def self.create(client, h)
|
303
|
+
u = client.users.first # TODO: the email of the user should be included in the hash descriptor, and validate the user exists and is belonging this client.
|
304
|
+
g = client.categories.select { |g| g.name == h[:category] }.first
|
305
|
+
if g.nil?
|
306
|
+
g = BlackStack::QABot::Category.new
|
307
|
+
g.id = guid()
|
308
|
+
g.create_time = now()
|
309
|
+
g.id_user = u.id
|
310
|
+
g.name = h[:category]
|
311
|
+
g.save
|
312
|
+
end # g.nil?
|
313
|
+
o = BlackStack::QABot::IntFlag.new
|
314
|
+
h[:id] = guid()
|
315
|
+
h[:id_qacategory] = g.id
|
316
|
+
h[:id_user] = u.id
|
317
|
+
h[:create_time] = now()
|
318
|
+
o.parse(h)
|
319
|
+
o
|
320
|
+
end # def parse(h)
|
321
|
+
|
322
|
+
# return true if the flag has been updated after `dateadd(#{self.trace_frequency_period}, -#{self.trace_frequency_units}, getdate())`
|
323
|
+
def up_to_date?
|
324
|
+
!DB["
|
325
|
+
select top 1 id
|
326
|
+
from qaintflagtrace with (nolock)
|
327
|
+
where trace_time > dateadd(#{self.trace_frequency_period}, -#{self.trace_frequency_units}, getdate())
|
328
|
+
and id_qaintflag = '#{self.id}'
|
329
|
+
"].first.nil?
|
330
|
+
end
|
331
|
+
|
332
|
+
# return a label to describe when this flag has been updated the last time
|
333
|
+
def last_update_description
|
334
|
+
row = DB["
|
335
|
+
select top 1 dbo.fnTimeAgoDescription(trace_time, getdate()) as timeago
|
336
|
+
from qaintflagtrace with (nolock)
|
337
|
+
where id_qaintflag = '#{self.id}'
|
338
|
+
order by trace_time desc
|
339
|
+
"].first
|
340
|
+
return !row.nil? ? row[:timeago] : 'never'
|
341
|
+
end
|
342
|
+
end # class IntFlag
|
343
|
+
|
344
|
+
#
|
345
|
+
class FloatFlag < Sequel::Model(:qafloatflag)
|
346
|
+
BlackStack::QABot::FloatFlag.dataset = BlackStack::QABot::FloatFlag.dataset.disable_insert_output
|
347
|
+
include BlackStack::QABot::Flag
|
348
|
+
many_to_one :user, :class=>:'BlackStack::User', :key=>:id_user
|
349
|
+
many_to_one :category, :class=>:'BlackStack::QABot::Category', :key=>:id_category
|
350
|
+
one_to_many :alerts, :class=>:'BlackStack::QABot::Alert', :key=>:id_qaxflag
|
351
|
+
|
352
|
+
# map the attributes of this object to a hash.
|
353
|
+
# add custom elements to the hash: `:up_to_date`, `:last_update_description`
|
354
|
+
# return such a hash.
|
355
|
+
def to_hash()
|
356
|
+
{
|
357
|
+
:id => self.id,
|
358
|
+
:type => BlackStack::QABot::FLOAT,
|
359
|
+
:id_qacategory => self.id_qacategory,
|
360
|
+
:create_time => self.create_time,
|
361
|
+
:delete_time => self.delete_time,
|
362
|
+
:id_user => self.id_user,
|
363
|
+
:name => self.name,
|
364
|
+
:html_description => self.html_description,
|
365
|
+
# how often run this qa check - what is the frequency to show the timeline -- ss, mi, hh, mm, dd, qq, yy
|
366
|
+
:trace_frequency_period => self.trace_frequency_period,
|
367
|
+
:trace_frequency_units => self.trace_frequency_units,
|
368
|
+
# show this flag public in the web
|
369
|
+
:public => self.public,
|
370
|
+
# what is the latest value reported
|
371
|
+
:value => self.value,
|
372
|
+
# what is the value to decide to go green or red.
|
373
|
+
value_threshold => self.value_threshold,
|
374
|
+
# what is the value to show red (true or false).
|
375
|
+
:trigger_red => self.trigger_red,
|
376
|
+
# each time the value is changed, you can show a comment about the last status.
|
377
|
+
:alert_comments => self.alert_comments,
|
378
|
+
# if it is currently red
|
379
|
+
:stat_red => self.stat_red,
|
380
|
+
# order to show it inside the category, in the dashboard
|
381
|
+
:order => self.order,
|
382
|
+
# sharing this with other people, so other people can add my alert on their dashboards.
|
383
|
+
:share => self.share,
|
384
|
+
# activate this flag to show in the flags column of the dashboard
|
385
|
+
:show_as_flag => self.show_as_flag,
|
386
|
+
# activate tis flag to show in the timelines column of the dashboard
|
387
|
+
:show_as_timeline => self.show_as_timeline,
|
388
|
+
# custom elements
|
389
|
+
:up_to_date => self.up_to_date?,
|
390
|
+
:last_update_description => self.last_update_description,
|
391
|
+
}
|
392
|
+
end # def to_hash()
|
393
|
+
|
394
|
+
# it is red if the `value` is the same than the value assigned to `trigger_red`
|
395
|
+
def is_red?
|
396
|
+
if self.trigger_red == BlackStack::QABot::LOWER_OR_EQUAL
|
397
|
+
return self.value <= self.value_threshold
|
398
|
+
elsif self.trigger_red == BlackStack::QABot::LOWER
|
399
|
+
return self.value < self.value_threshold
|
400
|
+
elsif self.trigger_red == BlackStack::QABot::EQUAL
|
401
|
+
return self.value == self.value_threshold
|
402
|
+
elsif self.trigger_red == BlackStack::QABot::GREATER
|
403
|
+
return self.value > self.value_threshold
|
404
|
+
elsif self.trigger_red == BlackStack::QABot::GREATER_OR_EQUAL
|
405
|
+
return self.value >= self.value_threshold
|
406
|
+
else
|
407
|
+
return nil
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
# map the attributes of the hash `h` to this object.
|
412
|
+
def parse(h)
|
413
|
+
self.value = h[:value].to_f
|
414
|
+
self.value_threshold = h[:value_threshold].to_f
|
415
|
+
self.value_red = h[:value_red].to_f
|
416
|
+
q = "
|
417
|
+
UPDATE qaboolflag
|
418
|
+
SET
|
419
|
+
id_qacategory = '#{h[:id_qacategory]}',
|
420
|
+
--create_time = '#{h[:create_time]}',
|
421
|
+
--delete_time = '#{h[:delete_time]}',
|
422
|
+
id_user = '#{h[:id_user]}',
|
423
|
+
name = '#{h[:name].to_s.to_sql}',
|
424
|
+
html_description = '#{h[:html_description].to_s.to_sql}',
|
425
|
+
-- how often run this qa check - what is the frequency to show the timeline -- ss, mi, hh, mm, dd, qq, yy
|
426
|
+
trace_frequency_period = '#{h[:trace_frequency_period]}',
|
427
|
+
trace_frequency_units = #{h[:trace_frequency_units]},
|
428
|
+
-- show this flag public in the web
|
429
|
+
[public] = #{h[:public] == true ? 1 : 0},
|
430
|
+
-- what is the latest value reported
|
431
|
+
[value] = #{h[:value].to_f.to_s},
|
432
|
+
-- what is the value to decide to go green or red.
|
433
|
+
value_threshold = #{h[:value_threshold].to_f.to_s},
|
434
|
+
-- what is the value to show red (true or false). Default value is `false`.
|
435
|
+
trigger_red = #{h[:trigger_red].to_f.to_s},
|
436
|
+
-- each time the value is changed, you can show a comment about the last status.
|
437
|
+
alert_comments = '#{h[:alert_comments].to_s.to_sql}',
|
438
|
+
-- if it is currently red
|
439
|
+
stat_red = #{self.is_red? ? 1 : 0},
|
440
|
+
-- order to show it inside the category, in the dashboard
|
441
|
+
[order] = #{h[:order].to_i},
|
442
|
+
-- sharing this with other people, so other people can add my alert on their dashboards.
|
443
|
+
share = #{h[:share] == true ? 1 : 0},
|
444
|
+
-- activate this flag to show in the flags column of the dashboard
|
445
|
+
show_as_flag = #{h[:show_as_flag] == true ? 1 : 0},
|
446
|
+
-- activate tis flag to show in the timelines column of the dashboard
|
447
|
+
show_as_timeline = #{h[:show_as_timeline] == true ? 1 : 0}
|
448
|
+
WHERE
|
449
|
+
id = '#{h[:id]}'
|
450
|
+
"
|
451
|
+
DB.execute(q)
|
452
|
+
end # def parse(h)
|
453
|
+
|
454
|
+
# create a new object.
|
455
|
+
# map the attributes of the hash `h` to this object.
|
456
|
+
# return such an object.
|
457
|
+
def self.create(h)
|
458
|
+
u = client.users.first # TODO: the email of the user should be included in the hash descriptor, and validate the user exists and is belonging this client.
|
459
|
+
g = client.categories.select { |g| g.name == h[:category] }.first
|
460
|
+
if g.nil?
|
461
|
+
g = BlackStack::QABot::Category.new
|
462
|
+
g.id = guid()
|
463
|
+
g.create_time = now()
|
464
|
+
g.id_user = u.id
|
465
|
+
g.name = h[:category]
|
466
|
+
g.save
|
467
|
+
end # g.nil?
|
468
|
+
o = BlackStack::QABot::FloatFlag.new
|
469
|
+
h[:id] = guid()
|
470
|
+
h[:id_qacategory] = g.id
|
471
|
+
h[:id_user] = u.id
|
472
|
+
h[:create_time] = now()
|
473
|
+
o.parse(h)
|
474
|
+
o
|
475
|
+
end # def parse(h)
|
476
|
+
|
477
|
+
# return true if the flag has been updated after `dateadd(#{self.trace_frequency_period}, -#{self.trace_frequency_units}, getdate())`
|
478
|
+
def up_to_date?
|
479
|
+
!DB["
|
480
|
+
select top 1 id
|
481
|
+
from qafloatflagtrace with (nolock)
|
482
|
+
where trace_time > dateadd(#{self.trace_frequency_period}, -#{self.trace_frequency_units}, getdate())
|
483
|
+
and id_qafloatflag = '#{self.id}'
|
484
|
+
"].first.nil?
|
485
|
+
end
|
486
|
+
|
487
|
+
# return a label to describe when this flag has been updated the last time
|
488
|
+
def last_update_description
|
489
|
+
row = DB["
|
490
|
+
select top 1 dbo.fnTimeAgoDescription(trace_time, getdate()) as timeago
|
491
|
+
from qafloatflagtrace with (nolock)
|
492
|
+
where id_qafloatflag = '#{self.id}'
|
493
|
+
order by trace_time desc
|
494
|
+
"].first
|
495
|
+
return !row.nil? ? row[:timeago] : 'never'
|
496
|
+
end
|
497
|
+
end # class FloatFlag
|
498
|
+
end # module QABot
|
499
|
+
end # module BlackStack
|
data/lib/qabot.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
|
2
|
+
# sequel class fro the qacategory table
|
3
|
+
module BlackStack
|
4
|
+
module QABot
|
5
|
+
BOOL = 'bool'
|
6
|
+
INT = 'int'
|
7
|
+
FLOAT = 'float'
|
8
|
+
|
9
|
+
SS = 'ss'
|
10
|
+
MI = 'mi'
|
11
|
+
HH = 'hh'
|
12
|
+
DD = 'dd'
|
13
|
+
WW = 'ww'
|
14
|
+
MM = 'mm'
|
15
|
+
QQ = 'qq'
|
16
|
+
YY = 'yy'
|
17
|
+
|
18
|
+
LOWER_OR_EQUAL = -2
|
19
|
+
LOWER = -1
|
20
|
+
EQUAL = 0
|
21
|
+
GREATER = 1
|
22
|
+
GREATER_OR_EQUAL = 2
|
23
|
+
|
24
|
+
@@flags_descriptors = []
|
25
|
+
|
26
|
+
def self.require_db_classes()
|
27
|
+
# CRM classes
|
28
|
+
require_relative '../lib/dbclasses.rb'
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.flag_descriptors()
|
32
|
+
@@flags_descriptors
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.types()
|
36
|
+
[
|
37
|
+
BlackStack::QABot::BOOL,
|
38
|
+
BlackStack::QABot::INT,
|
39
|
+
BlackStack::QABot::FLOAT,
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.periods()
|
44
|
+
[
|
45
|
+
BlackStack::QABot::SS,
|
46
|
+
BlackStack::QABot::MI,
|
47
|
+
BlackStack::QABot::HH,
|
48
|
+
BlackStack::QABot::DD,
|
49
|
+
BlackStack::QABot::WW,
|
50
|
+
BlackStack::QABot::MM,
|
51
|
+
BlackStack::QABot::QQ,
|
52
|
+
BlackStack::QABot::YY,
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.trigger_red_values()
|
57
|
+
[
|
58
|
+
BlackStack::QABot::LOWER_OR_EQUAL,
|
59
|
+
BlackStack::QABot::LOWER,
|
60
|
+
BlackStack::QABot::EQUAL,
|
61
|
+
BlackStack::QABot::GREATER_OR_EQUAL,
|
62
|
+
BlackStack::QABot::GREATER,
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Validate the values of the hash, after executing the `:value_function` procedure
|
67
|
+
def self.validate_descriptor_values(h)
|
68
|
+
errors = []
|
69
|
+
# VALIDATE: if `:type` is `BOOL`, `:value` must be boolean.
|
70
|
+
errors << "`:value` must be boolean" if h[:type] == BlackStack::QABot::BOOL && ![true, false].include?(h[:value])
|
71
|
+
# VALIDATE: if `:type` is `INT`, `:value` must be integer.
|
72
|
+
errors << "`:value` must be integer" if h[:type] == BlackStack::QABot::INT && !h[:value].is_a?(Integer)
|
73
|
+
# VALIDATE: if `:type` is `FLOAT`, `:value` must be float.
|
74
|
+
errors << "`:value` must be float" if h[:type] == BlackStack::QABot::FLOAT && !h[:value].is_a?(Float)
|
75
|
+
# return
|
76
|
+
errors
|
77
|
+
end
|
78
|
+
|
79
|
+
# Validate the values of the hash, prior executing the `:value_function` procedure
|
80
|
+
def self.validate_descriptor_parameters_except_functions(h)
|
81
|
+
errors = []
|
82
|
+
# VALIDATE: the `:type` is a valid value
|
83
|
+
errors << "Invalid type: #{h[:type]}" unless BlackStack::QABot.types().include?(h[:type])
|
84
|
+
# VALIDATE: the ':category' is a string no longer than 500 chars
|
85
|
+
errors << "Invalid category: #{h[:category]}. Must be string lower than 500 chars." unless h[:category].is_a?(String) && h[:category].length <= 500
|
86
|
+
# VALIDATE: the `:name` is a string no longer than 500 chars
|
87
|
+
errors << "Invalid name: #{h[:name]}. Must be string lower than 500 chars." unless h[:name].is_a?(String) && h[:name].length <= 500
|
88
|
+
# VALIDATE: the `:description` is a string no longer than 8000 chars
|
89
|
+
errors << "Invalid description: #{h[:description]}. Must be string lower than 8000 chars." unless h[:description].is_a?(String) && h[:description].length <= 8000
|
90
|
+
# VALIDATE: the ':trace_fraquency_period` is a valid value
|
91
|
+
errors << "Invalid trace_frequency_period: #{h[:trace_frequency_period]}" unless BlackStack::QABot.periods().include?(h[:trace_frequency_period])
|
92
|
+
# VALIDATE: the `:trace_frequency_units` is an integer higher than 0
|
93
|
+
errors << "Invalid trace_frequency_units: #{h[:trace_frequency_units]}. Must be integer higher than 0." unless h[:trace_frequency_units].is_a?(Integer) && h[:trace_frequency_units] > 0
|
94
|
+
# VALIDATE: the `:show_as_flag` is a boolean
|
95
|
+
errors << "Invalid show_as_flag: #{h[:show_as_flag]}. Must be boolean." unless h[:show_as_flag].is_a?(TrueClass) || h[:show_as_flag].is_a?(FalseClass)
|
96
|
+
# VALIDATE: the `:show_as_timeline` is a boolean
|
97
|
+
errors << "Invalid show_as_timeline: #{h[:show_as_timeline]}. Must be boolean." unless h[:show_as_timeline].is_a?(TrueClass) || h[:show_as_timeline].is_a?(FalseClass)
|
98
|
+
# VALIDATE: the `:public` is a boolean
|
99
|
+
errors << "Invalid public: #{h[:public]}. Must be boolean." unless h[:public].is_a?(TrueClass) || h[:public].is_a?(FalseClass)
|
100
|
+
# VALIDATE: the `:share` is a boolean
|
101
|
+
errors << "Invalid share: #{h[:share]}. Must be boolean." unless h[:share].is_a?(TrueClass) || h[:share].is_a?(FalseClass)
|
102
|
+
# VALIDATE: if `:type` is `BOOL`, then `:trigger_red` must be a boolean
|
103
|
+
errors << "Invalid trigger_red: #{h[:trigger_red]}. Must be boolean." if h[:type] == BlackStack::QABot::BOOL && !(h[:trigger_red].is_a?(TrueClass) || h[:trigger_red].is_a?(FalseClass))
|
104
|
+
# VALIDATE: if `:type` is `INT` OR `FLOAT`, then `:trigger_red` must be beloning `trigger_red_values`
|
105
|
+
errors << "Invalid trigger_red: #{h[:trigger_red]}. Must be one of #{trigger_red_values.to_s}" if (h[:type] == BlackStack::QABot::INT || h[:type] == BlackStack::QABot::FLOAT) && !trigger_red_values.include?(h[:trigger_red])
|
106
|
+
# VALIDATE: if `:type` is 'INT', then `:value_threshold` must ba an integer.
|
107
|
+
errors << "Invalid trigger_red: #{h[:value_threshold]}. Must be integer." if h[:type] == BlackStack::QABot::INT && !h[:value_threshold].is_a?(Integer)
|
108
|
+
# VALIDATE: if `:ty[e` is `FLOAT`, then `:trigger_red` must be a float.
|
109
|
+
errors << "Invalid trigger_red: #{h[:value_threshold]}. Must be float." if h[:type] == BlackStack::QABot::FLOAT && !h[:value_threshold].is_a?(Float)
|
110
|
+
# return errors
|
111
|
+
errors
|
112
|
+
end # def self.validate_descriptor_parameters_except_functions
|
113
|
+
|
114
|
+
# return a `flag` object belonging this client, with this name
|
115
|
+
def self.find(client, name)
|
116
|
+
return nil if client.nil? || name.nil?
|
117
|
+
return nil if client.flags.nil?
|
118
|
+
client.flags.each do |flag|
|
119
|
+
return flag if flag.name == name
|
120
|
+
end
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# return `true` if exists a flag belonging this client, with this name
|
125
|
+
def self.exists?(client, name)
|
126
|
+
!BlackStack::QABot::find(client, name).nil?
|
127
|
+
end
|
128
|
+
|
129
|
+
# create a new flag belonging this client, with this name
|
130
|
+
def self.create(client, h)
|
131
|
+
o = nil
|
132
|
+
o = BlackStack::QABot::BoolFlag.create(client, h) if h[:type] == BlackStack::QABot::BOOL
|
133
|
+
o = BlackStack::QABot::IntFlag.create(client, h) if h[:type] == BlackStack::QABot::INT
|
134
|
+
o = BlackStack::QABot::FloatFlag.create(client, h) if h[:type] == BlackStack::QABot::FLOAT
|
135
|
+
o
|
136
|
+
end # def self.create
|
137
|
+
|
138
|
+
# update the flag belonging this client, with this name, with the values in the hash descriptor
|
139
|
+
def self.parse(client, h)
|
140
|
+
o = BlackStack::QABot::find(client, h[:name])
|
141
|
+
return nil if o.nil?
|
142
|
+
h[:id] = o.id
|
143
|
+
|
144
|
+
g = client.categories.select { |g| g.name == h[:category] }.first
|
145
|
+
if g.nil?
|
146
|
+
g = BlackStack::QABot::Category.new
|
147
|
+
g.id = guid()
|
148
|
+
g.create_time = now()
|
149
|
+
g.id_user = u.id
|
150
|
+
g.name = h[:category]
|
151
|
+
g.save
|
152
|
+
end # g.nil?
|
153
|
+
h[:id_qacategory] = g.id
|
154
|
+
h[:id_user] = client.users.first.id # TODO: I should receive the email of the email in the hash descriptor, and validate the email is belonging the client.
|
155
|
+
h[:html_description] = h[:description]
|
156
|
+
o.parse(h)
|
157
|
+
o
|
158
|
+
end # def self.parse
|
159
|
+
|
160
|
+
# return `true` if exists a flag belonging this client, with this name
|
161
|
+
def self.exists_by_id?(client, id)
|
162
|
+
return false if client.nil? || id.nil?
|
163
|
+
return false if client.flags.nil?
|
164
|
+
client.flags.each do |flag|
|
165
|
+
return true if flag.id == id
|
166
|
+
end
|
167
|
+
false
|
168
|
+
end
|
169
|
+
|
170
|
+
# return `true` if exists a flag belonging this client, with this name
|
171
|
+
def self.exists_by_id_and_name?(client, id, name)
|
172
|
+
return false if client.nil? || id.nil? || name.nil?
|
173
|
+
return false if client.flags.nil?
|
174
|
+
client.flags.each do |flag|
|
175
|
+
return true if flag.id == id && flag.name == name
|
176
|
+
end
|
177
|
+
false
|
178
|
+
end
|
179
|
+
|
180
|
+
# return `true` if exists a flag belonging this client, with this name
|
181
|
+
def self.exists_by_name?(client, name)
|
182
|
+
return false if client.nil? || name.nil?
|
183
|
+
return false if client.flags.nil?
|
184
|
+
client.flags.each do |flag|
|
185
|
+
return true if flag.name == name
|
186
|
+
end
|
187
|
+
false
|
188
|
+
end
|
189
|
+
|
190
|
+
# return `true` if exists a flag belonging this client, with this name
|
191
|
+
def self.exists_by_name_and_id?(client, name, id)
|
192
|
+
return false if client.nil? || name.nil? || id.nil?
|
193
|
+
return false if client.flags.nil?
|
194
|
+
client.flags.each do |flag|
|
195
|
+
return true if flag.name == name && flag.id == id
|
196
|
+
end
|
197
|
+
false
|
198
|
+
end
|
199
|
+
|
200
|
+
#
|
201
|
+
def self.add_flag(h)
|
202
|
+
# get the errors on all parameters except functions
|
203
|
+
errors = BlackStack::QABot::validate_descriptor_parameters_except_functions(h)
|
204
|
+
# VALIDATE: the `:value_function` is a procedure
|
205
|
+
errors << "Invalid value_function. Must be procedure." unless h[:value_function].is_a?(Proc)
|
206
|
+
# VALIDATE: the `:alert_comments_function` is a procedure
|
207
|
+
errors << "Invalid alert_comments_function. Must be procedure." unless h[:alert_comments_function].is_a?(Proc)
|
208
|
+
# raise an exception if `errors` has any element
|
209
|
+
raise errors.join("\n") if errors.length > 0
|
210
|
+
# add `h` to `@@flags_descriptors`
|
211
|
+
@@flags_descriptors << h
|
212
|
+
# return the array
|
213
|
+
@@flags_descriptors
|
214
|
+
end # def self.add_flag
|
215
|
+
|
216
|
+
def self.run_all()
|
217
|
+
@@flags_descriptors.each do |h|
|
218
|
+
BlackStack::QABot.run(h)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def self.run(h)
|
223
|
+
h[:value] = h[:value_function].call()
|
224
|
+
h[:alert_comments] = h[:alert_comments_function].call()
|
225
|
+
BlackStack::QABot::Flag.push(h)
|
226
|
+
end
|
227
|
+
|
228
|
+
# this module contains methods that are commons to any kind flags
|
229
|
+
module Flag
|
230
|
+
# return true of the latest trace is `stat_red==true`, and the previous trace is `stat_red==false`.
|
231
|
+
def should_alert?
|
232
|
+
# TODO: Code Me!
|
233
|
+
false
|
234
|
+
end # def should_alert?
|
235
|
+
|
236
|
+
# send an email notification to all the receivers of this flag.
|
237
|
+
def alert()
|
238
|
+
# TODO: Code Me!
|
239
|
+
end # def alert()
|
240
|
+
|
241
|
+
# submit a flag descriptor to the server, via REST-API.
|
242
|
+
def self.push(h)
|
243
|
+
url = "#{BlackStack::Pampa::api_url}/api.1.1.0/qabot/push.json"
|
244
|
+
api_key = BlackStack::Pampa::api_key
|
245
|
+
BlackStack::Netting::api_call( url, {:api_key => api_key}.merge(h) )
|
246
|
+
end
|
247
|
+
end # module Flag
|
248
|
+
end # module QABot
|
249
|
+
end # module BlackStack
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qabot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Leandro Daniel Sardi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-04-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.8.1
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.8.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.8.1
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.8.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: tiny_tds
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.0.5
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.0.5
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 1.0.5
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.0.5
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: sequel
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 4.28.0
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 4.28.0
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 4.28.0
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 4.28.0
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: pampa_workers
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 1.1.36
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.1.36
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.1.36
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.1.36
|
93
|
+
description: 'THIS GEM IS STILL IN DEVELOPMENT STAGE. Find documentation here: https://github.com/leandrosardi/pampa_dispatcher.'
|
94
|
+
email: leandro.sardi@expandedventure.com
|
95
|
+
executables: []
|
96
|
+
extensions: []
|
97
|
+
extra_rdoc_files: []
|
98
|
+
files:
|
99
|
+
- lib/dbclasses.rb
|
100
|
+
- lib/qabot.rb
|
101
|
+
homepage: https://rubygems.org/gems/pampa_deployer
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.4.5.1
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: THIS GEM IS STILL IN DEVELOPMENT STAGE. Distribute work along a pool of Pampa
|
125
|
+
workers.
|
126
|
+
test_files: []
|