lita-locker 0.3.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +19 -0
- data/.travis.yml +8 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +84 -0
- data/Rakefile +8 -0
- data/lib/lita-locker.rb +9 -0
- data/lib/lita/handlers/locker.rb +502 -0
- data/lita-locker.gemspec +25 -0
- data/locales/en.yml +61 -0
- data/spec/lita/handlers/locker_spec.rb +352 -0
- data/spec/spec_helper.rb +10 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e5c32b7506039ce73526e9483adfd4243e1aa36d
|
4
|
+
data.tar.gz: 745a609c8595fe3bd6cb8caab9de837b439f9049
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3849f4ad7eec61bc12027688a35ff8002cac08dbcc8b0f0e478b7b3a31ea4f3e928ab33414c2e69fa28687dd005fb0cc0e9c3d7f986fb1b4a26de6fa871fcd84
|
7
|
+
data.tar.gz: 8408eff2c472f5d9f9141d91bed7bf6fa2a3c8e6d5a80e54d38a3eec20bba820d4d81ce78ea68fd2cbc886f7e3066b795e495fc733c9e5e14c934a39d1323ca0
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2014 Eric Sigler
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# lita-locker
|
2
|
+
|
3
|
+
[](https://travis-ci.org/esigler/lita-locker)
|
4
|
+
[](https://tldrlegal.com/license/mit-license)
|
5
|
+
[](https://rubygems.org/gems/lita-locker)
|
6
|
+
[](https://coveralls.io/r/esigler/lita-locker)
|
7
|
+
[](https://codeclimate.com/github/esigler/lita-locker)
|
8
|
+
[](https://gemnasium.com/esigler/lita-locker)
|
9
|
+
|
10
|
+
Locking, unlocking shared resource handler for [lita.io](https://github.com/jimmycuadra/lita).
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add lita-locker to your Lita instance's Gemfile:
|
15
|
+
|
16
|
+
``` ruby
|
17
|
+
gem "lita-locker"
|
18
|
+
```
|
19
|
+
|
20
|
+
## Configuration
|
21
|
+
|
22
|
+
None
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
lita-locker allows you to define resources (such as a server, or Git repo),
|
27
|
+
and labels (such as "production"). Labels can have multiple resources, and
|
28
|
+
resources can be referenced by multiple labels. A label can only be locked
|
29
|
+
if all of the resources it uses are available.
|
30
|
+
|
31
|
+
### Examples
|
32
|
+
```
|
33
|
+
lock web - Make something unavailable to others
|
34
|
+
lock web 30m - Make something unavailable to others for 30 minutes
|
35
|
+
unlock web - Make something available to others
|
36
|
+
locker reserve web - Make yourself the next owner of something after it is unlocked
|
37
|
+
locker status - Show what is and isn't available
|
38
|
+
```
|
39
|
+
|
40
|
+
### Locking, Unlocking, State
|
41
|
+
```
|
42
|
+
lock <subject> - A basic reservation, with no time limit. <subject> can be a resource or a label.
|
43
|
+
unlock <subject> - Remove a reservation. This can only be done by whomever made the request.
|
44
|
+
unlock <subject> force - Force removal of a reservation. This can be done by anyone.
|
45
|
+
```
|
46
|
+
|
47
|
+
### Time-based locking - Not implemented yet!
|
48
|
+
```
|
49
|
+
lock <subject> <time> - A time-limited reservation. <time> must be a number with a "s", "m", or "h" postfix.
|
50
|
+
```
|
51
|
+
|
52
|
+
### Reservations - Not implemented yet!
|
53
|
+
```
|
54
|
+
reserve <subject> - Add yourself to a FIFO queue of pending reservations for <subject>
|
55
|
+
unreserve <subject> - Remove yourself from the queue for <subject>
|
56
|
+
```
|
57
|
+
|
58
|
+
### Labels
|
59
|
+
```
|
60
|
+
locker label list - List all labels
|
61
|
+
locker label create <name> - Create a label with <name>. <name> must be A-Z, a-z, 0-9, no spaces or punctuation.
|
62
|
+
locker label delete <name> - Delete the label with <name>. Clears all locks associated.
|
63
|
+
locker label add <resource> to <name> - Adds <resource> to the list of things to lock/unlock for <name>
|
64
|
+
locker label remove <resource> from <name> - Removes <resource> from <name>
|
65
|
+
locker label show <name> - Show all resources for <name>
|
66
|
+
```
|
67
|
+
|
68
|
+
### Resources
|
69
|
+
```
|
70
|
+
locker resource list - List all resources
|
71
|
+
locker resource create <name> - Create a resource with <name>. <name> must be A-Z, a-z, 0-9, no spaces or punctuation.
|
72
|
+
locker resource delete <name> - Delete the resource with <name>. Clears all locks associated.
|
73
|
+
locker resource show <name> - Show the state of <name>
|
74
|
+
```
|
75
|
+
|
76
|
+
### HTTP access
|
77
|
+
```
|
78
|
+
curl http://lita.example.com/locker/label/<name> - Get current <name> status
|
79
|
+
curl http://lita.example.com/locker/resource/<name> - Get current <name> status
|
80
|
+
```
|
81
|
+
|
82
|
+
## License
|
83
|
+
|
84
|
+
[MIT](http://opensource.org/licenses/MIT)
|
data/Rakefile
ADDED
data/lib/lita-locker.rb
ADDED
@@ -0,0 +1,502 @@
|
|
1
|
+
module Lita
|
2
|
+
module Handlers
|
3
|
+
class Locker < Handler
|
4
|
+
http.get '/locker/label/:name', :http_label_show
|
5
|
+
http.get '/locker/resource/:name', :http_resource_show
|
6
|
+
|
7
|
+
# route(
|
8
|
+
# /^\(lock\)\s([a-zA-Z0-9_-]+)$/,
|
9
|
+
# :lock
|
10
|
+
# )
|
11
|
+
|
12
|
+
# route(
|
13
|
+
# /^\(unlock\)\s([a-zA-Z0-9_-]+)$/,
|
14
|
+
# :unlock
|
15
|
+
# )
|
16
|
+
|
17
|
+
route(
|
18
|
+
/^lock\s([a-zA-Z0-9_-]+)$/,
|
19
|
+
:lock,
|
20
|
+
command: true,
|
21
|
+
help: {
|
22
|
+
t('help.lock_key') => t('help.lock_value')
|
23
|
+
}
|
24
|
+
)
|
25
|
+
|
26
|
+
# route(
|
27
|
+
# /^lock\s([a-zA-Z0-9_-]+)\s(\d+)(s|m|h)$/,
|
28
|
+
# :lock,
|
29
|
+
# command: true,
|
30
|
+
# help: {
|
31
|
+
# t('help.lock_time_key') => t('help.lock_time_value')
|
32
|
+
# }
|
33
|
+
# )
|
34
|
+
|
35
|
+
route(
|
36
|
+
/^unlock\s([a-zA-Z0-9_-]+)$/,
|
37
|
+
:unlock,
|
38
|
+
command: true,
|
39
|
+
help: {
|
40
|
+
t('help.unlock_key') => t('help.unlock_value')
|
41
|
+
}
|
42
|
+
)
|
43
|
+
|
44
|
+
route(
|
45
|
+
/^unlock\s([a-zA-Z0-9_-]+)\sforce$/,
|
46
|
+
:unlock_force,
|
47
|
+
command: true,
|
48
|
+
help: {
|
49
|
+
t('help.unlock_force_key') => t('help.unlock_force_value')
|
50
|
+
}
|
51
|
+
)
|
52
|
+
|
53
|
+
route(
|
54
|
+
/^locker\sresource\slist$/,
|
55
|
+
:resource_list,
|
56
|
+
command: true,
|
57
|
+
help: {
|
58
|
+
t('help.resource_list_key') =>
|
59
|
+
t('help.resource_list_value')
|
60
|
+
}
|
61
|
+
)
|
62
|
+
|
63
|
+
route(
|
64
|
+
/^locker\sresource\screate\s([a-zA-Z0-9_-]+)$/,
|
65
|
+
:resource_create,
|
66
|
+
command: true,
|
67
|
+
help: {
|
68
|
+
t('help.resource_create_key') =>
|
69
|
+
t('help.resource_create_value')
|
70
|
+
}
|
71
|
+
)
|
72
|
+
|
73
|
+
route(
|
74
|
+
/^locker\sresource\sdelete\s([a-zA-Z0-9_-]+)$/,
|
75
|
+
:resource_delete,
|
76
|
+
command: true,
|
77
|
+
help: {
|
78
|
+
t('help.resource_delete_key') =>
|
79
|
+
t('help.resource_delete_value')
|
80
|
+
}
|
81
|
+
)
|
82
|
+
|
83
|
+
route(
|
84
|
+
/^locker\sresource\sshow\s([a-zA-Z0-9_-]+)$/,
|
85
|
+
:resource_show,
|
86
|
+
command: true,
|
87
|
+
help: {
|
88
|
+
t('help.resource_show_key') =>
|
89
|
+
t('help.resource_show_value')
|
90
|
+
}
|
91
|
+
)
|
92
|
+
|
93
|
+
route(
|
94
|
+
/^locker\slabel\slist$/,
|
95
|
+
:label_list,
|
96
|
+
command: true,
|
97
|
+
help: {
|
98
|
+
t('help.label_list_key') =>
|
99
|
+
t('help.label_list_value')
|
100
|
+
}
|
101
|
+
)
|
102
|
+
|
103
|
+
route(
|
104
|
+
/^locker\slabel\screate\s([a-zA-Z0-9_-]+)$/,
|
105
|
+
:label_create,
|
106
|
+
command: true,
|
107
|
+
help: {
|
108
|
+
t('help.label_create_key') =>
|
109
|
+
t('help.label_create_value')
|
110
|
+
}
|
111
|
+
)
|
112
|
+
|
113
|
+
route(
|
114
|
+
/^locker\slabel\sdelete\s([a-zA-Z0-9_-]+)$/,
|
115
|
+
:label_delete,
|
116
|
+
command: true,
|
117
|
+
help: {
|
118
|
+
t('help.label_delete_key') =>
|
119
|
+
t('help.label_delete_value')
|
120
|
+
}
|
121
|
+
)
|
122
|
+
|
123
|
+
route(
|
124
|
+
/^locker\slabel\sshow\s([a-zA-Z0-9_-]+)$/,
|
125
|
+
:label_show,
|
126
|
+
command: true,
|
127
|
+
help: {
|
128
|
+
t('help.label_show_key') =>
|
129
|
+
t('help.label_show_value')
|
130
|
+
}
|
131
|
+
)
|
132
|
+
|
133
|
+
route(
|
134
|
+
/^locker\slabel\sadd\s([a-zA-Z0-9_-]+)\sto\s([a-zA-Z0-9_-]+)$/,
|
135
|
+
:label_add,
|
136
|
+
command: true,
|
137
|
+
help: {
|
138
|
+
t('help.label_add_key') =>
|
139
|
+
t('help.label_add_value')
|
140
|
+
}
|
141
|
+
)
|
142
|
+
|
143
|
+
route(
|
144
|
+
/^locker\slabel\sremove\s([a-zA-Z0-9_-]+)\sfrom\s([a-zA-Z0-9_-]+)$/,
|
145
|
+
:label_remove,
|
146
|
+
command: true,
|
147
|
+
help: {
|
148
|
+
t('help.label_remove_key') =>
|
149
|
+
t('help.label_remove_value')
|
150
|
+
}
|
151
|
+
)
|
152
|
+
|
153
|
+
def http_label_show(request, response)
|
154
|
+
name = request.env['router.params'][:name]
|
155
|
+
response.headers['Content-Type'] = 'application/json'
|
156
|
+
result = label(name)
|
157
|
+
response.write(result.to_json)
|
158
|
+
end
|
159
|
+
|
160
|
+
def http_resource_show(request, response)
|
161
|
+
name = request.env['router.params'][:name]
|
162
|
+
response.headers['Content-Type'] = 'application/json'
|
163
|
+
result = resource(name)
|
164
|
+
response.write(result.to_json)
|
165
|
+
end
|
166
|
+
|
167
|
+
def lock(response)
|
168
|
+
name = response.matches[0][0]
|
169
|
+
timeamt = response.matches[0][1]
|
170
|
+
timeunit = response.matches[0][2]
|
171
|
+
case timeunit
|
172
|
+
when 's'
|
173
|
+
time_until = Time.now.utc + timeamt.to_i
|
174
|
+
when 'm'
|
175
|
+
time_until = Time.now.utc + (timeamt.to_i * 60)
|
176
|
+
when 'h'
|
177
|
+
time_until = Time.now.utc + (timeamt.to_i * 3600)
|
178
|
+
else
|
179
|
+
time_until = nil
|
180
|
+
end
|
181
|
+
|
182
|
+
if resource_exists?(name)
|
183
|
+
if lock_resource!(name, response.user, time_until)
|
184
|
+
response.reply(t('resource.lock', name: name))
|
185
|
+
else
|
186
|
+
response.reply(t('resource.is_locked', name: name))
|
187
|
+
end
|
188
|
+
elsif label_exists?(name)
|
189
|
+
m = label_membership(name)
|
190
|
+
if m.count > 0
|
191
|
+
if lock_label!(name, response.user, time_until)
|
192
|
+
response.reply(t('label.lock', name: name))
|
193
|
+
else
|
194
|
+
response.reply(t('label.unable_to_lock', name: name))
|
195
|
+
end
|
196
|
+
else
|
197
|
+
response.reply(t('label.no_resources', name: name))
|
198
|
+
end
|
199
|
+
else
|
200
|
+
response.reply(t('subject.does_not_exist', name: name))
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def unlock(response)
|
205
|
+
name = response.matches[0][0]
|
206
|
+
if resource_exists?(name)
|
207
|
+
res = resource(name)
|
208
|
+
if res['state'] == 'unlocked'
|
209
|
+
response.reply(t('resource.is_unlocked', name: name))
|
210
|
+
else
|
211
|
+
# FIXME: NOT SECURE
|
212
|
+
if response.user.name == res['owner']
|
213
|
+
unlock_resource!(name)
|
214
|
+
response.reply(t('resource.unlock', name: name))
|
215
|
+
# FIXME: Handle the case where things can't be unlocked?
|
216
|
+
else
|
217
|
+
response.reply(t('resource.owned', name: name,
|
218
|
+
owner: res['owner']))
|
219
|
+
end
|
220
|
+
end
|
221
|
+
elsif label_exists?(name)
|
222
|
+
l = label(name)
|
223
|
+
if l['state'] == 'unlocked'
|
224
|
+
response.reply(t('label.is_unlocked', name: name))
|
225
|
+
else
|
226
|
+
# FIXME: NOT SECURE
|
227
|
+
if response.user.name == l['owner']
|
228
|
+
unlock_label!(name)
|
229
|
+
response.reply(t('label.unlock', name: name))
|
230
|
+
# FIXME: Handle the case where things can't be unlocked?
|
231
|
+
else
|
232
|
+
response.reply(t('label.owned', name: name,
|
233
|
+
owner: l['owner']))
|
234
|
+
end
|
235
|
+
end
|
236
|
+
else
|
237
|
+
response.reply(t('subject.does_not_exist', name: name))
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def unlock_force(response)
|
242
|
+
name = response.matches[0][0]
|
243
|
+
if resource_exists?(name)
|
244
|
+
unlock_resource!(name)
|
245
|
+
response.reply(t('resource.unlock', name: name))
|
246
|
+
# FIXME: Handle the case where things can't be unlocked?
|
247
|
+
elsif label_exists?(name)
|
248
|
+
unlock_label!(name)
|
249
|
+
response.reply(t('label.unlock', name: name))
|
250
|
+
# FIXME: Handle the case where things can't be unlocked?
|
251
|
+
else
|
252
|
+
response.reply(t('subject.does_not_exist', name: name))
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def label_list(response)
|
257
|
+
labels.each do |l|
|
258
|
+
response.reply(t('label.desc', name: l.sub('label_', '')))
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def label_create(response)
|
263
|
+
name = response.matches[0][0]
|
264
|
+
if create_label(name)
|
265
|
+
response.reply(t('label.created', name: name))
|
266
|
+
else
|
267
|
+
response.reply(t('label.exists', name: name))
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def label_delete(response)
|
272
|
+
name = response.matches[0][0]
|
273
|
+
if delete_label(name)
|
274
|
+
response.reply(t('label.deleted', name: name))
|
275
|
+
else
|
276
|
+
response.reply(t('label.does_not_exist', name: name))
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def label_show(response)
|
281
|
+
name = response.matches[0][0]
|
282
|
+
if label_exists?(name)
|
283
|
+
members = label_membership(name)
|
284
|
+
if members.count > 0
|
285
|
+
response.reply(t('label.resources', name: name,
|
286
|
+
resources: members.join(', ')))
|
287
|
+
else
|
288
|
+
response.reply(t('label.has_no_resources', name: name))
|
289
|
+
end
|
290
|
+
else
|
291
|
+
response.reply(t('label.does_not_exist', name: name))
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def label_add(response)
|
296
|
+
resource_name = response.matches[0][0]
|
297
|
+
label_name = response.matches[0][1]
|
298
|
+
if label_exists?(label_name)
|
299
|
+
if resource_exists?(resource_name)
|
300
|
+
add_resource_to_label(label_name, resource_name)
|
301
|
+
response.reply(t('label.resource_added', label: label_name,
|
302
|
+
resource: resource_name))
|
303
|
+
else
|
304
|
+
response.reply(t('resource.does_not_exist', name: resource_name))
|
305
|
+
end
|
306
|
+
else
|
307
|
+
response.reply(t('label.does_not_exist', name: label_name))
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def label_remove(response)
|
312
|
+
resource_name = response.matches[0][0]
|
313
|
+
label_name = response.matches[0][1]
|
314
|
+
if label_exists?(label_name)
|
315
|
+
if resource_exists?(resource_name)
|
316
|
+
members = label_membership(label_name)
|
317
|
+
if members.include?(resource_name)
|
318
|
+
remove_resource_from_label(label_name, resource_name)
|
319
|
+
response.reply(t('label.resource_removed',
|
320
|
+
label: label_name, resource: resource_name))
|
321
|
+
else
|
322
|
+
response.reply(t('label.does_not_have_resource',
|
323
|
+
label: label_name, resource: resource_name))
|
324
|
+
end
|
325
|
+
else
|
326
|
+
response.reply(t('resource.does_not_exist', name: resource_name))
|
327
|
+
end
|
328
|
+
else
|
329
|
+
response.reply(t('label.does_not_exist', name: label_name))
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def resource_list(response)
|
334
|
+
resources.each do |r|
|
335
|
+
r_name = r.sub('resource_', '')
|
336
|
+
res = resource(r_name)
|
337
|
+
response.reply(t('resource.desc', name: r_name, state: res['state']))
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def resource_create(response)
|
342
|
+
name = response.matches[0][0]
|
343
|
+
if create_resource(name)
|
344
|
+
response.reply(t('resource.created', name: name))
|
345
|
+
else
|
346
|
+
response.reply(t('resource.exists', name: name))
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def resource_delete(response)
|
351
|
+
name = response.matches[0][0]
|
352
|
+
if delete_resource(name)
|
353
|
+
response.reply(t('resource.deleted', name: name))
|
354
|
+
else
|
355
|
+
response.reply(t('resource.does_not_exist', name: name))
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def resource_show(response)
|
360
|
+
name = response.matches[0][0]
|
361
|
+
if resource_exists?(name)
|
362
|
+
r = resource(name)
|
363
|
+
response.reply(t('resource.desc', name: name, state: r['state']))
|
364
|
+
else
|
365
|
+
response.reply(t('resource.does_not_exist', name: name))
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
private
|
370
|
+
|
371
|
+
def create_label(name)
|
372
|
+
label_key = "label_#{name}"
|
373
|
+
redis.hset(label_key, 'state', 'unlocked') unless
|
374
|
+
resource_exists?(name) || label_exists?(name)
|
375
|
+
end
|
376
|
+
|
377
|
+
def delete_label(name)
|
378
|
+
label_key = "label_#{name}"
|
379
|
+
redis.del(label_key) if label_exists?(name)
|
380
|
+
end
|
381
|
+
|
382
|
+
def label_exists?(name)
|
383
|
+
redis.exists("label_#{name}")
|
384
|
+
end
|
385
|
+
|
386
|
+
def label_membership(name)
|
387
|
+
redis.smembers("membership_#{name}")
|
388
|
+
end
|
389
|
+
|
390
|
+
def add_resource_to_label(label, resource)
|
391
|
+
if label_exists?(label) && resource_exists?(resource)
|
392
|
+
redis.sadd("membership_#{label}", resource)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def remove_resource_from_label(label, resource)
|
397
|
+
if label_exists?(label) && resource_exists?(resource)
|
398
|
+
redis.srem("membership_#{label}", resource)
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def create_resource(name)
|
403
|
+
resource_key = "resource_#{name}"
|
404
|
+
redis.hset(resource_key, 'state', 'unlocked') unless
|
405
|
+
resource_exists?(name) || label_exists?(name)
|
406
|
+
end
|
407
|
+
|
408
|
+
def delete_resource(name)
|
409
|
+
resource_key = "resource_#{name}"
|
410
|
+
redis.del(resource_key) if resource_exists?(name)
|
411
|
+
end
|
412
|
+
|
413
|
+
def resource_exists?(name)
|
414
|
+
redis.exists("resource_#{name}")
|
415
|
+
end
|
416
|
+
|
417
|
+
def lock_resource!(name, owner, time_until)
|
418
|
+
if resource_exists?(name)
|
419
|
+
resource_key = "resource_#{name}"
|
420
|
+
value = redis.hget(resource_key, 'state')
|
421
|
+
if value == 'unlocked'
|
422
|
+
# FIXME: Race condition!
|
423
|
+
# FIXME: Store something better than name
|
424
|
+
redis.hset(resource_key, 'state', 'locked')
|
425
|
+
redis.hset(resource_key, 'owner', owner.name)
|
426
|
+
redis.hset(resource_key, 'until', time_until)
|
427
|
+
true
|
428
|
+
else
|
429
|
+
false
|
430
|
+
end
|
431
|
+
else
|
432
|
+
false
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
def lock_label!(name, owner, time_until)
|
437
|
+
if label_exists?(name)
|
438
|
+
key = "label_#{name}"
|
439
|
+
members = label_membership(name)
|
440
|
+
members.each do |m|
|
441
|
+
r = resource(m)
|
442
|
+
return false if r['state'] == 'locked'
|
443
|
+
end
|
444
|
+
# FIXME: No, really, race condition.
|
445
|
+
members.each do |m|
|
446
|
+
lock_resource!(m, owner, time_until)
|
447
|
+
end
|
448
|
+
redis.hset(key, 'state', 'locked')
|
449
|
+
redis.hset(key, 'owner', owner.name)
|
450
|
+
redis.hset(key, 'until', time_until)
|
451
|
+
true
|
452
|
+
else
|
453
|
+
false
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
def unlock_resource!(name)
|
458
|
+
if resource_exists?(name)
|
459
|
+
# FIXME: Tracking here?
|
460
|
+
key = "resource_#{name}"
|
461
|
+
redis.hset(key, 'state', 'unlocked')
|
462
|
+
redis.hset(key, 'owner', '')
|
463
|
+
else
|
464
|
+
false
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
def unlock_label!(name)
|
469
|
+
if label_exists?(name)
|
470
|
+
key = "label_#{name}"
|
471
|
+
members = label_membership(name)
|
472
|
+
members.each do |m|
|
473
|
+
unlock_resource!(m)
|
474
|
+
end
|
475
|
+
redis.hset(key, 'state', 'unlocked')
|
476
|
+
redis.hset(key, 'owner', '')
|
477
|
+
true
|
478
|
+
else
|
479
|
+
false
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def resource(name)
|
484
|
+
redis.hgetall("resource_#{name}")
|
485
|
+
end
|
486
|
+
|
487
|
+
def resources
|
488
|
+
redis.keys('resource_*')
|
489
|
+
end
|
490
|
+
|
491
|
+
def label(name)
|
492
|
+
redis.hgetall("label_#{name}")
|
493
|
+
end
|
494
|
+
|
495
|
+
def labels
|
496
|
+
redis.keys('label_*')
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
Lita.register_handler(Locker)
|
501
|
+
end
|
502
|
+
end
|
data/lita-locker.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = 'lita-locker'
|
3
|
+
spec.version = '0.3.0'
|
4
|
+
spec.authors = ['Eric Sigler']
|
5
|
+
spec.email = ['me@esigler.com']
|
6
|
+
spec.description = %q("lock" and "unlock" arbitrary subjects)
|
7
|
+
spec.summary = %q("lock" and "unlock" arbitrary subjects)
|
8
|
+
spec.homepage = 'https://github.com/esigler/lita-locker'
|
9
|
+
spec.license = 'MIT'
|
10
|
+
spec.metadata = { 'lita_plugin_type' => 'handler' }
|
11
|
+
|
12
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
13
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
14
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
15
|
+
spec.require_paths = ['lib']
|
16
|
+
|
17
|
+
spec.add_runtime_dependency 'lita', '>= 3.2'
|
18
|
+
|
19
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
20
|
+
spec.add_development_dependency 'rake'
|
21
|
+
spec.add_development_dependency 'rspec', '>= 3.0.0.beta2'
|
22
|
+
spec.add_development_dependency 'simplecov'
|
23
|
+
spec.add_development_dependency 'coveralls'
|
24
|
+
spec.add_development_dependency 'rubocop'
|
25
|
+
end
|
data/locales/en.yml
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
en:
|
2
|
+
lita:
|
3
|
+
handlers:
|
4
|
+
locker:
|
5
|
+
help:
|
6
|
+
lock_key: lock <subject>
|
7
|
+
lock_value: Make something unavailable to others
|
8
|
+
unlock_key: unlock <subject>
|
9
|
+
unlock_value: Make something available to others
|
10
|
+
unlock_force_key: unlock <subject> force
|
11
|
+
unlock_force_value: Force removal of a reservation.
|
12
|
+
resource_list_key: locker resource list
|
13
|
+
resource_list_value: List all resources
|
14
|
+
resource_create_key: locker resource create <name>
|
15
|
+
resource_create_value: Create a resource with <name>
|
16
|
+
resource_delete_key: locker resource delete <name>
|
17
|
+
resource_delete_value: Delete the resource with <name>
|
18
|
+
resource_show_key: locker resource show <name>
|
19
|
+
resource_show_value: Show the state of <name>
|
20
|
+
label_list_key: locker label list
|
21
|
+
label_list_value: List all labels
|
22
|
+
label_create_key: locker label create <name>
|
23
|
+
label_create_value: Create a label with <name>
|
24
|
+
label_delete_key: locker label delete <name>
|
25
|
+
label_delete_value: Delete the label with <name>
|
26
|
+
label_show_key: locker label show <name>
|
27
|
+
label_show_value: Show all resources for <name>
|
28
|
+
label_add_key: locker label add <resource> to <name>
|
29
|
+
label_add_value: Adds <resource> to the list of things to lock/unlock for <name>
|
30
|
+
label_remove_key: locker label remove <resource> from <name>
|
31
|
+
label_remove_value: Removes <resource> from <name>
|
32
|
+
resource:
|
33
|
+
created: "Resource %{name} created"
|
34
|
+
desc: "Resource: %{name}, state: %{state}"
|
35
|
+
exists: "%{name} already exists"
|
36
|
+
deleted: "Resource %{name} deleted"
|
37
|
+
does_not_exist: "Resource %{name} does not exist"
|
38
|
+
lock: "%{name} locked"
|
39
|
+
is_locked: "%{name} is locked"
|
40
|
+
unlock: "%{name} unlocked"
|
41
|
+
is_unlocked: "%{name} is unlocked"
|
42
|
+
owned: "%{name} is locked by %{owner}"
|
43
|
+
subject:
|
44
|
+
does_not_exist: "%{name} does not exist"
|
45
|
+
label:
|
46
|
+
unlock: "%{name} unlocked"
|
47
|
+
owned: "%{name} is locked by %{owner}"
|
48
|
+
is_unlocked: "%{name} is unlocked"
|
49
|
+
unable_to_lock: "%{name} unable to be locked"
|
50
|
+
lock: "%{name} locked"
|
51
|
+
desc: "Label: %{name}"
|
52
|
+
created: "Label %{name} created"
|
53
|
+
exists: "%{name} already exists"
|
54
|
+
deleted: "Label %{name} deleted"
|
55
|
+
does_not_exist: "Label %{name} does not exist"
|
56
|
+
has_no_resources: "Label %{name} has no resources"
|
57
|
+
resource_added: "Resource %{resource} has been added to %{label}"
|
58
|
+
resource_removed: "Resource %{resource} has been removed from %{label}"
|
59
|
+
resources: "Label %{name} has: %{resources}"
|
60
|
+
does_not_have_resource: "Label %{label} does not have Resource %{resource}"
|
61
|
+
no_resources: "%{name} has no resources, so it cannot be locked"
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lita::Handlers::Locker, lita_handler: true do
|
4
|
+
# it { routes('(lock) foobar').to(:lock) }
|
5
|
+
# it { routes('(unlock) foobar').to(:unlock) }
|
6
|
+
|
7
|
+
it { routes_command('lock foobar').to(:lock) }
|
8
|
+
# it { routes_command('lock foobar 30m').to(:lock) }
|
9
|
+
|
10
|
+
it { routes_command('unlock foobar').to(:unlock) }
|
11
|
+
it { routes_command('unlock foobar force').to(:unlock_force) }
|
12
|
+
|
13
|
+
it { routes_command('locker resource list').to(:resource_list) }
|
14
|
+
it { routes_command('locker resource create foobar').to(:resource_create) }
|
15
|
+
it { routes_command('locker resource delete foobar').to(:resource_delete) }
|
16
|
+
it { routes_command('locker resource show foobar').to(:resource_show) }
|
17
|
+
|
18
|
+
it { routes_command('locker label list').to(:label_list) }
|
19
|
+
it { routes_command('locker label create foobar').to(:label_create) }
|
20
|
+
it { routes_command('locker label delete foobar').to(:label_delete) }
|
21
|
+
it { routes_command('locker label show foobar').to(:label_show) }
|
22
|
+
it { routes_command('locker label add foo to bar').to(:label_add) }
|
23
|
+
it { routes_command('locker label remove foo from bar').to(:label_remove) }
|
24
|
+
|
25
|
+
it { routes_http(:get, '/locker/label/foobar').to(:http_label_show) }
|
26
|
+
it { routes_http(:get, '/locker/resource/foobar').to(:http_resource_show) }
|
27
|
+
|
28
|
+
describe '#lock' do
|
29
|
+
it 'locks a resource when it is available' do
|
30
|
+
send_command('locker resource create foobar')
|
31
|
+
send_command('lock foobar')
|
32
|
+
expect(replies.last).to eq('foobar locked')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'locks a label when it is available and has resources' do
|
36
|
+
send_command('locker resource create foobar')
|
37
|
+
send_command('locker label create bazbat')
|
38
|
+
send_command('locker label add foobar to bazbat')
|
39
|
+
send_command('lock bazbat')
|
40
|
+
expect(replies.last).to eq('bazbat locked')
|
41
|
+
send_command('locker resource show foobar')
|
42
|
+
expect(replies.last).to eq('Resource: foobar, state: locked')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'shows a warning when a label has no resources' do
|
46
|
+
send_command('locker label create foobar')
|
47
|
+
send_command('lock foobar')
|
48
|
+
expect(replies.last).to eq('foobar has no resources, ' \
|
49
|
+
'so it cannot be locked')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'shows a warning when a resource is unavailable' do
|
53
|
+
send_command('locker resource create foobar')
|
54
|
+
send_command('lock foobar')
|
55
|
+
send_command('lock foobar')
|
56
|
+
expect(replies.last).to eq('foobar is locked')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'shows a warning when a label is unavailable' do
|
60
|
+
send_command('locker resource create foobar')
|
61
|
+
send_command('locker label create bazbat')
|
62
|
+
send_command('locker label add foobar to bazbat')
|
63
|
+
send_command('lock foobar')
|
64
|
+
send_command('lock bazbat')
|
65
|
+
expect(replies.last).to eq('bazbat unable to be locked')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'shows an error when a <subject> does not exist' do
|
69
|
+
send_command('lock foobar')
|
70
|
+
expect(replies.last).to eq('foobar does not exist')
|
71
|
+
end
|
72
|
+
|
73
|
+
# it 'locks a resource when it is available for a period of time' do
|
74
|
+
# send_command('locker resource create foobar')
|
75
|
+
# send_command('lock foobar 17m')
|
76
|
+
# expect(replies.last).to eq('foobar locked for 17 minutes')
|
77
|
+
# send_command('locker resource show foobar')
|
78
|
+
# expect(replies.last).to eq('Resource: foobar, state: locked')
|
79
|
+
# send_command('unlock foobar')
|
80
|
+
# send_command('lock foobar 12s')
|
81
|
+
# expect(replies.last).to eq('foobar locked for 17 seconds')
|
82
|
+
# send_command('unlock foobar')
|
83
|
+
# send_command('lock foobar 14h')
|
84
|
+
# expect(replies.last).to eq('foobar locked for 14 hours')
|
85
|
+
# end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#unlock' do
|
89
|
+
it 'unlocks a resource when it is available' do
|
90
|
+
send_command('locker resource create foobar')
|
91
|
+
send_command('lock foobar')
|
92
|
+
send_command('unlock foobar')
|
93
|
+
expect(replies.last).to eq('foobar unlocked')
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'does not unlock a resource when someone else locked it' do
|
97
|
+
alice = Lita::User.create(1, name: 'Alice')
|
98
|
+
bob = Lita::User.create(2, name: 'Bob')
|
99
|
+
send_command('locker resource create foobar')
|
100
|
+
send_command('lock foobar', as: alice)
|
101
|
+
send_command('unlock foobar', as: bob)
|
102
|
+
expect(replies.last).to eq('foobar is locked by Alice')
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'unlocks a label when it is available' do
|
106
|
+
send_command('locker resource create foobar')
|
107
|
+
send_command('locker label create bazbat')
|
108
|
+
send_command('locker label add foobar to bazbat')
|
109
|
+
send_command('lock bazbat')
|
110
|
+
send_command('unlock bazbat')
|
111
|
+
expect(replies.last).to eq('bazbat unlocked')
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'does not unlock a label when someone else locked it' do
|
115
|
+
alice = Lita::User.create(1, name: 'Alice')
|
116
|
+
bob = Lita::User.create(2, name: 'Bob')
|
117
|
+
send_command('locker resource create foobar')
|
118
|
+
send_command('locker label create bazbat')
|
119
|
+
send_command('locker label add foobar to bazbat')
|
120
|
+
send_command('lock bazbat', as: alice)
|
121
|
+
send_command('unlock bazbat', as: bob)
|
122
|
+
expect(replies.last).to eq('bazbat is locked by Alice')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'shows a warning when a resource is already unlocked' do
|
126
|
+
send_command('locker resource create foobar')
|
127
|
+
send_command('unlock foobar')
|
128
|
+
expect(replies.last).to eq('foobar is unlocked')
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'shows a warning when a label is already unlocked' do
|
132
|
+
send_command('locker resource create foobar')
|
133
|
+
send_command('locker label create bazbat')
|
134
|
+
send_command('locker label add foobar to bazbat')
|
135
|
+
send_command('unlock bazbat')
|
136
|
+
send_command('unlock bazbat')
|
137
|
+
expect(replies.last).to eq('bazbat is unlocked')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'shows an error when a <subject> does not exist' do
|
141
|
+
send_command('unlock foobar')
|
142
|
+
expect(replies.last).to eq('foobar does not exist')
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#unlock_force' do
|
147
|
+
it 'unlocks a resource from someone else when it is available' do
|
148
|
+
alice = Lita::User.create(1, name: 'Alice')
|
149
|
+
bob = Lita::User.create(2, name: 'Bob')
|
150
|
+
send_command('locker resource create foobar')
|
151
|
+
send_command('lock foobar', as: alice)
|
152
|
+
send_command('unlock foobar force', as: bob)
|
153
|
+
expect(replies.last).to eq('foobar unlocked')
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'unlocks a label from someone else when it is available' do
|
157
|
+
alice = Lita::User.create(1, name: 'Alice')
|
158
|
+
bob = Lita::User.create(2, name: 'Bob')
|
159
|
+
send_command('locker resource create foobar')
|
160
|
+
send_command('locker label create bazbat')
|
161
|
+
send_command('locker label add foobar to bazbat')
|
162
|
+
send_command('lock bazbat', as: alice)
|
163
|
+
send_command('unlock bazbat force', as: bob)
|
164
|
+
expect(replies.last).to eq('bazbat unlocked')
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'shows an error when a <subject> does not exist' do
|
168
|
+
send_command('unlock foobar force')
|
169
|
+
expect(replies.last).to eq('foobar does not exist')
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe '#label_list' do
|
174
|
+
it 'shows a list of labels if there are any' do
|
175
|
+
send_command('locker label create foobar')
|
176
|
+
send_command('locker label create bazbat')
|
177
|
+
send_command('locker label list')
|
178
|
+
expect(replies.last).to eq('Label: bazbat')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe '#label_create' do
|
183
|
+
it 'creates a label with <name>' do
|
184
|
+
send_command('locker label create foobar')
|
185
|
+
expect(replies.last).to eq('Label foobar created')
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'shows a warning when the <name> already exists as a label' do
|
189
|
+
send_command('locker label create foobar')
|
190
|
+
send_command('locker label create foobar')
|
191
|
+
expect(replies.last).to eq('foobar already exists')
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'shows a warning when the <name> already exists as a resource' do
|
195
|
+
send_command('locker resource create foobar')
|
196
|
+
send_command('locker label create foobar')
|
197
|
+
expect(replies.last).to eq('foobar already exists')
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#label_delete' do
|
202
|
+
it 'deletes a label with <name>' do
|
203
|
+
send_command('locker label create foobar')
|
204
|
+
send_command('locker label delete foobar')
|
205
|
+
expect(replies.last).to eq('Label foobar deleted')
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'shows a warning when <name> does not exist' do
|
209
|
+
send_command('locker label delete foobar')
|
210
|
+
expect(replies.last).to eq('Label foobar does not exist')
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe '#label_show' do
|
215
|
+
it 'shows a list of resources for a label if there are any' do
|
216
|
+
send_command('locker resource create whatever')
|
217
|
+
send_command('locker label create foobar')
|
218
|
+
send_command('locker label add whatever to foobar')
|
219
|
+
send_command('locker label show foobar')
|
220
|
+
expect(replies.last).to eq('Label foobar has: whatever')
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'shows a warning if there are no resources for the label' do
|
224
|
+
send_command('locker label create foobar')
|
225
|
+
send_command('locker label show foobar')
|
226
|
+
expect(replies.last).to eq('Label foobar has no resources')
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'shows an error if the label does not exist' do
|
230
|
+
send_command('locker label show foobar')
|
231
|
+
expect(replies.last).to eq('Label foobar does not exist')
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe '#label_add' do
|
236
|
+
it 'adds a resource to a label if both exist' do
|
237
|
+
send_command('locker resource create foo')
|
238
|
+
send_command('locker label create bar')
|
239
|
+
send_command('locker label add foo to bar')
|
240
|
+
expect(replies.last).to eq('Resource foo has been added to bar')
|
241
|
+
send_command('locker label show bar')
|
242
|
+
expect(replies.last).to eq('Label bar has: foo')
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'adds multiple resources to a label if all exist' do
|
246
|
+
send_command('locker resource create foo')
|
247
|
+
send_command('locker resource create baz')
|
248
|
+
send_command('locker label create bar')
|
249
|
+
send_command('locker label add foo to bar')
|
250
|
+
send_command('locker label add baz to bar')
|
251
|
+
send_command('locker label show bar')
|
252
|
+
expect(replies.last).to eq('Label bar has: baz, foo')
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'shows an error if the label does not exist' do
|
256
|
+
send_command('locker label add foo to bar')
|
257
|
+
expect(replies.last).to eq('Label bar does not exist')
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'shows an error if the resource does not exist' do
|
261
|
+
send_command('locker label create bar')
|
262
|
+
send_command('locker label add foo to bar')
|
263
|
+
expect(replies.last).to eq('Resource foo does not exist')
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe '#label_remove' do
|
268
|
+
it 'removes a resource from a label if both exist and are related' do
|
269
|
+
send_command('locker resource create foo')
|
270
|
+
send_command('locker label create bar')
|
271
|
+
send_command('locker label add foo to bar')
|
272
|
+
send_command('locker label remove foo from bar')
|
273
|
+
send_command('locker label show bar')
|
274
|
+
expect(replies.last).to eq('Label bar has no resources')
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'shows an error if they both exist but are not related' do
|
278
|
+
send_command('locker resource create foo')
|
279
|
+
send_command('locker label create bar')
|
280
|
+
send_command('locker label remove foo from bar')
|
281
|
+
expect(replies.last).to eq('Label bar does not have Resource foo')
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'shows an error if the label does not exist' do
|
285
|
+
send_command('locker label add foo to bar')
|
286
|
+
expect(replies.last).to eq('Label bar does not exist')
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'shows an error if the resource does not exist' do
|
290
|
+
send_command('locker label create bar')
|
291
|
+
send_command('locker label add foo to bar')
|
292
|
+
expect(replies.last).to eq('Resource foo does not exist')
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe '#resource_list' do
|
297
|
+
it 'shows a list of resources if there are any' do
|
298
|
+
send_command('locker resource create foobar')
|
299
|
+
send_command('locker resource create bazbat')
|
300
|
+
send_command('locker resource list')
|
301
|
+
expect(replies.last).to eq('Resource: foobar, state: unlocked')
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe '#resource_create' do
|
306
|
+
it 'creates a resource with <name>' do
|
307
|
+
send_command('locker resource create foobar')
|
308
|
+
expect(replies.last).to eq('Resource foobar created')
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'shows a warning when the <name> already exists as a resource' do
|
312
|
+
send_command('locker resource create foobar')
|
313
|
+
send_command('locker resource create foobar')
|
314
|
+
expect(replies.last).to eq('foobar already exists')
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'shows a warning when the <name> already exists as a label' do
|
318
|
+
send_command('locker label create foobar')
|
319
|
+
send_command('locker resource create foobar')
|
320
|
+
expect(replies.last).to eq('foobar already exists')
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
describe '#resource_delete' do
|
325
|
+
it 'deletes a resource with <name>' do
|
326
|
+
send_command('locker resource create foobar')
|
327
|
+
send_command('locker resource delete foobar')
|
328
|
+
expect(replies.last).to eq('Resource foobar deleted')
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'shows a warning when <name> does not exist' do
|
332
|
+
send_command('locker resource delete foobar')
|
333
|
+
expect(replies.last).to eq('Resource foobar does not exist')
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
describe '#resource_show' do
|
338
|
+
it 'shows the state of a <name> if it exists' do
|
339
|
+
send_command('locker resource create foobar')
|
340
|
+
send_command('locker resource show foobar')
|
341
|
+
expect(replies.last).to eq('Resource: foobar, state: unlocked')
|
342
|
+
send_command('lock foobar')
|
343
|
+
send_command('locker resource show foobar')
|
344
|
+
expect(replies.last).to eq('Resource: foobar, state: locked')
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'shows a warning when <name> does not exist' do
|
348
|
+
send_command('locker resource show foobar')
|
349
|
+
expect(replies.last).to eq('Resource foobar does not exist')
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
4
|
+
SimpleCov::Formatter::HTMLFormatter,
|
5
|
+
Coveralls::SimpleCov::Formatter
|
6
|
+
]
|
7
|
+
SimpleCov.start { add_filter '/spec/' }
|
8
|
+
|
9
|
+
require 'lita-locker'
|
10
|
+
require 'lita/rspec'
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lita-locker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Sigler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: lita
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.0.0.beta2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.0.0.beta2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: "\"lock\" and \"unlock\" arbitrary subjects"
|
112
|
+
email:
|
113
|
+
- me@esigler.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rubocop.yml"
|
120
|
+
- ".travis.yml"
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- lib/lita-locker.rb
|
126
|
+
- lib/lita/handlers/locker.rb
|
127
|
+
- lita-locker.gemspec
|
128
|
+
- locales/en.yml
|
129
|
+
- spec/lita/handlers/locker_spec.rb
|
130
|
+
- spec/spec_helper.rb
|
131
|
+
homepage: https://github.com/esigler/lita-locker
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata:
|
135
|
+
lita_plugin_type: handler
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.2.1
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: "\"lock\" and \"unlock\" arbitrary subjects"
|
156
|
+
test_files:
|
157
|
+
- spec/lita/handlers/locker_spec.rb
|
158
|
+
- spec/spec_helper.rb
|