lita-locker 0.7.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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