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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c652f7dfc79a47babe632a2f9516bfeedf1b4781
4
- data.tar.gz: d4f5f7802f48182a08fab5f573f44b35ecac6b90
3
+ metadata.gz: 932d1d2d4b5f788199bd0b8d3ea0884987fd8dec
4
+ data.tar.gz: f677dbe6a38e68ed8fa1485445bd89e037ea61c5
5
5
  SHA512:
6
- metadata.gz: c9113338335470eca38f6b2b947618b115c624ffeea5dc81acf94e43636bb1aaecc359f00128653784aace1ed48647e2da0ec5dad5318d90b533e361d66a5fe5
7
- data.tar.gz: 2acb94c1f70b702c67baedb3e2cd8095a68c79517c1ec46e57558e8d755e18a203bc5d6987181301999ce1e67aada7cb028fe91289291e0a55bf650b50dad748
6
+ metadata.gz: cd4694f2fe5c4247b918c1e59afda10d6ab1417c7938eb057d33560eb7d6fff3dc58f4fb63025cf2435c006e7b308c10ce37a342187c4d2d244972551273fe23
7
+ data.tar.gz: c0aa1d169d48406afb06f513efcb244b5df5f6b9dd05c22e0215c9efe9505deff2976efcf8fa0dc487d553a87467d7b8937b732f75941daa0fd51c2656c8aac7
data/.rubocop.yml CHANGED
@@ -1,21 +1,15 @@
1
- ClassLength:
2
- Max: 103
3
-
4
- CyclomaticComplexity:
5
- Max: 7
6
-
7
1
  FileName:
8
2
  Exclude:
9
3
  - lib/lita-locker.rb
10
4
 
11
- MethodLength:
12
- Max: 31
13
-
14
- PerceivedComplexity:
15
- Max: 12
16
-
17
5
  LineLength:
18
- Max: 120
6
+ Max: 130
19
7
 
20
8
  AbcSize:
21
- Max: 49
9
+ Max: 30
10
+
11
+ MethodLength:
12
+ Max: 20
13
+
14
+ ClassLength:
15
+ Max: 150
data/.travis.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.1
4
- script: bundle exec rake
5
- before_install:
6
- - gem update --system
4
+ - 2.2
5
+ cache: bundler
6
+ sudo: false
7
7
  services:
8
8
  - redis-server
data/README.md CHANGED
@@ -43,15 +43,10 @@ unlock <label> - Remove a reservation. This can only be done by whomever
43
43
  steal <label> - Force removal of a reservation. This can be done by anyone. Can have # comments afterwards.
44
44
  ```
45
45
 
46
- ### Time-based locking - Not implemented yet!
46
+ ### Queueing
47
47
  ```
