lita-locker 0.7.0 → 1.0.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 +4 -4
- data/.rubocop.yml +8 -14
- data/.travis.yml +3 -3
- data/README.md +3 -8
- data/UPGRADING.md +61 -0
- data/lib/lita/handlers/locker.rb +51 -68
- data/lib/lita/handlers/locker_events.rb +6 -2
- data/lib/lita/handlers/locker_http.rb +16 -6
- data/lib/lita/handlers/locker_labels.rb +85 -39
- data/lib/lita/handlers/locker_misc.rb +66 -19
- data/lib/lita/handlers/locker_resources.rb +51 -21
- data/lib/lita-locker.rb +23 -0
- data/lib/locker/label.rb +155 -48
- data/lib/locker/misc.rb +19 -4
- data/lib/locker/regex.rb +5 -3
- data/lib/locker/resource.rb +70 -35
- data/lita-locker.gemspec +9 -4
- data/locales/en.yml +31 -16
- data/spec/lita/handlers/locker_http_spec.rb +42 -4
- data/spec/lita/handlers/locker_labels_spec.rb +64 -6
- data/spec/lita/handlers/locker_misc_spec.rb +67 -8
- data/spec/lita/handlers/locker_resources_spec.rb +53 -6
- data/spec/lita/handlers/locker_spec.rb +82 -32
- data/templates/failed.erb +1 -0
- data/templates/failed.hipchat.erb +1 -0
- data/templates/failed.slack.erb +1 -0
- data/templates/lock.erb +1 -0
- data/templates/lock.hipchat.erb +1 -0
- data/templates/lock.slack.erb +1 -0
- data/templates/success.erb +1 -0
- data/templates/success.hipchat.erb +1 -0
- data/templates/success.slack.erb +1 -0
- data/templates/unlock.erb +1 -0
- data/templates/unlock.hipchat.erb +1 -0
- data/templates/unlock.slack.erb +1 -0
- metadata +47 -6
@@ -17,7 +17,7 @@ module Lita
|
|
17
17
|
)
|
18
18
|
|
19
19
|
route(
|
20
|
-
/^locker\sresource\screate\s#{
|
20
|
+
/^locker\sresource\screate\s#{RESOURCES_REGEX}$/,
|
21
21
|
:create,
|
22
22
|
command: true,
|
23
23
|
restrict_to: [:locker_admins],
|
@@ -27,7 +27,7 @@ module Lita
|
|
27
27
|
)
|
28
28
|
|
29
29
|
route(
|
30
|
-
/^locker\sresource\sdelete\s#{
|
30
|
+
/^locker\sresource\sdelete\s#{RESOURCES_REGEX}$/,
|
31
31
|
:delete,
|
32
32
|
command: true,
|
33
33
|
restrict_to: [:locker_admins],
|
@@ -44,36 +44,66 @@ module Lita
|
|
44
44
|
)
|
45
45
|
|
46
46
|
def list(response)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
should_rate_limit = false
|
48
|
+
|
49
|
+
Resource.list.each_slice(10) do |slice|
|
50
|
+
if should_rate_limit
|
51
|
+
sleep 1
|
52
|
+
else
|
53
|
+
should_rate_limit = true
|
54
|
+
end
|
55
|
+
|
56
|
+
slice.each do |r|
|
57
|
+
res = Resource.new(r)
|
58
|
+
response.reply(t('resource.desc', name: r, state: res.state.value))
|
59
|
+
end
|
52
60
|
end
|
53
|
-
response.reply(output)
|
54
61
|
end
|
55
62
|
|
56
63
|
def create(response)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
names = response.match_data['resources'].split(/,\s*/)
|
65
|
+
results = []
|
66
|
+
|
67
|
+
names.each do |name|
|
68
|
+
if Resource.exists?(name)
|
69
|
+
results <<= t('resource.exists', name: name)
|
70
|
+
else
|
71
|
+
Resource.create(name)
|
72
|
+
results <<= t('resource.created', name: name)
|
73
|
+
end
|
62
74
|
end
|
75
|
+
|
76
|
+
response.reply(results.join(', '))
|
63
77
|
end
|
64
78
|
|
65
79
|
def delete(response)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
80
|
+
names = response.match_data['resources'].split(/,\s*/)
|
81
|
+
results = []
|
82
|
+
|
83
|
+
names.each do |name|
|
84
|
+
if Resource.exists?(name)
|
85
|
+
Resource.delete(name)
|
86
|
+
results <<= t('resource.deleted', name: name)
|
87
|
+
else
|
88
|
+
results <<= t('resource.does_not_exist', name: name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
response.reply(results.join(', '))
|
70
93
|
end
|
71
94
|
|
72
95
|
def show(response)
|
73
|
-
name = response.
|
74
|
-
return response.reply(t('resource.does_not_exist', name: name)) unless
|
75
|
-
r =
|
76
|
-
|
96
|
+
name = response.match_data['resource']
|
97
|
+
return response.reply(t('resource.does_not_exist', name: name)) unless Resource.exists?(name)
|
98
|
+
r = Resource.new(name)
|
99
|
+
resp = t('resource.desc', name: name, state: r.state.value)
|
100
|
+
if r.labels.count > 0
|
101
|
+
resp += ', used by: '
|
102
|
+
r.labels.each do |label|
|
103
|
+
resp += Label.new(label).id
|
104
|
+
end
|
105
|
+
end
|
106
|
+
response.reply(resp)
|
77
107
|
end
|
78
108
|
|
79
109
|
Lita.register_handler(LockerResources)
|
data/lib/lita-locker.rb
CHANGED
@@ -4,6 +4,9 @@ Lita.load_locales Dir[File.expand_path(
|
|
4
4
|
File.join('..', '..', 'locales', '*.yml'), __FILE__
|
5
5
|
)]
|
6
6
|
|
7
|
+
require 'redis-objects'
|
8
|
+
require 'time-lord'
|
9
|
+
|
7
10
|
require 'locker/label'
|
8
11
|
require 'locker/misc'
|
9
12
|
require 'locker/regex'
|
@@ -15,3 +18,23 @@ require 'lita/handlers/locker_labels'
|
|
15
18
|
require 'lita/handlers/locker_misc'
|
16
19
|
require 'lita/handlers/locker_resources'
|
17
20
|
require 'lita/handlers/locker'
|
21
|
+
|
22
|
+
Lita::Handlers::Locker.template_root File.expand_path(
|
23
|
+
File.join('..', '..', 'templates'),
|
24
|
+
__FILE__
|
25
|
+
)
|
26
|
+
|
27
|
+
Lita::Handlers::LockerResources.template_root File.expand_path(
|
28
|
+
File.join('..', '..', 'templates'),
|
29
|
+
__FILE__
|
30
|
+
)
|
31
|
+
|
32
|
+
Lita::Handlers::LockerLabels.template_root File.expand_path(
|
33
|
+
File.join('..', '..', 'templates'),
|
34
|
+
__FILE__
|
35
|
+
)
|
36
|
+
|
37
|
+
Lita::Handlers::LockerMisc.template_root File.expand_path(
|
38
|
+
File.join('..', '..', 'templates'),
|
39
|
+
__FILE__
|
40
|
+
)
|
data/lib/locker/label.rb
CHANGED
@@ -2,66 +2,173 @@
|
|
2
2
|
module Locker
|
3
3
|
# Label helpers
|
4
4
|
module Label
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# Proper Resource class
|
6
|
+
class Label
|
7
|
+
include Redis::Objects
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
value :state
|
10
|
+
value :owner_id
|
11
|
+
value :taken_at
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
set :membership
|
14
|
+
list :wait_queue
|
15
|
+
list :journal
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
members = label_membership(name)
|
21
|
-
members.each do |m|
|
22
|
-
return false unless lock_resource!(m, owner, time_until)
|
23
|
-
end
|
24
|
-
redis.hset(key, 'state', 'locked')
|
25
|
-
redis.hset(key, 'owner_id', owner.id)
|
26
|
-
redis.hset(key, 'until', time_until)
|
27
|
-
true
|
28
|
-
end
|
17
|
+
lock :coord, expiration: 5
|
18
|
+
|
19
|
+
attr_reader :id
|
29
20
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
members = label_membership(name)
|
34
|
-
members.each do |m|
|
35
|
-
unlock_resource!(m)
|
21
|
+
def initialize(key)
|
22
|
+
fail 'Unknown label key' unless Label.exists?(key)
|
23
|
+
@id = Label.normalize(key)
|
36
24
|
end
|
37
|
-
redis.hset(key, 'state', 'unlocked')
|
38
|
-
redis.hset(key, 'owner_id', '')
|
39
|
-
true
|
40
|
-
end
|
41
25
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
resource_exists?(name) || label_exists?(name)
|
46
|
-
end
|
26
|
+
def self.exists?(key)
|
27
|
+
redis.sismember('label-list', Label.normalize(key))
|
28
|
+
end
|
47
29
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
30
|
+
def self.create(key)
|
31
|
+
fail 'Label key already exists' if Label.exists?(key)
|
32
|
+
redis.sadd('label-list', Label.normalize(key))
|
33
|
+
l = Label.new(key)
|
34
|
+
l.state = 'unlocked'
|
35
|
+
l.owner_id = ''
|
36
|
+
l.log('Created')
|
37
|
+
l
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.delete(key)
|
41
|
+
fail 'Unknown label key' unless Label.exists?(key)
|
42
|
+
%w(state, owner_id, membership, wait_queue, journal).each do |item|
|
43
|
+
redis.del("label:#{key}:#{item}")
|
44
|
+
end
|
45
|
+
redis.srem('label-list', Label.normalize(key))
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.list
|
49
|
+
redis.smembers('label-list').sort
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.normalize(key)
|
53
|
+
key.strip.downcase
|
54
|
+
end
|
55
|
+
|
56
|
+
def lock!(owner_id)
|
57
|
+
if locked?
|
58
|
+
wait_queue << owner_id if wait_queue.last != owner_id
|
59
|
+
return false
|
60
|
+
end
|
61
|
+
|
62
|
+
coord_lock.lock do
|
63
|
+
membership.each do |resource_name|
|
64
|
+
r = Locker::Resource::Resource.new(resource_name)
|
65
|
+
return false if r.locked?
|
66
|
+
end
|
67
|
+
# TODO: read-modify-write cycle, not the best
|
68
|
+
membership.each do |resource_name|
|
69
|
+
r = Locker::Resource::Resource.new(resource_name)
|
70
|
+
r.lock!(owner_id)
|
71
|
+
end
|
72
|
+
self.owner_id = owner_id
|
73
|
+
self.state = 'locked'
|
74
|
+
self.taken_at = Time.now.utc
|
75
|
+
end
|
76
|
+
u = Lita::User.fuzzy_find(owner_id)
|
77
|
+
log("Locked by #{u.name}")
|
78
|
+
true
|
79
|
+
end
|
80
|
+
|
81
|
+
def unlock!
|
82
|
+
return true if state == 'unlocked'
|
83
|
+
coord_lock.lock do
|
84
|
+
self.owner_id = ''
|
85
|
+
self.state = 'unlocked'
|
86
|
+
self.taken_at = ''
|
87
|
+
membership.each do |resource_name|
|
88
|
+
r = Locker::Resource::Resource.new(resource_name)
|
89
|
+
r.unlock!
|
90
|
+
end
|
91
|
+
end
|
92
|
+
log('Unlocked')
|
52
93
|
|
53
|
-
|
54
|
-
|
94
|
+
# FIXME: Possible race condition where resources become unavailable between unlock and relock
|
95
|
+
if wait_queue.count > 0
|
96
|
+
next_user = wait_queue.shift
|
97
|
+
self.lock!(next_user)
|
98
|
+
end
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def steal!(owner_id)
|
103
|
+
log("Stolen from #{owner.id} to #{owner_id}")
|
104
|
+
wait_queue.unshift(owner_id)
|
105
|
+
self.unlock!
|
106
|
+
end
|
107
|
+
|
108
|
+
def locked?
|
109
|
+
(state == 'locked')
|
110
|
+
end
|
111
|
+
|
112
|
+
def add_resource(resource)
|
113
|
+
log("Resource #{resource.id} added")
|
114
|
+
resource.labels << id
|
115
|
+
membership << resource.id
|
116
|
+
end
|
117
|
+
|
118
|
+
def remove_resource(resource)
|
119
|
+
log("Resource #{resource.id} removed")
|
120
|
+
resource.labels.delete(id)
|
121
|
+
membership.delete(resource.id)
|
122
|
+
end
|
123
|
+
|
124
|
+
def owner
|
125
|
+
return nil unless locked?
|
126
|
+
Lita::User.find_by_id(owner_id.value)
|
127
|
+
end
|
128
|
+
|
129
|
+
def held_for
|
130
|
+
return '' unless locked?
|
131
|
+
TimeLord::Time.new(Time.parse(taken_at.value) - 1).period.to_words
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_json
|
135
|
+
val = { id: id,
|
136
|
+
state: state.value,
|
137
|
+
membership: membership }
|
138
|
+
|
139
|
+
if locked?
|
140
|
+
val[:owner_id] = owner_id.value
|
141
|
+
val[:taken_at] = taken_at.value
|
142
|
+
val[:wait_queue] = wait_queue
|
143
|
+
end
|
144
|
+
|
145
|
+
val.to_json
|
146
|
+
end
|
147
|
+
|
148
|
+
def log(statement)
|
149
|
+
journal << "#{Time.now.utc}: #{statement}"
|
150
|
+
end
|
55
151
|
end
|
56
152
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
153
|
+
def label_ownership(name)
|
154
|
+
l = Label.new(name)
|
155
|
+
return label_dependencies(name) unless l.locked?
|
156
|
+
mention = l.owner.mention_name ? "(@#{l.owner.mention_name})" : ''
|
157
|
+
failed(t('label.owned_lock', name: name, owner_name: l.owner.name, mention: mention, time: l.held_for))
|
60
158
|
end
|
61
159
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
160
|
+
def label_dependencies(name)
|
161
|
+
msg = failed(t('label.dependency')) + "\n"
|
162
|
+
deps = []
|
163
|
+
l = Label.new(name)
|
164
|
+
l.membership.each do |resource_name|
|
165
|
+
resource = Locker::Resource::Resource.new(resource_name)
|
166
|
+
if resource.state.value == 'locked'
|
167
|
+
deps.push "#{resource_name} - #{resource.owner.name}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
msg += deps.join("\n")
|
171
|
+
msg
|
65
172
|
end
|
66
173
|
end
|
67
174
|
end
|
data/lib/locker/misc.rb
CHANGED
@@ -4,12 +4,27 @@ module Locker
|
|
4
4
|
module Misc
|
5
5
|
def user_locks(user)
|
6
6
|
owned = []
|
7
|
-
|
8
|
-
name
|
9
|
-
label
|
10
|
-
owned.push(name) if label['owner_id'] == user.id
|
7
|
+
Locker::Label::Label.list.each do |name|
|
8
|
+
label = Locker::Label::Label.new(name)
|
9
|
+
owned.push(name) if label.owner == user
|
11
10
|
end
|
12
11
|
owned
|
13
12
|
end
|
13
|
+
|
14
|
+
def success(message)
|
15
|
+
render_template('success', string: message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def failed(message)
|
19
|
+
render_template('failed', string: message)
|
20
|
+
end
|
21
|
+
|
22
|
+
def locked(message)
|
23
|
+
render_template('lock', string: message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def unlocked(message)
|
27
|
+
render_template('unlock', string: message)
|
28
|
+
end
|
14
29
|
end
|
15
30
|
end
|
data/lib/locker/regex.rb
CHANGED
@@ -2,11 +2,13 @@
|
|
2
2
|
module Locker
|
3
3
|
# Regex definitions
|
4
4
|
module Regex
|
5
|
-
LABEL_REGEX = /([\.\w\s-]+)
|
6
|
-
|
5
|
+
LABEL_REGEX = /(?<label>[\.\w\s-]+)(\s)?/
|
6
|
+
LABELS_REGEX = /(?<labels>[\.\w\s-]+(?:,\s*[\.\w\s-]+)*)(\s)?/
|
7
|
+
RESOURCE_REGEX = /(?<resource>[\.\w-]+)/
|
8
|
+
RESOURCES_REGEX = /(?<resources>[\.\w-]+(?:,\s*[\.\w-]+)*)/
|
7
9
|
COMMENT_REGEX = /(\s\#.+)?/
|
8
10
|
LOCK_REGEX = /\(lock\)\s/i
|
9
|
-
USER_REGEX = /(?:@)?(?<username>[\w\s]+)/
|
11
|
+
USER_REGEX = /(?:@)?(?<username>[\w\s-]+)/
|
10
12
|
UNLOCK_REGEX = /(?:\(unlock\)|\(release\))\s/i
|
11
13
|
end
|
12
14
|
end
|
data/lib/locker/resource.rb
CHANGED
@@ -2,47 +2,82 @@
|
|
2
2
|
module Locker
|
3
3
|
# Resource helpers
|
4
4
|
module Resource
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# Proper Resource class
|
6
|
+
class Resource
|
7
|
+
include Redis::Objects
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
value :state
|
10
|
+
value :owner_id
|
11
|
+
set :labels
|
12
12
|
|
13
|
-
|
14
|
-
redis.exists("resource_#{name}")
|
15
|
-
end
|
13
|
+
lock :coord, expiration: 5
|
16
14
|
|
17
|
-
|
18
|
-
return false unless resource_exists?(name)
|
19
|
-
resource_key = "resource_#{name}"
|
20
|
-
value = redis.hget(resource_key, 'state')
|
21
|
-
return false unless value == 'unlocked'
|
22
|
-
# FIXME: Race condition!
|
23
|
-
redis.hset(resource_key, 'state', 'locked')
|
24
|
-
redis.hset(resource_key, 'owner_id', owner.id)
|
25
|
-
redis.hset(resource_key, 'until', time_until)
|
26
|
-
true
|
27
|
-
end
|
15
|
+
attr_reader :id
|
28
16
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
redis.hset(key, 'owner_id', '')
|
34
|
-
true
|
35
|
-
end
|
17
|
+
def initialize(key)
|
18
|
+
fail 'Unknown resource key' unless Resource.exists?(key)
|
19
|
+
@id = key
|
20
|
+
end
|
36
21
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
22
|
+
def self.exists?(key)
|
23
|
+
redis.sismember('resource-list', key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.create(key)
|
27
|
+
fail 'Resource key already exists' if Resource.exists?(key)
|
28
|
+
redis.sadd('resource-list', key)
|
29
|
+
r = Resource.new(key)
|
30
|
+
r.state = 'unlocked'
|
31
|
+
r.owner_id = ''
|
32
|
+
r
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.delete(key)
|
36
|
+
fail 'Unknown resource key' unless Resource.exists?(key)
|
37
|
+
%w(state, owner_id).each do |item|
|
38
|
+
redis.del("resource:#{key}:#{item}")
|
39
|
+
end
|
40
|
+
redis.srem('resource-list', key)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.list
|
44
|
+
redis.smembers('resource-list').sort
|
45
|
+
end
|
46
|
+
|
47
|
+
def lock!(owner_id)
|
48
|
+
return false if state == 'locked'
|
49
|
+
coord_lock.lock do
|
50
|
+
self.owner_id = owner_id
|
51
|
+
self.state = 'locked'
|
52
|
+
end
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def unlock!
|
57
|
+
return true if state == 'unlocked'
|
58
|
+
coord_lock.lock do
|
59
|
+
self.owner_id = ''
|
60
|
+
self.state = 'unlocked'
|
61
|
+
end
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def locked?
|
66
|
+
(state == 'locked')
|
67
|
+
end
|
68
|
+
|
69
|
+
def owner
|
70
|
+
return nil unless locked?
|
71
|
+
Lita::User.find_by_id(owner_id.value)
|
72
|
+
end
|
42
73
|
|
43
|
-
|
44
|
-
|
45
|
-
|
74
|
+
def to_json
|
75
|
+
{
|
76
|
+
id: id,
|
77
|
+
state: state.value,
|
78
|
+
owner_id: owner_id.value
|
79
|
+
}.to_json
|
80
|
+
end
|
46
81
|
end
|
47
82
|
end
|
48
83
|
end
|
data/lita-locker.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = 'lita-locker'
|
3
|
-
spec.version = '0.
|
3
|
+
spec.version = '1.0.0'
|
4
4
|
spec.authors = ['Eric Sigler']
|
5
5
|
spec.email = ['me@esigler.com']
|
6
6
|
spec.description = '"lock" and "unlock" arbitrary subjects'
|
@@ -10,11 +10,13 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.metadata = { 'lita_plugin_type' => 'handler' }
|
11
11
|
|
12
12
|
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
13
|
-
spec.executables = spec.files.grep(
|
14
|
-
spec.test_files = spec.files.grep(
|
13
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
14
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
15
15
|
spec.require_paths = ['lib']
|
16
16
|
|
17
|
-
spec.add_runtime_dependency 'lita', '>= 4.
|
17
|
+
spec.add_runtime_dependency 'lita', '>= 4.2'
|
18
|
+
spec.add_runtime_dependency 'redis-objects'
|
19
|
+
spec.add_runtime_dependency 'time-lord'
|
18
20
|
|
19
21
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
20
22
|
spec.add_development_dependency 'coveralls'
|
@@ -22,4 +24,7 @@ Gem::Specification.new do |spec|
|
|
22
24
|
spec.add_development_dependency 'rspec', '>= 3.0.0'
|
23
25
|
spec.add_development_dependency 'rubocop'
|
24
26
|
spec.add_development_dependency 'simplecov'
|
27
|
+
|
28
|
+
spec.post_install_message = 'After upgrading to lita-locker 1.x, you should read: ' \
|
29
|
+
'https://github.com/esigler/lita-locker/blob/master/UPGRADING.md'
|
25
30
|
end
|
data/locales/en.yml
CHANGED
@@ -7,6 +7,12 @@ en:
|
|
7
7
|
already_unlocked: "%{label} was already unlocked"
|
8
8
|
self: Why are you stealing the lock from yourself?
|
9
9
|
help:
|
10
|
+
log:
|
11
|
+
syntax: locker log <label>
|
12
|
+
desc: Show up to the last 10 activity log entries for <label>
|
13
|
+
dequeue:
|
14
|
+
syntax: locker dequeue <label>
|
15
|
+
desc: Remove yourself from the queue for a label
|
10
16
|
lock:
|
11
17
|
syntax: lock <subject>
|
12
18
|
desc: Make something unavailable to others. Can have # comments afterwards.
|
@@ -27,11 +33,11 @@ en:
|
|
27
33
|
syntax: locker resource list
|
28
34
|
desc: List all resources
|
29
35
|
create:
|
30
|
-
syntax: locker resource create <name>
|
31
|
-
desc: Create
|
36
|
+
syntax: "locker resource create <name>[, <name> ...]"
|
37
|
+
desc: Create resource(s) with each <name>
|
32
38
|
delete:
|
33
|
-
syntax: locker resource delete <name>
|
34
|
-
desc: Delete the resource with <name>
|
39
|
+
syntax: "locker resource delete <name>[, <name> ...]"
|
40
|
+
desc: Delete the resource(s) with each <name>
|
35
41
|
show:
|
36
42
|
syntax: locker resource show <name>
|
37
43
|
desc: Show the state of <name>
|
@@ -40,20 +46,20 @@ en:
|
|
40
46
|
syntax: locker label list
|
41
47
|
desc: List all labels
|
42
48
|
create:
|
43
|
-
syntax: locker label create <name>
|
44
|
-
desc: Create
|
49
|
+
syntax: "locker label create <name>[, <name> ...]"
|
50
|
+
desc: Create label(s) with each <name>
|
45
51
|
delete:
|
46
|
-
syntax: locker label delete <name>
|
47
|
-
desc: Delete the label with <name>
|
52
|
+
syntax: "locker label delete <name>[, <name> ...]"
|
53
|
+
desc: Delete the label(s) with each <name>
|
48
54
|
show:
|
49
55
|
syntax: locker label show <name>
|
50
56
|
desc: Show all resources for <name>
|
51
57
|
add:
|
52
|
-
syntax: locker label add <resource> to <name>
|
53
|
-
desc: Adds <resource> to the list of things to lock/unlock for <name>
|
58
|
+
syntax: "locker label add <resource>[, <resource> ...] to <name>"
|
59
|
+
desc: Adds each <resource> to the list of things to lock/unlock for <name>
|
54
60
|
remove:
|
55
|
-
syntax: locker label remove <resource> from <name>
|
56
|
-
desc: Removes <resource> from <name>
|
61
|
+
syntax: "locker label remove <resource>[, <resource> ...] from <name>"
|
62
|
+
desc: Removes each <resource> from <name>
|
57
63
|
resource:
|
58
64
|
created: "Resource %{name} created"
|
59
65
|
desc: "Resource: %{name}, state: %{state}"
|
@@ -69,14 +75,17 @@ en:
|
|
69
75
|
subject:
|
70
76
|
does_not_exist: "Sorry, that does not exist"
|
71
77
|
label:
|
78
|
+
log_entry: "%{entry}"
|
79
|
+
self_lock: "You already have the lock on %{name}"
|
72
80
|
unlock: "%{name} unlocked"
|
73
|
-
|
74
|
-
|
81
|
+
owned_lock: "%{name} is locked by %{owner_name} %{mention} (taken %{time}), you have been added to the queue, type 'locker dequeue %{name}' to be removed"
|
82
|
+
owned_unlock: "%{name} is locked by %{owner_name} %{mention} (taken %{time})"
|
75
83
|
is_unlocked: "%{name} is unlocked"
|
76
84
|
unable_to_lock: "%{name} unable to be locked"
|
77
85
|
lock: "%{name} locked"
|
78
|
-
desc: "
|
79
|
-
desc_owner: "
|
86
|
+
desc: "%{name} is unlocked"
|
87
|
+
desc_owner: "%{name} is locked by %{owner_name} (taken %{time})"
|
88
|
+
desc_owner_queue: "%{name} is locked by %{owner_name} (taken %{time}). Next up: %{queue}"
|
80
89
|
created: "Label %{name} created"
|
81
90
|
exists: "%{name} already exists"
|
82
91
|
deleted: "Label %{name} deleted"
|
@@ -88,3 +97,9 @@ en:
|
|
88
97
|
does_not_have_resource: "Label %{label} does not have Resource %{resource}"
|
89
98
|
no_resources: "%{name} has no resources, so it cannot be locked"
|
90
99
|
dependency: 'Label unable to be locked, blocked on:'
|
100
|
+
now_locked_by: "%{name} now locked by %{owner} %{mention}"
|
101
|
+
removed_from_queue: "You have been removed from the queue for %{name}"
|
102
|
+
unknown_in_queue: "You weren't in the queue for %{name}"
|
103
|
+
user:
|
104
|
+
unknown: Unknown user
|
105
|
+
no_active_locks: That user has no active locks
|