lita-totems 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 52a873b89b3b0fcda3f1ac5cb94df40319ce0898
4
+ data.tar.gz: 9be24d8f06768443fe0cab96f3fa80cd71b3d055
5
+ SHA512:
6
+ metadata.gz: e36de592b355f98f4ca21785829f165d0eb22ec3b4532d32ba8b3c54246014e43c18af94f550018a690378cafee197bf4aac21a2063f4a527f5dc6f684b7b39c
7
+ data.tar.gz: d56dba1969ac6a4369cfbbc35d76a1a353ddd9d2f70052f6aef263fc44dac33ec3774ecec0f88355c95f04b38f5c03e7a4f9107aef3cb4b25f05fdcbf5da0cc5
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
19
+ .DS_Store
20
+
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ script: bundle exec rake
5
+ before_install:
6
+ - gem update --system
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,191 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and
10
+ distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright
13
+ owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all other entities
16
+ that control, are controlled by, or are under common control with that entity.
17
+ For the purposes of this definition, "control" means (i) the power, direct or
18
+ indirect, to cause the direction or management of such entity, whether by
19
+ contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20
+ outstanding shares, or (iii) beneficial ownership of such entity.
21
+
22
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
23
+ permissions granted by this License.
24
+
25
+ "Source" form shall mean the preferred form for making modifications, including
26
+ but not limited to software source code, documentation source, and configuration
27
+ files.
28
+
29
+ "Object" form shall mean any form resulting from mechanical transformation or
30
+ translation of a Source form, including but not limited to compiled object code,
31
+ generated documentation, and conversions to other media types.
32
+
33
+ "Work" shall mean the work of authorship, whether in Source or Object form, made
34
+ available under the License, as indicated by a copyright notice that is included
35
+ in or attached to the work (an example is provided in the Appendix below).
36
+
37
+ "Derivative Works" shall mean any work, whether in Source or Object form, that
38
+ is based on (or derived from) the Work and for which the editorial revisions,
39
+ annotations, elaborations, or other modifications represent, as a whole, an
40
+ original work of authorship. For the purposes of this License, Derivative Works
41
+ shall not include works that remain separable from, or merely link (or bind by
42
+ name) to the interfaces of, the Work and Derivative Works thereof.
43
+
44
+ "Contribution" shall mean any work of authorship, including the original version
45
+ of the Work and any modifications or additions to that Work or Derivative Works
46
+ thereof, that is intentionally submitted to Licensor for inclusion in the Work
47
+ by the copyright owner or by an individual or Legal Entity authorized to submit
48
+ on behalf of the copyright owner. For the purposes of this definition,
49
+ "submitted" means any form of electronic, verbal, or written communication sent
50
+ to the Licensor or its representatives, including but not limited to
51
+ communication on electronic mailing lists, source code control systems, and
52
+ issue tracking systems that are managed by, or on behalf of, the Licensor for
53
+ the purpose of discussing and improving the Work, but excluding communication
54
+ that is conspicuously marked or otherwise designated in writing by the copyright
55
+ owner as "Not a Contribution."
56
+
57
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58
+ of whom a Contribution has been received by Licensor and subsequently
59
+ incorporated within the Work.
60
+
61
+ 2. Grant of Copyright License.
62
+
63
+ Subject to the terms and conditions of this License, each Contributor hereby
64
+ grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65
+ irrevocable copyright license to reproduce, prepare Derivative Works of,
66
+ publicly display, publicly perform, sublicense, and distribute the Work and such
67
+ Derivative Works in Source or Object form.
68
+
69
+ 3. Grant of Patent License.
70
+
71
+ Subject to the terms and conditions of this License, each Contributor hereby
72
+ grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73
+ irrevocable (except as stated in this section) patent license to make, have
74
+ made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75
+ such license applies only to those patent claims licensable by such Contributor
76
+ that are necessarily infringed by their Contribution(s) alone or by combination
77
+ of their Contribution(s) with the Work to which such Contribution(s) was
78
+ submitted. If You institute patent litigation against any entity (including a
79
+ cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80
+ Contribution incorporated within the Work constitutes direct or contributory
81
+ patent infringement, then any patent licenses granted to You under this License
82
+ for that Work shall terminate as of the date such litigation is filed.
83
+
84
+ 4. Redistribution.
85
+
86
+ You may reproduce and distribute copies of the Work or Derivative Works thereof
87
+ in any medium, with or without modifications, and in Source or Object form,
88
+ provided that You meet the following conditions:
89
+
90
+ You must give any other recipients of the Work or Derivative Works a copy of
91
+ this License; and
92
+ You must cause any modified files to carry prominent notices stating that You
93
+ changed the files; and
94
+ You must retain, in the Source form of any Derivative Works that You distribute,
95
+ all copyright, patent, trademark, and attribution notices from the Source form
96
+ of the Work, excluding those notices that do not pertain to any part of the
97
+ Derivative Works; and
98
+ If the Work includes a "NOTICE" text file as part of its distribution, then any
99
+ Derivative Works that You distribute must include a readable copy of the
100
+ attribution notices contained within such NOTICE file, excluding those notices
101
+ that do not pertain to any part of the Derivative Works, in at least one of the
102
+ following places: within a NOTICE text file distributed as part of the
103
+ Derivative Works; within the Source form or documentation, if provided along
104
+ with the Derivative Works; or, within a display generated by the Derivative
105
+ Works, if and wherever such third-party notices normally appear. The contents of
106
+ the NOTICE file are for informational purposes only and do not modify the
107
+ License. You may add Your own attribution notices within Derivative Works that
108
+ You distribute, alongside or as an addendum to the NOTICE text from the Work,
109
+ provided that such additional attribution notices cannot be construed as
110
+ modifying the License.
111
+ You may add Your own copyright statement to Your modifications and may provide
112
+ additional or different license terms and conditions for use, reproduction, or
113
+ distribution of Your modifications, or for any such Derivative Works as a whole,
114
+ provided Your use, reproduction, and distribution of the Work otherwise complies
115
+ with the conditions stated in this License.
116
+
117
+ 5. Submission of Contributions.
118
+
119
+ Unless You explicitly state otherwise, any Contribution intentionally submitted
120
+ for inclusion in the Work by You to the Licensor shall be under the terms and
121
+ conditions of this License, without any additional terms or conditions.
122
+ Notwithstanding the above, nothing herein shall supersede or modify the terms of
123
+ any separate license agreement you may have executed with Licensor regarding
124
+ such Contributions.
125
+
126
+ 6. Trademarks.
127
+
128
+ This License does not grant permission to use the trade names, trademarks,
129
+ service marks, or product names of the Licensor, except as required for
130
+ reasonable and customary use in describing the origin of the Work and
131
+ reproducing the content of the NOTICE file.
132
+
133
+ 7. Disclaimer of Warranty.
134
+
135
+ Unless required by applicable law or agreed to in writing, Licensor provides the
136
+ Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138
+ including, without limitation, any warranties or conditions of TITLE,
139
+ NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140
+ solely responsible for determining the appropriateness of using or
141
+ redistributing the Work and assume any risks associated with Your exercise of
142
+ permissions under this License.
143
+
144
+ 8. Limitation of Liability.
145
+
146
+ In no event and under no legal theory, whether in tort (including negligence),
147
+ contract, or otherwise, unless required by applicable law (such as deliberate
148
+ and grossly negligent acts) or agreed to in writing, shall any Contributor be
149
+ liable to You for damages, including any direct, indirect, special, incidental,
150
+ or consequential damages of any character arising as a result of this License or
151
+ out of the use or inability to use the Work (including but not limited to
152
+ damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153
+ any and all other commercial damages or losses), even if such Contributor has
154
+ been advised of the possibility of such damages.
155
+
156
+ 9. Accepting Warranty or Additional Liability.
157
+
158
+ While redistributing the Work or Derivative Works thereof, You may choose to
159
+ offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160
+ other liability obligations and/or rights consistent with this License. However,
161
+ in accepting such obligations, You may act only on Your own behalf and on Your
162
+ sole responsibility, not on behalf of any other Contributor, and only if You
163
+ agree to indemnify, defend, and hold each Contributor harmless for any liability
164
+ incurred by, or claims asserted against, such Contributor by reason of your
165
+ accepting any such warranty or additional liability.
166
+
167
+ END OF TERMS AND CONDITIONS
168
+
169
+ APPENDIX: How to apply the Apache License to your work
170
+
171
+ To apply the Apache License to your work, attach the following boilerplate
172
+ notice, with the fields enclosed by brackets "[]" replaced with your own
173
+ identifying information. (Don't include the brackets!) The text should be
174
+ enclosed in the appropriate comment syntax for the file format. We also
175
+ recommend that a file or class name and description of purpose be included on
176
+ the same "printed page" as the copyright notice for easier identification within
177
+ third-party archives.
178
+
179
+ Copyright [yyyy] [name of copyright owner]
180
+
181
+ Licensed under the Apache License, Version 2.0 (the "License");
182
+ you may not use this file except in compliance with the License.
183
+ You may obtain a copy of the License at
184
+
185
+ http://www.apache.org/licenses/LICENSE-2.0
186
+
187
+ Unless required by applicable law or agreed to in writing, software
188
+ distributed under the License is distributed on an "AS IS" BASIS,
189
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190
+ See the License for the specific language governing permissions and
191
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # lita-totems
2
+
3
+ Totems handler for [Lita](https://github.com/jimmycuadra/lita)
4
+
5
+ ## Installation
6
+
7
+ Add lita-totems to your Lita instance's Gemfile:
8
+
9
+ ``` ruby
10
+ gem "lita-totems"
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ TODO: Describe any configuration attributes the plugin exposes.
16
+
17
+ ## Usage
18
+
19
+ TODO: Describe the plugin's features and how to use them.
20
+
21
+ ## License
22
+
23
+ [MIT](http://opensource.org/licenses/MIT)
24
+
25
+ ## Todo:
26
+
27
+ * core routes:
28
+ C Create a totem (set ACL?)
29
+ * create
30
+ C Destroy a totem
31
+ * destroy, delete
32
+ C add queue for totem
33
+ * route: add, join, take, queue
34
+ * yield a totem
35
+ C route: yield, finish, leave, done, complete, remove
36
+ C should also notify next person
37
+ C should not require totem name if user only has one totem
38
+ C if user has multiple totems but doesn't specify totem to yield, lita tells user which totems she has to yield (and provides commands)
39
+ * should alert people in queue that they are closer in the queue
40
+ * kick someone from totem/queue
41
+ C route: kick
42
+ C example: kick totem
43
+ C notifies user that he/she's been kicked from queue
44
+ C should alert people in queue that they are closer in the queue
45
+ * info (get all totem info, plus queues you're on as primary)
46
+ C route : info, list
47
+ C route: just "totems" or "totems info"
48
+ C if passed a totem name, gets that totem's queue
49
+ * extras:
50
+ * totem groups
51
+ * refactor:
52
+ * convert to celluloid and DAO's. Completely hide redis commands behind DAOs
53
+ * fix "user id" issue, use name instead
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1 @@
1
+ require "lita/handlers/totems"
@@ -0,0 +1,213 @@
1
+ require "lita"
2
+ require 'active_support/core_ext/integer/inflections'
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'redis-semaphore'
5
+
6
+ module Lita
7
+ module Handlers
8
+ class Totems < Handler
9
+
10
+ def self.route_regex(action_capture_group)
11
+ %r{
12
+ ^totems?\s+
13
+ (#{action_capture_group})\s+
14
+ (?<totem>\w+)
15
+ }x
16
+ end
17
+
18
+ route(route_regex("add|join|take|queue"), :add,
19
+ help: {
20
+ 'totems add TOTEM' => "Adds yourself to the TOTEM queue, or assigns yourself to the TOTEM if it's unassigned"
21
+ })
22
+
23
+ route(
24
+ %r{
25
+ ^totems?\s+
26
+ (yield|finish|leave|done|complete|remove)
27
+ (\s+(?<totem>\w+))?
28
+ }x,
29
+ :yield,
30
+ help: {
31
+ 'totems yield TOTEM' => 'Yields the TOTEM. If you are in the queue for the totem, leaves the queue.'
32
+ })
33
+
34
+ route(route_regex("create"), :create,
35
+ help: {
36
+ 'totems create TOTEM' => 'Creates a new totem TOTEM.'
37
+ })
38
+
39
+ route(route_regex("destroy|delete"), :destroy,
40
+ help: {
41
+ 'totems destroy TOTEM' => 'Destroys totem TOTEM.'
42
+ })
43
+
44
+ route(route_regex("kick"),
45
+ :kick,
46
+ help: {
47
+ 'totems kick TOTEM' => 'Kicks the user currently in possession of the TOTEM off.',
48
+ })
49
+
50
+
51
+ route(
52
+ %r{
53
+ ^totems?
54
+ (\s+info?
55
+ (\s+(?<totem>\w+))?
56
+ )?
57
+ $
58
+ }x,
59
+ :info,
60
+ help: {
61
+ 'totems info' => "Shows info of all totems queues",
62
+ 'totems info TOTEM' => 'Shows info of just one totem'
63
+ })
64
+
65
+ def destroy(response)
66
+ totem = response.match_data[:totem]
67
+ if redis.exists("totem/#{totem}")
68
+ redis.del("totem/#{totem}")
69
+ redis.del("totem/#{totem}/list")
70
+ redis.srem("totems", totem)
71
+ owning_user_id = redis.get("totem/#{totem}/owning_user_id")
72
+ redis.srem("user/#{owning_user_id}/totems", totem) if owning_user_id
73
+ response.reply(%{Destroyed totem "#{totem}".})
74
+ else
75
+ response.reply(%{Error: totem "#{totem}" doesn't exist.})
76
+ end
77
+ end
78
+
79
+ def create(response)
80
+ totem = response.match_data[:totem]
81
+
82
+ if redis.exists("totem/#{totem}")
83
+ response.reply %{Error: totem "#{totem}" already exists.}
84
+ else
85
+ redis.set("totem/#{totem}", 1)
86
+ redis.sadd("totems", totem)
87
+ response.reply %{Created totem "#{totem}".}
88
+ end
89
+
90
+ end
91
+
92
+ def add(response)
93
+ totem = response.match_data[:totem]
94
+ unless redis.exists("totem/#{totem}")
95
+ response.reply %{Error: there is no totem "#{totem}".}
96
+ return
97
+ end
98
+
99
+ user_id = response.user.id
100
+
101
+ token_acquired = false
102
+ queue_size = nil
103
+ Redis::Semaphore.new("totem/#{totem}", redis: redis).lock do
104
+ if redis.llen("totem/#{totem}/list") == 0 && redis.get("totem/#{totem}/owning_user_id").blank?
105
+ # take it:
106
+ token_acquired = true
107
+ redis.set("totem/#{totem}/owning_user_id", user_id)
108
+ else
109
+ # queue:
110
+ queue_size = redis.lpush("totem/#{totem}/list", user_id)
111
+ end
112
+ end
113
+
114
+ if token_acquired
115
+ redis.sadd("user/#{user_id}/totems", totem)
116
+ response.reply(%{#{response.user.name}, you now have totem "#{totem}".})
117
+ else
118
+ response.reply(%{#{response.user.name}, you are #{queue_size.ordinalize} in line for totem "#{totem}".})
119
+ end
120
+
121
+ end
122
+
123
+ def yield(response)
124
+ user_id = response.user.id
125
+ totems_owned_by_user = redis.smembers("user/#{user_id}/totems")
126
+ if totems_owned_by_user.empty?
127
+ response.reply "Error: You do not have any totems to yield."
128
+ elsif totems_owned_by_user.size == 1
129
+ yield_totem(totems_owned_by_user[0], user_id, response)
130
+ else # totems count > 1
131
+ totem_specified = response.match_data[:totem]
132
+ if totem_specified
133
+ if totems_owned_by_user.include?(totem_specified)
134
+ yield_totem(totem_specified, user_id, response)
135
+ else
136
+ response.reply %{Error: You don't own the "#{totem_specified}" totem.}
137
+ end
138
+ else
139
+ response.reply "You must specify a totem to yield. Totems you own: #{totems_owned_by_user.sort}"
140
+ end
141
+ end
142
+ end
143
+
144
+ def kick(response)
145
+ totem = response.match_data[:totem]
146
+ unless redis.exists("totem/#{totem}")
147
+ response.reply %{Error: there is no totem "#{totem}".}
148
+ return
149
+ end
150
+
151
+ past_owning_user_id = redis.get("totem/#{totem}/owning_user_id")
152
+ if past_owning_user_id.nil?
153
+ response.reply %{Error: Nobody owns totem "#{totem}" so you can't kick someone from it.}
154
+ return
155
+ end
156
+
157
+ redis.srem("user/#{past_owning_user_id}/totems", totem)
158
+ robot.send_messages(User.new(past_owning_user_id), %{You have been kicked from totem "#{totem}".})
159
+ next_user_id = redis.lpop("totem/#{totem}/list")
160
+ redis.set("totem/#{totem}/owning_user_id", next_user_id)
161
+ if next_user_id
162
+ redis.sadd("user/#{next_user_id}/totems", totem)
163
+ robot.send_messages(User.new(next_user_id), %{You are now in possession of totem "#{totem}".})
164
+ end
165
+
166
+ end
167
+
168
+ def info(response)
169
+ totem_param = response.match_data[:totem]
170
+ resp = if totem_param.present?
171
+ list_users_print(totem_param)
172
+ else
173
+ r = "Totems:\n"
174
+ redis.smembers("totems").each do |totem|
175
+ r += "- #{totem}\n"
176
+ r += list_users_print(totem, ' ')
177
+ end
178
+ r
179
+ end
180
+ response.reply resp
181
+ end
182
+
183
+ private
184
+ def list_users_print(totem, prefix='')
185
+ str = ''
186
+ first_id = redis.get("totem/#{totem}/owning_user_id")
187
+ if first_id
188
+ str += "#{prefix}1. User id #{first_id}\n"
189
+ rest = redis.lrange("totem/#{totem}/list", 0, -1)
190
+ rest.each_with_index do |user_id, index|
191
+ str += "#{prefix}#{index+2}. User id #{user_id}\n"
192
+ end
193
+ end
194
+ str
195
+ end
196
+
197
+ def yield_totem(totem, user_id, response)
198
+ redis.srem("user/#{user_id}/totems", totem)
199
+ next_user_id = redis.lpop("totem/#{totem}/list")
200
+ if next_user_id
201
+ redis.sadd("user/#{next_user_id}/totems", totem)
202
+ robot.send_messages(User.new(next_user_id), %{You are now in possession of totem "#{totem}."})
203
+ response.reply "You have yielded the totem to #{next_user_id}."
204
+ else
205
+ response.reply %{You have yielded the "#{totem}" totem.}
206
+ end
207
+ redis.set("totem/#{totem}/owning_user_id", next_user_id)
208
+ end
209
+ end
210
+
211
+ Lita.register_handler(Totems)
212
+ end
213
+ end
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "lita-totems"
3
+ spec.version = "0.1.0"
4
+ spec.authors = ["Charles Finkel"]
5
+ spec.email = ["cf@dropbox.com"]
6
+ spec.description = %q{Totems handler for Lita)}
7
+ spec.summary = %q{Adds support to Lita for Totems}
8
+ spec.homepage = "https://github.com/charleseff/lita-totems"
9
+ spec.license = "MIT"
10
+
11
+ spec.files = `git ls-files`.split($/)
12
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
13
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
14
+ spec.require_paths = ["lib"]
15
+
16
+ spec.add_runtime_dependency "lita", "~> 2.4"
17
+ spec.add_runtime_dependency "activesupport"
18
+ spec.add_runtime_dependency "redis-semaphore"
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec", ">= 2.14"
23
+ spec.add_development_dependency "simplecov"
24
+ spec.add_development_dependency "coveralls"
25
+ end
@@ -0,0 +1,259 @@
1
+ require "spec_helper"
2
+
3
+ describe Lita::Handlers::Totems, lita_handler: true do
4
+ it { routes("totems add foo").to(:add) }
5
+ it { routes("totem add foo").to(:add) }
6
+ it { routes("totem join foo").to(:add) }
7
+ it { doesnt_route("totems add ").to(:add) }
8
+ it { doesnt_route("tote add foo").to(:add) }
9
+ it { routes("totems kick foo").to(:kick) }
10
+ it { routes("totems kick foo bob").to(:kick) }
11
+ it { routes("totems").to(:info) }
12
+ it { routes("totems info").to(:info) }
13
+ it { routes("totems info chicken").to(:info) }
14
+
15
+ let(:totem_creator) { Class.new do
16
+ def initialize
17
+ @id = 0
18
+ end
19
+
20
+ def create(container)
21
+ @id += 1
22
+ name = "totem_#{@id}"
23
+ container.send_message("totems create #{name}")
24
+ name
25
+ end
26
+ end.new
27
+ }
28
+
29
+ let(:carl) { Lita::User.create(123, name: "Carl") }
30
+ let(:user_generator) { Class.new do
31
+ def initialize
32
+ @id = 0
33
+ end
34
+
35
+ def generate
36
+ @id += 1
37
+ Lita::User.create(@id, name: "person_#{@id}")
38
+ end
39
+ end.new
40
+ }
41
+ let(:another_user) { user_generator.generate }
42
+ let(:yet_another_user) { user_generator.generate }
43
+
44
+ describe "create" do
45
+ it "creates a totem" do
46
+ send_message('totems create chicken')
47
+ expect(replies.last).to eq('Created totem "chicken".')
48
+
49
+ send_message('totems create chicken')
50
+ expect(replies.last).to eq('Error: totem "chicken" already exists.')
51
+ end
52
+ end
53
+
54
+ describe "destroy" do
55
+ def send_destroy_message
56
+ send_message('totems destroy chicken')
57
+ end
58
+
59
+ context "totem is present" do
60
+ before do
61
+ send_message('totems create chicken')
62
+ end
63
+
64
+ it "kicks successfully" do
65
+ send_destroy_message
66
+ expect(replies.last).to eq('Destroyed totem "chicken".')
67
+ end
68
+
69
+ end
70
+ context "totem isn't present" do
71
+ it "kicks unsuccessfully" do
72
+ send_destroy_message
73
+ expect(replies.last).to eq(%{Error: totem "chicken" doesn't exist.})
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+
80
+ describe "add" do
81
+
82
+ context "totem exists" do
83
+ before do
84
+ send_message("totems create chicken")
85
+ end
86
+
87
+ context "when nobody is in line" do
88
+ it "gives totem to the user" do
89
+ send_message("totems add chicken", as: carl)
90
+ expect(replies.last).to eq('Carl, you now have totem "chicken".')
91
+ end
92
+ end
93
+ context "when people are in line" do
94
+ before do
95
+ send_message("totems add chicken", as: user_generator.generate)
96
+ send_message("totems add chicken", as: user_generator.generate)
97
+ end
98
+ it "adds user to the queue" do
99
+ send_message("totems add chicken", as: carl)
100
+ expect(replies.last).to eq('Carl, you are 2nd in line for totem "chicken".')
101
+ end
102
+ end
103
+ end
104
+
105
+ context "when the totem doesn't exist" do
106
+ it "lets user know" do
107
+ send_message("totems add chicken", as: carl)
108
+ expect(replies.last).to eq('Error: there is no totem "chicken".')
109
+ end
110
+
111
+ end
112
+
113
+ end
114
+
115
+ describe "yield" do
116
+ before do
117
+ send_message("totems create chicken")
118
+ end
119
+
120
+ context "when user has one totem" do
121
+ before do
122
+ send_message("totems add chicken", as: carl)
123
+ end
124
+
125
+ context "someone else is in line" do
126
+ before do
127
+ send_message("totems add chicken", as: another_user)
128
+ end
129
+ it "yields that totem, gives to the next person in line" do
130
+ expect(robot).to receive(:send_messages) do |target, message|
131
+ expect(target.id).to eq(another_user.id)
132
+ expect(message).to eq(%{You are now in possession of totem "chicken."})
133
+ end
134
+ send_message("totems yield", as: carl)
135
+ # todo: check for message to other user
136
+ expect(replies.last).to eq("You have yielded the totem to #{another_user.id}.")
137
+ end
138
+ end
139
+ context "nobody else is in line" do
140
+ it "yields the totem" do
141
+ send_message("totems yield", as: carl)
142
+ expect(replies.last).to eq(%{You have yielded the "chicken" totem.})
143
+ end
144
+ end
145
+ end
146
+ context "when user has no totems" do
147
+ it "sends an error" do
148
+ send_message("totems yield", as: carl)
149
+ expect(replies.last).to eq("Error: You do not have any totems to yield.")
150
+ end
151
+ end
152
+ context "when user has multiple totems" do
153
+ let(:other_totem) { totem_creator.create(self) }
154
+ before do
155
+ send_message("totems add chicken", as: carl)
156
+ send_message("totems add #{other_totem}", as: carl)
157
+ end
158
+ context "when specifying a totem" do
159
+ context "user doesn't have that totem" do
160
+ it "sends error message" do
161
+ send_message("totems yield duck", as: carl)
162
+ expect(replies.last).to eq(%{Error: You don't own the "duck" totem.})
163
+ end
164
+ end
165
+ context "user has that totem" do
166
+ it "yields totem" do
167
+ send_message("totems yield chicken", as: carl)
168
+ expect(replies.last).to eq(%{You have yielded the "chicken" totem.})
169
+ end
170
+
171
+ end
172
+ end
173
+ context "when not specifying a totem" do
174
+ it "sends a message about which totem it can yield" do
175
+ send_message("totems yield", as: carl)
176
+ expect(replies.last).to eq(%{You must specify a totem to yield. Totems you own: ["chicken", "#{other_totem}"]})
177
+ end
178
+ end
179
+
180
+ end
181
+
182
+ end
183
+
184
+ describe "kick" do
185
+ before do
186
+ send_message("totems create chicken")
187
+ end
188
+ context "there is a user owning the totem" do
189
+ before do
190
+ send_message("totems add chicken", as: another_user)
191
+ send_message("totems add chicken", as: carl)
192
+ end
193
+ it "should notify that user that she has been kicked" do
194
+ expect(robot).to receive(:send_messages) do |target, message|
195
+ expect(target.id).to eq(another_user.id)
196
+ expect(message).to eq(%{You have been kicked from totem "chicken".})
197
+ end
198
+ send_message("totems kick chicken")
199
+
200
+ end
201
+ it "should notify next user in line that she now has the totem" do
202
+ send_message("totems kick chicken")
203
+ expect(replies.last).to eq(%{You are now in possession of totem "chicken".})
204
+ end
205
+
206
+ end
207
+
208
+ context "nobody owns that totem" do
209
+ it "sends an error" do
210
+ send_message("totems kick chicken")
211
+ expect(replies.last).to eq(%{Error: Nobody owns totem "chicken" so you can't kick someone from it.})
212
+ end
213
+ end
214
+ end
215
+
216
+ describe "info" do
217
+ before do
218
+ send_message("totems create chicken")
219
+ send_message("totems create duck")
220
+ send_message("totems create ball")
221
+ send_message("totems add chicken", as: carl)
222
+ send_message("totems add chicken", as: another_user)
223
+ send_message("totems add duck", as: yet_another_user)
224
+ send_message("totems add duck", as: carl)
225
+
226
+ end
227
+ context "totem is passed" do
228
+ it "shows info for just that totem" do
229
+ send_message("totems info chicken")
230
+ expect(replies.last).to eq <<-END
231
+ 1. User id #{carl.id}
232
+ 2. User id #{another_user.id}
233
+ END
234
+
235
+ end
236
+ end
237
+
238
+ context "totem isn't passed" do
239
+ it "shows info for all totems" do
240
+ send_message("totems info")
241
+ expect(replies.last).to include <<-END
242
+ - chicken
243
+ 1. User id #{carl.id}
244
+ 2. User id #{another_user.id}
245
+ END
246
+ expect(replies.last).to include <<-END
247
+ - duck
248
+ 1. User id #{yet_another_user.id}
249
+ 2. User id #{carl.id}
250
+ END
251
+ expect(replies.last).to include <<-END
252
+ - ball
253
+ END
254
+ end
255
+ end
256
+
257
+ end
258
+
259
+ end
@@ -0,0 +1,10 @@
1
+ require "simplecov"
2
+ require "coveralls"
3
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ Coveralls::SimpleCov::Formatter
6
+ ]
7
+ SimpleCov.start { add_filter "/spec/" }
8
+
9
+ require "lita-totems"
10
+ require "lita/rspec"
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lita-totems
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Charles Finkel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lita
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
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'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis-semaphore
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '2.14'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '2.14'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: coveralls
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Totems handler for Lita)
126
+ email:
127
+ - cf@dropbox.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - .gitignore
133
+ - .travis.yml
134
+ - Gemfile
135
+ - LICENSE
136
+ - README.md
137
+ - Rakefile
138
+ - lib/lita-totems.rb
139
+ - lib/lita/handlers/totems.rb
140
+ - lita-totems.gemspec
141
+ - spec/lita/handlers/totems_spec.rb
142
+ - spec/spec_helper.rb
143
+ homepage: https://github.com/charleseff/lita-totems
144
+ licenses:
145
+ - MIT
146
+ metadata: {}
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - '>='
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubyforge_project:
163
+ rubygems_version: 2.0.5
164
+ signing_key:
165
+ specification_version: 4
166
+ summary: Adds support to Lita for Totems
167
+ test_files:
168
+ - spec/lita/handlers/totems_spec.rb
169
+ - spec/spec_helper.rb