48
- lock <subject> <time> - A time-limited reservation. <time> must be a number with a "s", "m", or "h" postfix.
49
- ```
50
-
51
- ### Reservations - Not implemented yet!
52
- ```
53
- reserve <subject> - Add yourself to a FIFO queue of pending reservations for <subject>
54
- unreserve <subject> - Remove yourself from the queue for <subject>
48
+ lock <label> - If <label> is already locked, adds you to a FIFO queue of pending reservations for <label>
49
+ locker dequeue <label> - Remove yourself from the queue for <label>
55
50
  ```
56
51
 
57
52
  ### Labels
data/UPGRADING.md ADDED
@@ -0,0 +1,61 @@
1
+ # lita-locker upgrading
2
+
3
+ ## Overview
4
+
5
+ lita-locker 1.x has a breaking data model change. This enables a huge number
6
+ of new features, such as queueing and timestamps. You'll need to export out
7
+ your existing data and bring it back in when you upgrade lita-locker.
8
+
9
+ As a note, the 0.x and 1.x plugins use different redis keysets, so it's possible
10
+ to upgrade to 1.x and retain your old data, should you need to downgrade for
11
+ some reason. However, 1.x locks will not show up in 0.x clients, and vice-versa.
12
+
13
+ ## Example migration script
14
+
15
+ The below is a no-warranties-provided Ruby script you can use to export your
16
+ existing data, and optionally remove the older information.
17
+
18
+ ``
19
+ require 'redis'
20
+
21
+ bot_prefix = "!"
22
+ remove_old_data = false
23
+ redis = Redis.new
24
+ resources = []
25
+ labels = []
26
+
27
+ redis.keys('lita:handlers:locker:resource_*').each do |k|
28
+ resources.push(k.gsub(/^lita:handlers:locker:resource_/, ''))
29
+ end
30
+
31
+ resources.each_slice(10) do |batch|
32
+ puts "#{bot_prefix}locker resource create #{batch.join(', ')}\n\n"
33
+ end
34
+
35
+ redis.keys('lita:handlers:locker:label_*').each do |k|
36
+ labels.push(k.gsub(/^lita:handlers:locker:label_/, ''))
37
+ end
38
+
39
+ labels.each_slice(10) do |batch|
40
+ puts "#{bot_prefix}locker label create #{batch.join(', ')}\n\n"
41
+ end
42
+
43
+ labels.each do |label|
44
+ members = []
45
+ redis.smembers("lita:handlers:locker:membership_#{label}").each do |k|
46
+ members.push(k)
47
+ end
48
+ puts "#{bot_prefix}locker label add #{members.join(', ')} to #{label}\n" if members.count > 0
49
+ end
50
+
51
+ if remove_old_data
52
+ resources.each do |r|
53
+ redis.del("lita:handlers:locker:resource_#{r}")
54
+ end
55
+
56
+ labels.each do |l|
57
+ redis.del("lita:handlers:locker:label_#{l}")
58
+ redis.del("lita:handlers:locker:membership_#{l}")
59
+ end
60
+ end
61
+ ``
@@ -3,6 +3,8 @@ module Lita
3
3
  module Handlers
4
4
  # Top-level class for Locker
5
5
  class Locker < Handler
6
+ on :loaded, :setup_redis
7
+
6
8
  include ::Locker::Label
7
9
  include ::Locker::Misc
8
10
  include ::Locker::Regex
@@ -39,82 +41,63 @@ module Lita
39
41
  help: { t('help.steal.syntax') => t('help.steal.desc') }
40
42
  )
41
43
 
44
+ def setup_redis(_payload)
45
+ Label.redis = redis
46
+ Resource.redis = redis
47
+ end
48
+
42
49
  def lock(response)
43
- name = response.matches[0][0]
44
-
45
- return response.reply('(failed) ' + t('label.does_not_exist', name: name)) unless label_exists?(name)
46
- m = label_membership(name)
47
- return response.reply('(failed) ' + t('label.no_resources', name: name)) unless m.count > 0
48
- return response.reply('(successful) ' + t('label.lock', name: name)) if lock_label!(name, response.user, nil)
49
-
50
- l = label(name)
51
- if l['state'] == 'locked'
52
- o = Lita::User.find_by_id(l['owner_id'])
53
- if o.mention_name
54
- response.reply('(failed) ' + t('label.owned_mention',
55
- name: name,
56
- owner_name: o.name,
57
- owner_mention: o.mention_name))
58
- else
59
- response.reply('(failed) ' + t('label.owned',
60
- name: name,
61
- owner_name: o.name))
62
- end
63
- else
64
- msg = '(failed) ' + t('label.dependency') + "\n"
65
- deps = []
66
- label_membership(name).each do |resource_name|
67
- resource = resource(resource_name)
68
- u = Lita::User.find_by_id(resource['owner_id'])
69
- if resource['state'] == 'locked'
70
- deps.push "#{resource_name} - #{u.name}"
71
- end
72
- end
73
- msg += deps.join("\n")
74
- response.reply(msg)
75
- end
50
+ name = response.match_data['label']
51
+
52
+ return response.reply(failed(t('label.does_not_exist', name: name))) unless Label.exists?(name)
53
+ l = Label.new(name)
54
+ return response.reply(failed(t('label.no_resources', name: name))) unless l.membership.count > 0
55
+ return response.reply(t('label.self_lock', name: name)) if l.owner == response.user
56
+ return response.reply(success(t('label.lock', name: name))) if l.lock!(response.user.id)
57
+
58
+ response.reply(label_ownership(name))
76
59
  end
77
60
 
78
61
  def unlock(response)
79
- name = response.matches[0][0]
80
- return response.reply('(failed) ' + t('subject.does_not_exist', name: name)) unless label_exists?(name)
81
- l = label(name)
82
- return response.reply('(successful) ' + t('label.is_unlocked', name: name)) if l['state'] == 'unlocked'
83
-
84
- if response.user.id == l['owner_id']
85
- unlock_label!(name)
86
- response.reply('(successful) ' + t('label.unlock', name: name))
87
- else
88
- o = Lita::User.find_by_id(l['owner_id'])
89
- if o.mention_name
90
- response.reply('(failed) ' + t('label.owned_mention',
91
- name: name,
92
- owner_name: o.name,
93
- owner_mention: o.mention_name))
94
- else
95
- response.reply('(failed) ' + t('label.owned',
96
- name: name,
97
- owner_name: o.name))
98
- end
99
- end
62
+ name = response.match_data['label']
63
+ return response.reply(failed(t('subject.does_not_exist', name: name))) unless Label.exists?(name)
64
+ l = Label.new(name)
65
+ return response.reply(success(t('label.is_unlocked', name: name))) unless l.locked?
66
+ response.reply(attempt_unlock(name, response.user))
100
67
  end
101
68
 
102
69
  def steal(response)
103
- name = response.matches[0][0]
104
- return response.reply('(failed) ' + t('subject.does_not_exist', name: name)) unless label_exists?(name)
105
- l = label(name)
106
- return response.reply(t('steal.already_unlocked', label: name)) unless l['state'] == 'locked'
107
- o = Lita::User.find_by_id(l['owner_id'])
108
- if o.id != response.user.id
109
- unlock_label!(name)
110
- lock_label!(name, response.user, nil)
111
- mention = o.mention_name ? "(@#{o.mention_name})" : ''
112
- response.reply('(successful) ' + t('steal.stolen',
113
- label: name,
114
- old_owner: o.name,
115
- mention: mention))
70
+ name = response.match_data['label']
71
+ return response.reply(failed(t('subject.does_not_exist', name: name))) unless Label.exists?(name)
72
+ l = Label.new(name)
73
+ return response.reply(t('steal.already_unlocked', label: name)) unless l.locked?
74
+ response.reply(attempt_steal(name, response.user))
75
+ end
76
+
77
+ private
78
+
79
+ def attempt_steal(name, user)
80
+ label = Label.new(name)
81
+ return t('steal.self') if label.owner == user
82
+ old_owner = label.owner
83
+ label.steal!(user.id)
84
+ mention = old_owner.mention_name ? "(@#{old_owner.mention_name})" : ''
85
+ success(t('steal.stolen', label: name, old_owner: old_owner.name, mention: mention))
86
+ end
87
+
88
+ def attempt_unlock(name, user)
89
+ label = Label.new(name)
90
+ if label.owner == user
91
+ label.unlock!
92
+ if label.locked?
93
+ mention = label.owner.mention_name ? "(@#{label.owner.mention_name})" : ''
94
+ failed(t('label.now_locked_by', name: name, owner: label.owner.name, mention: mention))
95
+ else
96
+ success(t('label.unlock', name: name))
97
+ end
116
98
  else
117
- response.reply(t('steal.self'))
99
+ mention = label.owner.mention_name ? "(@#{label.owner.mention_name})" : ''
100
+ failed(t('label.owned_unlock', name: name, owner_name: label.owner.name, mention: mention, time: label.held_for))
118
101
  end
119
102
  end
120
103
  end
@@ -17,7 +17,9 @@ module Lita
17
17
  user = Lita::User.find_by_id(payload[:user_id])
18
18
  request_id = payload[:request_id]
19
19
 
20
- if label_exists?(label) && lock_label!(label, user, nil)
20
+ return unless Label.exists?(label)
21
+ l = Label.new(label)
22
+ if l.lock!(user.id)
21
23
  robot.trigger(:lock_success, request_id: request_id)
22
24
  else
23
25
  robot.trigger(:lock_failure, request_id: request_id)
@@ -28,7 +30,9 @@ module Lita
28
30
  label = payload[:label]
29
31
  request_id = payload[:request_id]
30
32
 
31
- if label_exists?(label) && unlock_label!(label)
33
+ return unless Label.exists?(label)
34
+ l = Label.new(label)
35
+ if l.unlock!
32
36
  robot.trigger(:unlock_success, request_id: request_id)
33
37
  else
34
38
  robot.trigger(:unlock_failure, request_id: request_id)
@@ -9,19 +9,29 @@ module Lita
9
9
  include ::Locker::Regex
10
10
  include ::Locker::Resource
11
11
 
12
- http.get '/locker/label/:name', :http_label_show
13
- http.get '/locker/resource/:name', :http_resource_show
12
+ http.get '/locker/label/:name', :label_show
13
+ http.get '/locker/resource/:name', :resource_show
14
14
 
15
- def http_label_show(request, response)
15
+ def label_show(request, response)
16
16
  name = request.env['router.params'][:name]
17
17
  response.headers['Content-Type'] = 'application/json'
18
- response.write(label(name).to_json)
18
+ unless Label.exists?(name)
19
+ response.status = 404
20
+ return
21
+ end
22
+ l = Label.new(name)
23
+ response.write(l.to_json)
19
24
  end
20
25
 
21
- def http_resource_show(request, response)
26
+ def resource_show(request, response)
22
27
  name = request.env['router.params'][:name]
23
28
  response.headers['Content-Type'] = 'application/json'
24
- response.write(resource(name).to_json)
29
+ unless Resource.exists?(name)
30
+ response.status = 404
31
+ return
32
+ end
33
+ r = Resource.new(name)
34
+ response.write(r.to_json)
25
35
  end
26
36
 
27
37
  Lita.register_handler(LockerHttp)
@@ -17,14 +17,14 @@ module Lita
17
17
  )
18
18
 
19
19
  route(
20
- /^locker\slabel\screate\s#{LABEL_REGEX}$/,
20
+ /^locker\slabel\screate\s#{LABELS_REGEX}$/,
21
21
  :create,
22
22
  command: true,
23
23
  help: { t('help.label.create.syntax') => t('help.label.create.desc') }
24
24
  )
25
25
 
26
26
  route(
27
- /^locker\slabel\sdelete\s#{LABEL_REGEX}$/,
27
+ /^locker\slabel\sdelete\s#{LABELS_REGEX}$/,
28
28
  :delete,
29
29
  command: true,
30
30
  help: { t('help.label.delete.syntax') => t('help.label.delete.desc') }
@@ -38,74 +38,120 @@ module Lita
38
38
  )
39
39
 
40
40
  route(
41
- /^locker\slabel\sadd\s#{RESOURCE_REGEX}\sto\s#{LABEL_REGEX}$/,
41
+ /^locker\slabel\sadd\s#{RESOURCES_REGEX}\sto\s#{LABEL_REGEX}$/,
42
42
  :add,
43
43
  command: true,
44
44
  help: { t('help.label.add.syntax') => t('help.label.add.desc') }
45
45
  )
46
46
 
47
47
  route(
48
- /^locker\slabel\sremove\s#{RESOURCE_REGEX}\sfrom\s#{LABEL_REGEX}$/,
48
+ /^locker\slabel\sremove\s#{RESOURCES_REGEX}\sfrom\s#{LABEL_REGEX}$/,
49
49
  :remove,
50
50
  command: true,
51
51
  help: { t('help.label.remove.syntax') => t('help.label.remove.desc') }
52
52
  )
53
53
 
54
54
  def list(response)
55
- labels.sort.each do |n|
56
- name = n.sub('label_', '')
57
- l = label(name)
58
- response.reply(t('label.desc', name: name, state: l['state']))
55
+ should_rate_limit = false
56
+
57
+ Label.list.each_slice(10) do |slice|
58
+ if should_rate_limit
59
+ sleep 1
60
+ else
61
+ should_rate_limit = true
62
+ end
63
+
64
+ slice.each do |n|
65
+ l = Label.new(n)
66
+ response.reply(unlocked(t('label.desc', name: n, state: l.state.value)))
67
+ end
59
68
  end
60
69
  end
61
70
 
62
71
  def create(response)
63
- name = response.matches[0][0]
64
- if create_label(name)
65
- response.reply(t('label.created', name: name))
66
- else
67
- response.reply(t('label.exists', name: name))
72
+ names = response.match_data['labels'].split(/,\s*/)
73
+ results = []
74
+
75
+ names.each do |name|
76
+ if !Label.exists?(name) && Label.create(name)
77
+ results <<= t('label.created', name: name)
78
+ else
79
+ results <<= t('label.exists', name: name)
80
+ end
68
81
  end
82
+
83
+ response.reply(results.join(', '))
69
84
  end
70
85
 
71
86
  def delete(response)
72
- name = response.matches[0][0]
73
- if delete_label(name)
74
- response.reply(t('label.deleted', name: name))
75
- else
76
- response.reply(t('label.does_not_exist', name: name))
87
+ names = response.match_data['labels'].split(/,\s*/)
88
+ results = []
89
+
90
+ names.each do |name|
91
+ if Label.exists?(name) && Label.delete(name)
92
+ results <<= t('label.deleted', name: name)
93
+ else
94
+ results <<= failed(t('label.does_not_exist', name: name))
95
+ end
77
96
  end
97
+
98
+ response.reply(results.join(', '))
78
99
  end
79
100
 
80
101
  def show(response)
81
- name = response.matches[0][0]
82
- return response.reply(t('label.does_not_exist', name: name)) unless label_exists?(name)
83
- members = label_membership(name)
84
- return response.reply(t('label.has_no_resources', name: name)) unless members.count > 0
85
- response.reply(t('label.resources', name: name, resources: members.join(', ')))
102
+ name = response.match_data['label']
103
+ return response.reply(failed(t('label.does_not_exist', name: name))) unless Label.exists?(name)
104
+ l = Label.new(name)
105
+ return response.reply(t('label.has_no_resources', name: name)) unless l.membership.count > 0
106
+ res = []
107
+ l.membership.each do |member|
108
+ res.push(member)
109
+ end
110
+ response.reply(t('label.resources', name: name, resources: res.join(', ')))
86
111
  end
87
112
 
88
113
  def add(response)
89
- resource_name = response.matches[0][0]
90
- label_name = response.matches[0][1]
91
- return response.reply(t('label.does_not_exist', name: label_name)) unless label_exists?(label_name)
92
- return response.reply(t('resource.does_not_exist', name: resource_name)) unless resource_exists?(resource_name)
93
- add_resource_to_label(label_name, resource_name)
94
- response.reply(t('label.resource_added', label: label_name, resource: resource_name))
114
+ results = []
115
+ resource_names = response.match_data['resources'].split(/,\s*/)
116
+ label_name = response.match_data['label']
117
+ return response.reply(failed(t('label.does_not_exist', name: label_name))) unless Label.exists?(label_name)
118
+
119
+ resource_names.each do |resource_name|
120
+ if Resource.exists?(resource_name)
121
+ l = Label.new(label_name)
122
+ r = Resource.new(resource_name)
123
+ l.add_resource(r)
124
+ results <<= t('label.resource_added', label: label_name, resource: resource_name)
125
+ else
126
+ results <<= t('resource.does_not_exist', name: resource_name)
127
+ end
128
+ end
129
+
130
+ response.reply(results.join(', '))
95
131
  end
96
132
 
97
133
  def remove(response)
98
- resource_name = response.matches[0][0]
99
- label_name = response.matches[0][1]
100
- return response.reply(t('label.does_not_exist', name: label_name)) unless label_exists?(label_name)
101
- return response.reply(t('resource.does_not_exist', name: resource_name)) unless resource_exists?(resource_name)
102
- members = label_membership(label_name)
103
- if members.include?(resource_name)
104
- remove_resource_from_label(label_name, resource_name)
105
- response.reply(t('label.resource_removed', label: label_name, resource: resource_name))
106
- else
107
- response.reply(t('label.does_not_have_resource', label: label_name, resource: resource_name))
134
+ results = []
135
+ resource_names = response.match_data['resources'].split(/,\s*/)
136
+ label_name = response.match_data['label']
137
+ return response.reply(failed(t('label.does_not_exist', name: label_name))) unless Label.exists?(label_name)
138
+
139
+ resource_names.each do |resource_name|
140
+ if Resource.exists?(resource_name)
141
+ l = Label.new(label_name)
142
+ if l.membership.include?(resource_name)
143
+ r = Resource.new(resource_name)
144
+ l.remove_resource(r)
145
+ results <<= t('label.resource_removed', label: label_name, resource: resource_name)
146
+ else
147
+ results <<= t('label.does_not_have_resource', label: label_name, resource: resource_name)
148
+ end
149
+ else
150
+ results <<= t('resource.does_not_exist', name: resource_name)
151
+ end
108
152
  end
153
+
154
+ response.reply(results.join(', '))
109
155
  end
110
156
 
111
157
  Lita.register_handler(LockerLabels)
@@ -23,32 +23,50 @@ module Lita
23
23
  help: { t('help.list.syntax') => t('help.list.desc') }
24
24
  )
25
25
 
26
- def status(response)
27
- name = response.matches[0][0]
28
- if label_exists?(name)
29
- l = label(name)
30
- if l['owner_id'] && l['owner_id'] != ''
31
- o = Lita::User.find_by_id(l['owner_id'])
32
- response.reply(t('label.desc_owner', name: name,
33
- state: l['state'],
34
- owner_name: o.name))
35
- else
36
- response.reply(t('label.desc', name: name, state: l['state']))
37
- end
38
- elsif resource_exists?(name)
39
- r = resource(name)
40
- response.reply(t('resource.desc', name: name, state: r['state']))
41
- else
42
- response.reply(t('subject.does_not_exist', name: name))
26
+ route(
27
+ /^locker\sdequeue\s#{LABEL_REGEX}$/,
28
+ :dequeue,
29
+ command: true,
30
+ help: { t('help.dequeue.syntax') => t('help.dequeue.desc') }
31
+ )
32
+
33
+ route(
34
+ /^locker\slog\s#{LABEL_REGEX}$/,
35
+ :log,
36
+ command: true,
37
+ help: { t('help.log.syntax.') => t('help.log.desc') }
38
+ )
39
+
40
+ def log(response)
41
+ name = response.match_data['label']
42
+ return response.reply(failed(t('subject.does_not_exist', name: name))) unless Label.exists?(name)
43
+ l = Label.new(name)
44
+ l.journal.range(-10, -1).each do |entry|
45
+ response.reply(t('label.log_entry', entry: entry))
43
46
  end
44
47
  end
45
48
 
49
+ def status(response)
50
+ name = response.match_data['label']
51
+ return response.reply(status_label(name)) if Label.exists?(name)
52
+ return response.reply(status_resource(name)) if Resource.exists?(name)
53
+ response.reply(failed(t('subject.does_not_exist', name: name)))
54
+ end
55
+
56
+ def dequeue(response)
57
+ name = response.match_data['label']
58
+ return response.reply(t('subject.does_not_exist', name: name)) unless Label.exists?(name)
59
+ l = Label.new(name)
60
+ l.wait_queue.delete(response.user.id)
61
+ response.reply(t('label.removed_from_queue', name: name))
62
+ end
63
+
46
64
  def list(response)
47
65
  username = response.match_data['username']
48
66
  user = Lita::User.fuzzy_find(username)
49
- return response.reply('Unknown user') unless user
67
+ return response.reply(t('user.unknown')) unless user
50
68
  l = user_locks(user)
51
- return response.reply('That user has no active locks') unless l.size > 0
69
+ return response.reply(t('user.no_active_locks')) unless l.size > 0
52
70
  composed = ''
53
71
  l.each do |label_name|
54
72
  composed += "Label: #{label_name}\n"
@@ -56,6 +74,35 @@ module Lita
56
74
  response.reply(composed)
57
75
  end
58
76
 
77
+ private
78
+
79
+ def status_label(name)
80
+ l = Label.new(name)
81
+ return unlocked(t('label.desc', name: name, state: l.state.value)) unless l.locked?
82
+ if l.wait_queue.count > 0
83
+ queue = []
84
+ l.wait_queue.each do |u|
85
+ usr = Lita::User.find_by_id(u)
86
+ queue.push(usr.name)
87
+ end
88
+ locked(t('label.desc_owner_queue', name: name,
89
+ state: l.state.value,
90
+ owner_name: l.owner.name,
91
+ time: l.held_for,
92
+ queue: queue.join(', ')))
93
+ else
94
+ locked(t('label.desc_owner', name: name,
95
+ state: l.state.value,
96
+ owner_name: l.owner.name,
97
+ time: l.held_for))
98
+ end
99
+ end
100
+
101
+ def status_resource(name)
102
+ r = Resource.new(name)
103
+ t('resource.desc', name: name, state: r.state.value)
104
+ end
105
+
59
106
  Lita.register_handler(LockerMisc)
60
107
  end
61
108
  end