lita-locker 1.0.10 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -2
- data/.ruby-version +1 -0
- data/.travis.yml +1 -2
- data/Gemfile +2 -0
- data/README.md +15 -6
- data/Rakefile +3 -1
- data/lib/lita-locker.rb +3 -0
- data/lib/lita/handlers/locker.rb +11 -5
- data/lib/lita/handlers/locker_events.rb +2 -0
- data/lib/lita/handlers/locker_http.rb +2 -0
- data/lib/lita/handlers/locker_labels.rb +37 -20
- data/lib/lita/handlers/locker_misc.rb +8 -6
- data/lib/lita/handlers/locker_resources.rb +37 -20
- data/lib/locker/label.rb +5 -5
- data/lib/locker/list.rb +57 -0
- data/lib/locker/misc.rb +2 -0
- data/lib/locker/regex.rb +2 -0
- data/lib/locker/resource.rb +3 -1
- data/lita-locker.gemspec +4 -1
- data/locales/en.yml +7 -3
- data/spec/lita/handlers/locker_events_spec.rb +2 -0
- data/spec/lita/handlers/locker_http_spec.rb +3 -1
- data/spec/lita/handlers/locker_labels_spec.rb +54 -3
- data/spec/lita/handlers/locker_misc_spec.rb +6 -0
- data/spec/lita/handlers/locker_resources_spec.rb +49 -3
- data/spec/lita/handlers/locker_spec.rb +2 -0
- data/spec/locker/list_spec.rb +100 -0
- data/spec/spec_helper.rb +11 -6
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40e3c6cfb38098e4089ab667b84796cf38dddb98
|
4
|
+
data.tar.gz: 01c834e9d945c39cf53fa37c5984384505f557b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9d7e3f1151c101a757688053d3d8f82f446db64e7bacabb72ee9baa064f1221f6489ec6d891201eba9c2284158ae6f59c2c31300918d84be75d10d6ad9cb674
|
7
|
+
data.tar.gz: bde7f3cf399c8e069ab48bdf7fc48b93f1aad29ef6d2fbbdb2dad7684291ab28f2bf9ad0cafc37d05227ed443e87c951992519636f640df26e746eef1d06fb66
|
data/.rubocop.yml
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.4.2
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -19,7 +19,16 @@ gem "lita-locker"
|
|
19
19
|
|
20
20
|
## Configuration
|
21
21
|
|
22
|
-
|
22
|
+
### Optional attributes
|
23
|
+
|
24
|
+
* `per_page` - The number of items to show at once when listing labels or resources. Default: 10
|
25
|
+
|
26
|
+
### Example
|
27
|
+
|
28
|
+
``` ruby
|
29
|
+
Lita.configure do |config|
|
30
|
+
config.handlers.locker.per_page = 3
|
31
|
+
```
|
23
32
|
|
24
33
|
## Usage
|
25
34
|
|
@@ -61,7 +70,7 @@ locker dequeue <label> - Remove yourself from the queue for <label>
|
|
61
70
|
|
62
71
|
### Labels
|
63
72
|
```
|
64
|
-
locker label list
|
73
|
+
locker label list [--page N] - List all labels
|
65
74
|
locker label create <name> - Create a label with <name>.
|
66
75
|
locker label delete <name> - Delete the label with <name>. Clears all locks associated.
|
67
76
|
locker label add <resource> to <name> - Adds <resource> to the list of things to lock/unlock for <name>
|
@@ -71,10 +80,10 @@ locker label show <name> - Show all resources for <name>
|
|
71
80
|
|
72
81
|
### Resources
|
73
82
|
```
|
74
|
-
locker resource list
|
75
|
-
locker resource create <name>
|
76
|
-
locker resource delete <name>
|
77
|
-
locker resource show <name>
|
83
|
+
locker resource list [--page N] - List all resources
|
84
|
+
locker resource create <name> - Create a resource with <name>. (Restricted to locker_admins group)
|
85
|
+
locker resource delete <name> - Delete the resource with <name>. Clears all locks associated. (Restricted to locker_admins group)
|
86
|
+
locker resource show <name> - Show the state of <name>
|
78
87
|
```
|
79
88
|
|
80
89
|
### HTTP access
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler/gem_tasks'
|
2
4
|
require 'rspec/core/rake_task'
|
3
5
|
require 'rubocop/rake_task'
|
@@ -5,4 +7,4 @@ require 'rubocop/rake_task'
|
|
5
7
|
RSpec::Core::RakeTask.new(:spec)
|
6
8
|
RuboCop::RakeTask.new(:rubocop)
|
7
9
|
|
8
|
-
task default: [
|
10
|
+
task default: %i[spec rubocop]
|
data/lib/lita-locker.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'lita'
|
2
4
|
|
3
5
|
Lita.load_locales Dir[File.expand_path(
|
@@ -6,6 +8,7 @@ Lita.load_locales Dir[File.expand_path(
|
|
6
8
|
|
7
9
|
require 'redis-objects'
|
8
10
|
require 'time-lord'
|
11
|
+
require 'lita-keyword-arguments'
|
9
12
|
|
10
13
|
require 'locker/label'
|
11
14
|
require 'locker/misc'
|
data/lib/lita/handlers/locker.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Lita
|
2
4
|
# Handy, isn't it?
|
3
5
|
module Handlers
|
4
6
|
# Top-level class for Locker
|
5
7
|
class Locker < Handler
|
8
|
+
config :per_page, type: Integer, default: 10
|
9
|
+
|
6
10
|
on :loaded, :setup_redis
|
7
11
|
|
8
12
|
include ::Locker::Label
|
@@ -72,7 +76,7 @@ module Lita
|
|
72
76
|
|
73
77
|
return response.reply(failed(t('label.does_not_exist', name: name))) unless Label.exists?(name)
|
74
78
|
l = Label.new(name)
|
75
|
-
return response.reply(failed(t('label.no_resources', name: name))) unless l.membership.count
|
79
|
+
return response.reply(failed(t('label.no_resources', name: name))) unless l.membership.count.positive?
|
76
80
|
return response.reply(t('label.self_lock', name: name, user: response.user.name)) if l.owner == response.user
|
77
81
|
return response.reply(success(t('label.lock', name: name))) if l.lock!(response.user.id)
|
78
82
|
|
@@ -134,10 +138,12 @@ module Lita
|
|
134
138
|
l = Label.new(name)
|
135
139
|
return response.reply(failed(t('give.not_owned', label: name))) unless l.locked?
|
136
140
|
owner_mention = render_template('mention', name: l.owner.mention_name, id: l.owner.id)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
+
unless l.owner == response.user
|
142
|
+
return response.reply(t('give.not_owner',
|
143
|
+
label: name,
|
144
|
+
owner: l.owner.name,
|
145
|
+
mention: owner_mention))
|
146
|
+
end
|
141
147
|
recipient_name = response.match_data['username'].rstrip
|
142
148
|
recipient = Lita::User.fuzzy_find(recipient_name)
|
143
149
|
return response.reply(t('user.unknown', user: recipient_name)) unless recipient
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'locker/list'
|
4
|
+
|
1
5
|
module Lita
|
2
6
|
module Handlers
|
3
7
|
# Label-related handlers
|
@@ -10,64 +14,77 @@ module Lita
|
|
10
14
|
include ::Locker::Resource
|
11
15
|
|
12
16
|
route(
|
13
|
-
/^locker\slabel\slist
|
17
|
+
/^locker\slabel\slist/,
|
14
18
|
:list,
|
15
19
|
command: true,
|
20
|
+
kwargs: { page: { default: 1 } },
|
16
21
|
help: { t('help.label.list.syntax') => t('help.label.list.desc') }
|
17
22
|
)
|
18
23
|
|
19
24
|
route(
|
20
|
-
/^locker\slabel\screate\s#{LABELS_REGEX}$/,
|
25
|
+
/^locker\slabel\screate\s#{LABELS_REGEX}#{COMMENT_REGEX}$/,
|
21
26
|
:create,
|
22
27
|
command: true,
|
23
28
|
help: { t('help.label.create.syntax') => t('help.label.create.desc') }
|
24
29
|
)
|
25
30
|
|
26
31
|
route(
|
27
|
-
/^locker\slabel\sdelete\s#{LABELS_REGEX}$/,
|
32
|
+
/^locker\slabel\sdelete\s#{LABELS_REGEX}#{COMMENT_REGEX}$/,
|
28
33
|
:delete,
|
29
34
|
command: true,
|
30
35
|
help: { t('help.label.delete.syntax') => t('help.label.delete.desc') }
|
31
36
|
)
|
32
37
|
|
33
38
|
route(
|
34
|
-
/^locker\slabel\sshow\s#{LABEL_REGEX}$/,
|
39
|
+
/^locker\slabel\sshow\s#{LABEL_REGEX}#{COMMENT_REGEX}$/,
|
35
40
|
:show,
|
36
41
|
command: true,
|
37
42
|
help: { t('help.label.show.syntax') => t('help.label.show.desc') }
|
38
43
|
)
|
39
44
|
|
40
45
|
route(
|
41
|
-
/^locker\slabel\sadd\s#{RESOURCES_REGEX}\sto\s#{LABEL_REGEX}$/,
|
46
|
+
/^locker\slabel\sadd\s#{RESOURCES_REGEX}\sto\s#{LABEL_REGEX}#{COMMENT_REGEX}$/,
|
42
47
|
:add,
|
43
48
|
command: true,
|
44
49
|
help: { t('help.label.add.syntax') => t('help.label.add.desc') }
|
45
50
|
)
|
46
51
|
|
47
52
|
route(
|
48
|
-
/^locker\slabel\sremove\s#{RESOURCES_REGEX}\sfrom\s#{LABEL_REGEX}$/,
|
53
|
+
/^locker\slabel\sremove\s#{RESOURCES_REGEX}\sfrom\s#{LABEL_REGEX}#{COMMENT_REGEX}$/,
|
49
54
|
:remove,
|
50
55
|
command: true,
|
51
56
|
help: { t('help.label.remove.syntax') => t('help.label.remove.desc') }
|
52
57
|
)
|
53
58
|
|
54
59
|
def list(response)
|
55
|
-
|
56
|
-
|
60
|
+
begin
|
61
|
+
list = ::Locker::List.new(Label, config.per_page, response.extensions[:kwargs][:page])
|
62
|
+
rescue ArgumentError
|
63
|
+
return response.reply(t('list.invalid_page_type'))
|
64
|
+
end
|
57
65
|
|
58
|
-
|
59
|
-
if should_rate_limit
|
60
|
-
sleep 3
|
61
|
-
else
|
62
|
-
should_rate_limit = true
|
63
|
-
end
|
66
|
+
return response.reply(t('list.page_outside_range', pages: list.pages)) unless list.valid_page?
|
64
67
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
68
|
+
message = list.requested_page.map do |key|
|
69
|
+
label = Label.new(key)
|
70
|
+
|
71
|
+
state = label.state.value.to_s
|
72
|
+
|
73
|
+
case state
|
74
|
+
when 'unlocked'
|
75
|
+
unlocked(t('label.desc', name: key, state: state))
|
76
|
+
when 'locked'
|
77
|
+
locked(t('label.desc', name: key, state: state))
|
78
|
+
else
|
79
|
+
# This case shouldn't happen, but it will if a label
|
80
|
+
# gets saved with some other value for `state`.
|
81
|
+
t('label.desc', name: key, state: state)
|
69
82
|
end
|
70
|
-
end
|
83
|
+
end.join("\n")
|
84
|
+
|
85
|
+
message += "\n#{t('list.paginate', page: list.page, pages: list.pages)}" if list.multiple_pages?
|
86
|
+
|
87
|
+
response.reply(message)
|
71
88
|
end
|
72
89
|
|
73
90
|
def create(response)
|
@@ -104,7 +121,7 @@ module Lita
|
|
104
121
|
name = response.match_data['label']
|
105
122
|
return response.reply(failed(t('label.does_not_exist', name: name))) unless Label.exists?(name)
|
106
123
|
l = Label.new(name)
|
107
|
-
return response.reply(t('label.has_no_resources', name: name)) unless l.membership.count
|
124
|
+
return response.reply(t('label.has_no_resources', name: name)) unless l.membership.count.positive?
|
108
125
|
res = []
|
109
126
|
l.membership.each do |member|
|
110
127
|
res.push(member)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Lita
|
2
4
|
module Handlers
|
3
5
|
# Misc Locker handlers
|
@@ -10,28 +12,28 @@ module Lita
|
|
10
12
|
include ::Locker::Resource
|
11
13
|
|
12
14
|
route(
|
13
|
-
/^locker\sstatus\s#{LABEL_WILDCARD_REGEX}$/,
|
15
|
+
/^locker\sstatus\s#{LABEL_WILDCARD_REGEX}#{COMMENT_REGEX}$/,
|
14
16
|
:status,
|
15
17
|
command: true,
|
16
18
|
help: { t('help.status.syntax') => t('help.status.desc') }
|
17
19
|
)
|
18
20
|
|
19
21
|
route(
|
20
|
-
/^locker\slist\s#{USER_REGEX}$/,
|
22
|
+
/^locker\slist\s#{USER_REGEX}#{COMMENT_REGEX}$/,
|
21
23
|
:list,
|
22
24
|
command: true,
|
23
25
|
help: { t('help.list.syntax') => t('help.list.desc') }
|
24
26
|
)
|
25
27
|
|
26
28
|
route(
|
27
|
-
/^locker\s(dq|dequeue)\s#{LABEL_REGEX}$/,
|
29
|
+
/^locker\s(dq|dequeue)\s#{LABEL_REGEX}#{COMMENT_REGEX}$/,
|
28
30
|
:dequeue,
|
29
31
|
command: true,
|
30
32
|
help: { t('help.dequeue.syntax') => t('help.dequeue.desc') }
|
31
33
|
)
|
32
34
|
|
33
35
|
route(
|
34
|
-
/^locker\slog\s#{LABEL_REGEX}$/,
|
36
|
+
/^locker\slog\s#{LABEL_REGEX}#{COMMENT_REGEX}$/,
|
35
37
|
:log,
|
36
38
|
command: true,
|
37
39
|
help: { t('help.log.syntax.') => t('help.log.desc') }
|
@@ -48,7 +50,7 @@ module Lita
|
|
48
50
|
|
49
51
|
def status(response)
|
50
52
|
name = response.match_data['label']
|
51
|
-
unless name
|
53
|
+
unless name.match?(/\*/)
|
52
54
|
# Literal query
|
53
55
|
return response.reply(status_label(name)) if Label.exists?(name)
|
54
56
|
return response.reply(status_resource(name)) if Resource.exists?(name)
|
@@ -90,7 +92,7 @@ module Lita
|
|
90
92
|
def status_label(name)
|
91
93
|
l = Label.new(name)
|
92
94
|
return unlocked(t('label.desc', name: name, state: l.state.value)) unless l.locked?
|
93
|
-
if l.wait_queue.count
|
95
|
+
if l.wait_queue.count.positive?
|
94
96
|
queue = []
|
95
97
|
l.wait_queue.each do |u|
|
96
98
|
usr = Lita::User.find_by_id(u)
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'locker/list'
|
4
|
+
|
1
5
|
module Lita
|
2
6
|
module Handlers
|
3
7
|
# Resource-related handlers
|
@@ -10,14 +14,15 @@ module Lita
|
|
10
14
|
include ::Locker::Resource
|
11
15
|
|
12
16
|
route(
|
13
|
-
/^locker\sresource\slist
|
17
|
+
/^locker\sresource\slist/,
|
14
18
|
:list,
|
15
19
|
command: true,
|
20
|
+
kwargs: { page: { default: 1 } },
|
16
21
|
help: { t('help.resource.list.syntax') => t('help.resource.list.desc') }
|
17
22
|
)
|
18
23
|
|
19
24
|
route(
|
20
|
-
/^locker\sresource\screate\s#{RESOURCES_REGEX}$/,
|
25
|
+
/^locker\sresource\screate\s#{RESOURCES_REGEX}#{COMMENT_REGEX}$/,
|
21
26
|
:create,
|
22
27
|
command: true,
|
23
28
|
restrict_to: [:locker_admins],
|
@@ -27,7 +32,7 @@ module Lita
|
|
27
32
|
)
|
28
33
|
|
29
34
|
route(
|
30
|
-
/^locker\sresource\sdelete\s#{RESOURCES_REGEX}$/,
|
35
|
+
/^locker\sresource\sdelete\s#{RESOURCES_REGEX}#{COMMENT_REGEX}$/,
|
31
36
|
:delete,
|
32
37
|
command: true,
|
33
38
|
restrict_to: [:locker_admins],
|
@@ -37,29 +42,41 @@ module Lita
|
|
37
42
|
)
|
38
43
|
|
39
44
|
route(
|
40
|
-
/^locker\sresource\sshow\s#{RESOURCE_REGEX}$/,
|
45
|
+
/^locker\sresource\sshow\s#{RESOURCE_REGEX}#{COMMENT_REGEX}$/,
|
41
46
|
:show,
|
42
47
|
command: true,
|
43
48
|
help: { t('help.resource.show.syntax') => t('help.resource.show.desc') }
|
44
49
|
)
|
45
50
|
|
46
51
|
def list(response)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
if should_rate_limit
|
52
|
-
sleep 3
|
53
|
-
else
|
54
|
-
should_rate_limit = true
|
55
|
-
end
|
56
|
-
|
57
|
-
slice.each do |r|
|
58
|
-
res = Resource.new(r)
|
59
|
-
response.reply(t('resource.desc', name: r, state: res.state.value))
|
60
|
-
end
|
61
|
-
end
|
52
|
+
begin
|
53
|
+
list = ::Locker::List.new(Resource, config.per_page, response.extensions[:kwargs][:page])
|
54
|
+
rescue ArgumentError
|
55
|
+
return response.reply(t('list.invalid_page_type'))
|
62
56
|
end
|
57
|
+
|
58
|
+
return response.reply(t('list.page_outside_range', pages: list.pages)) unless list.valid_page?
|
59
|
+
|
60
|
+
message = list.requested_page.map do |key|
|
61
|
+
resource = Resource.new(key)
|
62
|
+
|
63
|
+
state = resource.state.value
|
64
|
+
|
65
|
+
case state
|
66
|
+
when 'unlocked'
|
67
|
+
unlocked(t('resource.desc', name: key, state: state))
|
68
|
+
when 'locked'
|
69
|
+
locked(t('resource.desc', name: key, state: state))
|
70
|
+
else
|
71
|
+
# This case shouldn't happen, but it will if a label
|
72
|
+
# gets saved with some other value for `state`.
|
73
|
+
t('resource.desc', name: key, state: state)
|
74
|
+
end
|
75
|
+
end.join("\n")
|
76
|
+
|
77
|
+
message += "\n#{t('list.paginate', page: list.page, pages: list.pages)}" if list.multiple_pages?
|
78
|
+
|
79
|
+
response.reply(message)
|
63
80
|
end
|
64
81
|
|
65
82
|
def create(response)
|
@@ -99,7 +116,7 @@ module Lita
|
|
99
116
|
return response.reply(t('resource.does_not_exist', name: name)) unless Resource.exists?(name)
|
100
117
|
r = Resource.new(name)
|
101
118
|
resp = t('resource.desc', name: name, state: r.state.value)
|
102
|
-
if r.labels.count
|
119
|
+
if r.labels.count.positive?
|
103
120
|
resp += ', used by: '
|
104
121
|
r.labels.each do |label|
|
105
122
|
resp += Label.new(label).id
|
data/lib/locker/label.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Locker subsystem
|
2
4
|
module Locker
|
3
5
|
# Label helpers
|
@@ -40,7 +42,7 @@ module Locker
|
|
40
42
|
|
41
43
|
def self.delete(key)
|
42
44
|
raise 'Unknown label key' unless Label.exists?(key)
|
43
|
-
%w
|
45
|
+
%w[state owner_id membership wait_queue journal observer_ids].each do |item|
|
44
46
|
redis.del("label:#{key}:#{item}")
|
45
47
|
end
|
46
48
|
redis.srem('label-list', Label.normalize(key))
|
@@ -93,7 +95,7 @@ module Locker
|
|
93
95
|
log('Unlocked')
|
94
96
|
|
95
97
|
# FIXME: Possible race condition where resources become unavailable between unlock and relock
|
96
|
-
if wait_queue.count
|
98
|
+
if wait_queue.count.positive?
|
97
99
|
next_user = wait_queue.shift
|
98
100
|
lock!(next_user)
|
99
101
|
end
|
@@ -207,9 +209,7 @@ module Locker
|
|
207
209
|
l = Label.new(name)
|
208
210
|
l.membership.each do |resource_name|
|
209
211
|
resource = Locker::Resource::Resource.new(resource_name)
|
210
|
-
if resource.state.value == 'locked'
|
211
|
-
deps.push "#{resource_name} - #{resource.owner.name}"
|
212
|
-
end
|
212
|
+
deps.push "#{resource_name} - #{resource.owner.name}" if resource.state.value == 'locked'
|
213
213
|
end
|
214
214
|
msg += deps.join("\n")
|
215
215
|
msg
|
data/lib/locker/list.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Locker subsystem
|
4
|
+
module Locker
|
5
|
+
# A paginated list of items (e.g. labels or resources).
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class List
|
9
|
+
# @return [#list] The Ruby class of the item to be listed.
|
10
|
+
attr_reader :item_class
|
11
|
+
|
12
|
+
# @return [Enumerable] The full list of items.
|
13
|
+
attr_reader :list
|
14
|
+
|
15
|
+
# @return [Integer] The number of items displayed per page.
|
16
|
+
attr_reader :per_page
|
17
|
+
|
18
|
+
# @return [Integer] The page the user has requested.
|
19
|
+
attr_reader :page
|
20
|
+
|
21
|
+
# @return [Integer] The total number of pages.
|
22
|
+
attr_reader :pages
|
23
|
+
|
24
|
+
# @return [Integer] The zero-based index offset that the requested page starts on within the full list.
|
25
|
+
attr_reader :offset
|
26
|
+
|
27
|
+
def initialize(item_class, per_page, page)
|
28
|
+
@item_class = item_class
|
29
|
+
@list = item_class.list
|
30
|
+
@per_page = per_page
|
31
|
+
@page = Integer(page.to_s, 10)
|
32
|
+
@pages = (list.count / per_page).ceil + 1
|
33
|
+
@offset = per_page * (self.page - 1)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Whether or not the list has multiple pages.
|
37
|
+
#
|
38
|
+
# @return [Boolean]
|
39
|
+
def multiple_pages?
|
40
|
+
list.count > per_page
|
41
|
+
end
|
42
|
+
|
43
|
+
# An enumerable of the items in the requested page.
|
44
|
+
#
|
45
|
+
# @return [Enumerable]
|
46
|
+
def requested_page
|
47
|
+
list[offset, per_page]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Whether or not the requested page exists.
|
51
|
+
#
|
52
|
+
# @return [Boolean]
|
53
|
+
def valid_page?
|
54
|
+
page >= 1 && page <= pages
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/locker/misc.rb
CHANGED
data/lib/locker/regex.rb
CHANGED
data/lib/locker/resource.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Locker subsystem
|
2
4
|
module Locker
|
3
5
|
# Resource helpers
|
@@ -34,7 +36,7 @@ module Locker
|
|
34
36
|
|
35
37
|
def self.delete(key)
|
36
38
|
raise 'Unknown resource key' unless Resource.exists?(key)
|
37
|
-
%w
|
39
|
+
%w[state owner_id].each do |item|
|
38
40
|
redis.del("resource:#{key}:#{item}")
|
39
41
|
end
|
40
42
|
redis.srem('resource-list', key)
|
data/lita-locker.gemspec
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Gem::Specification.new do |spec|
|
2
4
|
spec.name = 'lita-locker'
|
3
|
-
spec.version = '1.0
|
5
|
+
spec.version = '1.1.0'
|
4
6
|
spec.authors = ['Eric Sigler']
|
5
7
|
spec.email = ['me@esigler.com']
|
6
8
|
spec.description = '"lock" and "unlock" arbitrary subjects'
|
@@ -15,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
15
17
|
spec.require_paths = ['lib']
|
16
18
|
|
17
19
|
spec.add_runtime_dependency 'lita', '>= 4.2'
|
20
|
+
spec.add_runtime_dependency 'lita-keyword-arguments'
|
18
21
|
spec.add_runtime_dependency 'redis-objects'
|
19
22
|
spec.add_runtime_dependency 'time-lord'
|
20
23
|
|
data/locales/en.yml
CHANGED
@@ -16,6 +16,10 @@ en:
|
|
16
16
|
already_observing: "%{user}, you are already observing %{name}"
|
17
17
|
stopped_observing: "%{user}, you have stopped observing %{name}"
|
18
18
|
were_not_observing: "%{user}, you were not observing %{name}"
|
19
|
+
list:
|
20
|
+
paginate: "Page %{page} of %{pages} shown. Use --page to specify additional pages."
|
21
|
+
invalid_page_type: "Page specified must be an integer."
|
22
|
+
page_outside_range: "Page specified must be between 1 and %{pages}."
|
19
23
|
help:
|
20
24
|
log:
|
21
25
|
syntax: locker log <label>
|
@@ -49,7 +53,7 @@ en:
|
|
49
53
|
desc: Show what locks a user currently holds
|
50
54
|
resource:
|
51
55
|
list:
|
52
|
-
syntax: locker resource list
|
56
|
+
syntax: locker resource list [--page N]
|
53
57
|
desc: List all resources
|
54
58
|
create:
|
55
59
|
syntax: "locker resource create <name>[, <name> ...]"
|
@@ -62,7 +66,7 @@ en:
|
|
62
66
|
desc: Show the state of <name>
|
63
67
|
label:
|
64
68
|
list:
|
65
|
-
syntax: locker label list
|
69
|
+
syntax: locker label list [--page N]
|
66
70
|
desc: List all labels
|
67
71
|
create:
|
68
72
|
syntax: "locker label create <name>[, <name> ...]"
|
@@ -105,7 +109,7 @@ en:
|
|
105
109
|
unlocked_no_queue: "%{name} is unlocked and no one is next up %{mention}"
|
106
110
|
unable_to_lock: "%{name} unable to be locked"
|
107
111
|
lock: "%{name} locked"
|
108
|
-
desc: "%{name} is
|
112
|
+
desc: "%{name} is %{state}"
|
109
113
|
desc_owner: "%{name} is locked by %{owner_name} (taken %{time})"
|
110
114
|
desc_owner_queue: "%{name} is locked by %{owner_name} (taken %{time}). Next up: %{queue}"
|
111
115
|
created: "Label %{name} created"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Lita::Handlers::LockerHttp, lita_handler: true do
|
@@ -24,7 +26,7 @@ describe Lita::Handlers::LockerHttp, lita_handler: true do
|
|
24
26
|
it 'shows json if the label exists' do
|
25
27
|
send_command('locker label create foo')
|
26
28
|
subject.label_show(request, response)
|
27
|
-
expect(response.body).to eq(['{"id":"foo","state":"unlocked","membership":""}'])
|
29
|
+
expect(response.body).to eq(['{"id":"foo","state":"unlocked","membership":{"key":"label:foo:membership","options":{"type":"set"},"value":[]}}'])
|
28
30
|
end
|
29
31
|
|
30
32
|
it 'shows 404 if the label does not exist' do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Lita::Handlers::LockerLabels, lita_handler: true do
|
@@ -17,6 +19,12 @@ describe Lita::Handlers::LockerLabels, lita_handler: true do
|
|
17
19
|
is_expected.to route_command("locker label add foo, bar to #{l}").to(:add)
|
18
20
|
is_expected.to route_command("locker label remove resource from #{l}").to(:remove)
|
19
21
|
is_expected.to route_command("locker label remove foo, bar from #{l}").to(:remove)
|
22
|
+
|
23
|
+
is_expected.to route_command("locker label create #{l} # comment").to(:create)
|
24
|
+
is_expected.to route_command("locker label delete #{l} # comment").to(:delete)
|
25
|
+
is_expected.to route_command("locker label show #{l} # comment").to(:show)
|
26
|
+
is_expected.to route_command("locker label add resource to #{l} # comment").to(:add)
|
27
|
+
is_expected.to route_command("locker label remove resource from #{l} # comment").to(:remove)
|
20
28
|
end
|
21
29
|
end
|
22
30
|
|
@@ -33,12 +41,55 @@ describe Lita::Handlers::LockerLabels, lita_handler: true do
|
|
33
41
|
|
34
42
|
describe '#label_list' do
|
35
43
|
it 'shows a list of labels if there are any' do
|
44
|
+
send_command('locker resource create whatever')
|
36
45
|
send_command('locker label create foobar')
|
37
46
|
send_command('locker label create bazbat')
|
47
|
+
send_command('locker label add whatever to bazbat')
|
48
|
+
send_command('lock bazbat')
|
38
49
|
send_command('locker label list')
|
39
|
-
|
40
|
-
expect(replies.include
|
41
|
-
|
50
|
+
expect(replies.last).to include('foobar is unlocked')
|
51
|
+
expect(replies.last).to include('bazbat is locked')
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when per_page is configured to 3' do
|
55
|
+
before do
|
56
|
+
robot.config.handlers.locker.per_page = 3
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when there are 4 labels' do
|
60
|
+
before do
|
61
|
+
send_command('locker label create 1')
|
62
|
+
send_command('locker label create 2')
|
63
|
+
send_command('locker label create 3')
|
64
|
+
send_command('locker label create 4')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'includes details about what page was shown' do
|
68
|
+
send_command('locker label list')
|
69
|
+
expect(replies.last).to include('Page 1 of 2 shown. Use --page to specify additional pages.')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'displays the page specified' do
|
73
|
+
send_command('locker label list --page 2')
|
74
|
+
expect(replies.last).to include('4 is unlocked')
|
75
|
+
expect(replies.last).to include('Page 2 of 2 shown. Use --page to specify additional pages.')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'rejects pages lower than 1' do
|
79
|
+
send_command('locker label list --page 0')
|
80
|
+
expect(replies.last).to eq('Page specified must be between 1 and 2.')
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'rejects pages higher than the number there are' do
|
84
|
+
send_command('locker label list --page 3')
|
85
|
+
expect(replies.last).to eq('Page specified must be between 1 and 2.')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'rejects non-integer values for pages' do
|
90
|
+
send_command('locker label list --page x')
|
91
|
+
expect(replies.last).to eq('Page specified must be an integer.')
|
92
|
+
end
|
42
93
|
end
|
43
94
|
end
|
44
95
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Lita::Handlers::LockerMisc, lita_handler: true do
|
@@ -18,19 +20,23 @@ describe Lita::Handlers::LockerMisc, lita_handler: true do
|
|
18
20
|
end
|
19
21
|
|
20
22
|
it { is_expected.to route_command('locker status foo*').to(:status) }
|
23
|
+
it { is_expected.to route_command('locker status foo* # comment').to(:status) }
|
21
24
|
|
22
25
|
it do
|
23
26
|
is_expected.to route_command('locker list @alice').to(:list)
|
24
27
|
is_expected.to route_command('locker list Alice').to(:list)
|
28
|
+
is_expected.to route_command('locker list Alice # comment').to(:list)
|
25
29
|
end
|
26
30
|
|
27
31
|
it do
|
28
32
|
is_expected.to route_command('locker log something').to(:log)
|
33
|
+
is_expected.to route_command('locker log something # comment').to(:log)
|
29
34
|
end
|
30
35
|
|
31
36
|
it do
|
32
37
|
is_expected.to route_command('locker dequeue something something').to(:dequeue)
|
33
38
|
is_expected.to route_command('locker dq something something').to(:dequeue)
|
39
|
+
is_expected.to route_command('locker dq something something # comment').to(:dequeue)
|
34
40
|
end
|
35
41
|
|
36
42
|
let(:alice) do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Lita::Handlers::LockerResources, lita_handler: true do
|
@@ -12,6 +14,9 @@ describe Lita::Handlers::LockerResources, lita_handler: true do
|
|
12
14
|
is_expected.to route_command("locker resource create #{r}").to(:create)
|
13
15
|
is_expected.to route_command("locker resource delete #{r}").to(:delete)
|
14
16
|
is_expected.to route_command("locker resource show #{r}").to(:show)
|
17
|
+
is_expected.to route_command("locker resource create #{r} # comment").to(:create)
|
18
|
+
is_expected.to route_command("locker resource delete #{r} # comment").to(:delete)
|
19
|
+
is_expected.to route_command("locker resource show #{r} # comment").to(:show)
|
15
20
|
end
|
16
21
|
end
|
17
22
|
|
@@ -25,15 +30,56 @@ describe Lita::Handlers::LockerResources, lita_handler: true do
|
|
25
30
|
end
|
26
31
|
|
27
32
|
it { is_expected.to route_command('locker resource list').to(:list) }
|
33
|
+
it { is_expected.to route_command('locker resource list # comment').to(:list) }
|
28
34
|
|
29
35
|
describe '#resource_list' do
|
30
36
|
it 'shows a list of resources if there are any' do
|
31
37
|
send_command('locker resource create foobar')
|
32
38
|
send_command('locker resource create bazbat')
|
33
39
|
send_command('locker resource list')
|
34
|
-
|
35
|
-
expect(replies).to include('Resource:
|
36
|
-
|
40
|
+
expect(replies.last).to include('Resource: foobar, state: unlocked')
|
41
|
+
expect(replies.last).to include('Resource: bazbat, state: unlocked')
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when per_page is configured to 3' do
|
45
|
+
before do
|
46
|
+
robot.config.handlers.locker.per_page = 3
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when there are 4 resources' do
|
50
|
+
before do
|
51
|
+
send_command('locker resource create 1')
|
52
|
+
send_command('locker resource create 2')
|
53
|
+
send_command('locker resource create 3')
|
54
|
+
send_command('locker resource create 4')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'includes details about what page was shown' do
|
58
|
+
send_command('locker resource list')
|
59
|
+
expect(replies.last).to include('Page 1 of 2 shown. Use --page to specify additional pages.')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'displays the page specified' do
|
63
|
+
send_command('locker resource list --page 2')
|
64
|
+
expect(replies.last).to include('Resource: 4, state: unlocked')
|
65
|
+
expect(replies.last).to include('Page 2 of 2 shown. Use --page to specify additional pages.')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'rejects pages lower than 1' do
|
69
|
+
send_command('locker resource list --page 0')
|
70
|
+
expect(replies.last).to eq('Page specified must be between 1 and 2.')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'rejects pages higher than the number there are' do
|
74
|
+
send_command('locker resource list --page 3')
|
75
|
+
expect(replies.last).to eq('Page specified must be between 1 and 2.')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'rejects non-integer values for pages' do
|
80
|
+
send_command('locker resource list --page x')
|
81
|
+
expect(replies.last).to eq('Page specified must be an integer.')
|
82
|
+
end
|
37
83
|
end
|
38
84
|
end
|
39
85
|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Locker::List do
|
6
|
+
let(:item_class) { double('item class', list: [one, two, three, four]) }
|
7
|
+
|
8
|
+
let(:one) { double('one') }
|
9
|
+
let(:two) { double('two') }
|
10
|
+
let(:three) { double('three') }
|
11
|
+
let(:four) { double('four') }
|
12
|
+
|
13
|
+
it 'raises an argument error if a non-integer page is requested' do
|
14
|
+
expect { described_class.new(item_class, 3, 'z') }.to raise_error(ArgumentError)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#multiple_pages' do
|
18
|
+
it 'is true when there are more total items than can be displayed on one page' do
|
19
|
+
subject = described_class.new(item_class, 1, 1)
|
20
|
+
|
21
|
+
expect(subject).to be_multiple_pages
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'is false when the whole list fits in one page' do
|
25
|
+
subject = described_class.new(item_class, 10, 1)
|
26
|
+
|
27
|
+
expect(subject).not_to be_multiple_pages
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#requested_page' do
|
32
|
+
context 'with an empty list' do
|
33
|
+
let(:item_class) { double('item class', list: []) }
|
34
|
+
|
35
|
+
it 'returns an empty list' do
|
36
|
+
subject = described_class.new(item_class, 3, 1)
|
37
|
+
|
38
|
+
expect(subject.requested_page).to be_empty
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with a single item list' do
|
43
|
+
let(:item_class) { double('item class', list: [one]) }
|
44
|
+
|
45
|
+
it 'returns a list with one item' do
|
46
|
+
subject = described_class.new(item_class, 3, 1)
|
47
|
+
|
48
|
+
expect(subject.requested_page).to eq([one])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with a four item list' do
|
53
|
+
it 'splits the pages correctly when there are two items per page' do
|
54
|
+
subject = described_class.new(item_class, 2, 1)
|
55
|
+
|
56
|
+
expect(subject.requested_page).to eq([one, two])
|
57
|
+
|
58
|
+
subject = described_class.new(item_class, 2, 2)
|
59
|
+
|
60
|
+
expect(subject.requested_page).to eq([three, four])
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'splits the pages correctly when there are three items per page' do
|
64
|
+
subject = described_class.new(item_class, 3, 1)
|
65
|
+
|
66
|
+
expect(subject.requested_page).to eq([one, two, three])
|
67
|
+
|
68
|
+
subject = described_class.new(item_class, 3, 2)
|
69
|
+
|
70
|
+
expect(subject.requested_page).to eq([four])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#valid_page?' do
|
76
|
+
it 'is true when the first page is requested' do
|
77
|
+
subject = described_class.new(item_class, 3, 1)
|
78
|
+
|
79
|
+
expect(subject).to be_valid_page
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'is true when the page is between 1 and the total number of pages' do
|
83
|
+
subject = described_class.new(item_class, 3, 2)
|
84
|
+
|
85
|
+
expect(subject).to be_valid_page
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'is false when the page is less than 1' do
|
89
|
+
subject = described_class.new(item_class, 3, 0)
|
90
|
+
|
91
|
+
expect(subject).not_to be_valid_page
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'is false when the page is greater than the total number of pages' do
|
95
|
+
subject = described_class.new(item_class, 3, 10)
|
96
|
+
|
97
|
+
expect(subject).not_to be_valid_page
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'simplecov'
|
2
4
|
require 'coveralls'
|
3
5
|
SimpleCov.formatters = [
|
@@ -12,12 +14,15 @@ Lita.version_3_compatibility_mode = false
|
|
12
14
|
|
13
15
|
RSpec.configure do |config|
|
14
16
|
config.before do
|
15
|
-
registry
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
if defined? registry
|
18
|
+
registry.register_hook(:trigger_route, Lita::Extensions::KeywordArguments)
|
19
|
+
registry.register_handler(Lita::Handlers::Locker)
|
20
|
+
registry.register_handler(Lita::Handlers::LockerEvents)
|
21
|
+
registry.register_handler(Lita::Handlers::LockerHttp)
|
22
|
+
registry.register_handler(Lita::Handlers::LockerLabels)
|
23
|
+
registry.register_handler(Lita::Handlers::LockerMisc)
|
24
|
+
registry.register_handler(Lita::Handlers::LockerResources)
|
25
|
+
end
|
21
26
|
end
|
22
27
|
end
|
23
28
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lita-locker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Sigler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lita
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: lita-keyword-arguments
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: redis-objects
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -146,6 +160,7 @@ files:
|
|
146
160
|
- ".gitignore"
|
147
161
|
- ".rspec"
|
148
162
|
- ".rubocop.yml"
|
163
|
+
- ".ruby-version"
|
149
164
|
- ".travis.yml"
|
150
165
|
- CONTRIBUTING.md
|
151
166
|
- Gemfile
|
@@ -161,6 +176,7 @@ files:
|
|
161
176
|
- lib/lita/handlers/locker_misc.rb
|
162
177
|
- lib/lita/handlers/locker_resources.rb
|
163
178
|
- lib/locker/label.rb
|
179
|
+
- lib/locker/list.rb
|
164
180
|
- lib/locker/misc.rb
|
165
181
|
- lib/locker/regex.rb
|
166
182
|
- lib/locker/resource.rb
|
@@ -172,6 +188,7 @@ files:
|
|
172
188
|
- spec/lita/handlers/locker_misc_spec.rb
|
173
189
|
- spec/lita/handlers/locker_resources_spec.rb
|
174
190
|
- spec/lita/handlers/locker_spec.rb
|
191
|
+
- spec/locker/list_spec.rb
|
175
192
|
- spec/spec_helper.rb
|
176
193
|
- templates/failed.erb
|
177
194
|
- templates/failed.hipchat.erb
|
@@ -209,7 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
209
226
|
version: '0'
|
210
227
|
requirements: []
|
211
228
|
rubyforge_project:
|
212
|
-
rubygems_version: 2.
|
229
|
+
rubygems_version: 2.6.13
|
213
230
|
signing_key:
|
214
231
|
specification_version: 4
|
215
232
|
summary: '"lock" and "unlock" arbitrary subjects'
|
@@ -220,4 +237,5 @@ test_files:
|
|
220
237
|
- spec/lita/handlers/locker_misc_spec.rb
|
221
238
|
- spec/lita/handlers/locker_resources_spec.rb
|
222
239
|
- spec/lita/handlers/locker_spec.rb
|
240
|
+
- spec/locker/list_spec.rb
|
223
241
|
- spec/spec_helper.rb
|