rack-campfire 0.0.1

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.
data/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # Rack Campfire
2
+
3
+ Rack middleware to facilitate Campfire control via a Rack application.
4
+
5
+ ```
6
+ gem install rack-campfire
7
+ ```
8
+
9
+ # Concept
10
+ A campfire bot is responsible for listening to requests and responding
11
+ to them.
12
+
13
+ A web application is responsible for listening to requests and
14
+ responding to them.
15
+
16
+ Do you see a theme emerging?
17
+
18
+ Wouldn't it be nice if we could write a web application which is in fact
19
+ a campfire bot? That way, we could treat users' campfire messages as
20
+ requests and respond using the knowledge we've built up over our years
21
+ of web programming. That's what rack-campfire let's you do.
22
+
23
+ There's nothing stopping you running it alongside your current applications either. So it should be
24
+ trivial to plug it into your Rails applications and handle Campfire
25
+ messages in your campfire_controller, etc.
26
+
27
+ # Sinatra Example
28
+ The sinatra framwork is a very simple DSL that lets us create a
29
+ bare-bones web application. Here's how we might set up Sinatra with
30
+ rack-campfire:
31
+
32
+ ```ruby
33
+ # config.ru
34
+ require 'sinatra'
35
+ require 'rack/campfire'
36
+
37
+ get '/' do
38
+ 'Hello, webapp!'
39
+ end
40
+
41
+ get '/campfire' do
42
+ 'Hello, campfire!'
43
+ end
44
+
45
+ use Rack::Campfire, subdomain, api_key, rooms
46
+ run Sinatra::Application
47
+ ```
48
+
49
+ ```subdomain``` and ```api_key``` are strings that correspond to your
50
+ bot. ```rooms``` is a list of rooms you'd like your bot to join. It can
51
+ be a single string if you like, or nil if you'd like it to join the
52
+ first room.
53
+
54
+ After running ```rackup```, you should see 'Hello, campfire!' in one of the
55
+ rooms your campfire bot has joined, after posting any message in that
56
+ room. We can still visit the root of our
57
+ application, or /campfire and everything will behave as a standard web
58
+ application.
59
+
60
+ Perhaps we'd like /campfire to only respond to campfire messages? No
61
+ problem.
62
+
63
+ ```ruby
64
+ # config.ru
65
+ require 'sinatra'
66
+ require 'rack/campfire'
67
+
68
+ get '/campfire' do
69
+ if env['campfire.message']
70
+ 'Hello, campfire!'
71
+ else
72
+ 'Hello, webapp!'
73
+ end
74
+ end
75
+
76
+ use Rack::Campfire, subdomain, api_key, rooms
77
+ run Sinatra::Application
78
+ ```
79
+
80
+ Accessing ```env``` directly feels a bit messy though, so let's clean
81
+ things up a bit.
82
+
83
+ ```ruby
84
+ # config.ru
85
+ require 'sinatra'
86
+ require 'rack/campfire'
87
+
88
+ class MyApp < Sinatra::Application
89
+ include Rack::Campfire::Hooks
90
+
91
+ get '/campfire' do
92
+ if message
93
+ 'Hello, campfire!'
94
+ else
95
+ 'Hello, webapp!'
96
+ end
97
+ end
98
+ end
99
+
100
+ use Rack::Campfire, subdomain, api_key, rooms
101
+ run MyApp
102
+ ```
103
+
104
+ As you can see, including the campfire hooks module in our application
105
+ let's us access message directly. If we visit our app through a browser,
106
+ message will be nil. So what is a message? Let's take a
107
+ look.
108
+
109
+ ```
110
+ #<Hashie::Mash body="This is a test." created_at=Sat Jul 28 20:13:10 +0100 2012 id=630340875 room_id=439640 starred=false type="TextMessage" user=#<Hashie::Mash admin=false avatar_url="http://asset0.37img.com/global/missing/avatar.gif?r=3" created_at=Fri Sep 16 11:13:41 +0100 2011 email_address="chris.patuzzo@gmail.com" id=1005527 name="Chris Patuzzo" type="Member">>
111
+ ```
112
+
113
+ This let's us call ```message.body``` to get the textual content, or
114
+ ```message.user.email_address``` to get the email of the user who posted
115
+ the message, etc.
116
+
117
+ So it becomes trivial to write a bot that simply echoes messages back.
118
+
119
+ ```ruby
120
+ get '/campfire' do
121
+ message.body
122
+ end
123
+ ```
124
+
125
+ What's really cool is when the two worlds collide. Let's create an app
126
+ that let's us hit /campfire?message=Hello in our browser and see that piped
127
+ through to our campfire rooms.
128
+
129
+ ```ruby
130
+ get '/campfire' do
131
+ rooms.each { |r| r.speak params[:message] }
132
+ 'Message sent.'
133
+ end
134
+ ```
135
+
136
+ Where did rooms come from? It's just env['campfire.rooms'] and is given
137
+ to us by Hooks. So what else is available? ```room``` is available if
138
+ you're responding to a campfire message. It is the room that the message
139
+ was posted in. So we could do something like this, if we really wanted.
140
+
141
+ ```ruby
142
+ get '/campfire' do
143
+ room.speak 'Hello'
144
+ 'world'
145
+ end
146
+ ```
147
+
148
+ Posting a message to the campfire room should prompt the bot to reply
149
+ 'Hello' and 'world' on separate lines.
150
+
151
+ The only other things available is 'tinder'. This is an
152
+ instance of [Tinder](https://github.com/collectiveidea/tinder/). I'd
153
+ recommend looking at their documentation if you'd like to learn how your
154
+ bot could paste snippets of code, upload files, etc.
155
+
156
+ # Rails
157
+
158
+ Just like Sinatra- Rails sits on top of Rack. This means that we can use
159
+ rack-campfire inside our Rails applications. Here's how.
160
+
161
+ Add rack-campfire to your Gemfile.
162
+
163
+ ```ruby
164
+ # Gemfile
165
+ gem 'rack-campfire'
166
+ ```
167
+
168
+ Add rack-campfire to the middleware stack.
169
+
170
+ ```ruby
171
+ # config/application.rb
172
+ config.middleware.use Rack::Campfire, subdomain, api_key, rooms
173
+ ```
174
+
175
+ Add a route for campfire.
176
+
177
+ ```ruby
178
+ # config/routes.rb
179
+ match '/campfire' => 'campfire#campfire'
180
+ ```
181
+
182
+ And finally, create your controller. You can include the hooks here too
183
+ if you like.
184
+
185
+ ```ruby
186
+ class CampfireController < ApplicationController
187
+ include Rack::Campfire::Hooks
188
+
189
+ def campfire
190
+ render :text => "Responding to #{message.body}"
191
+ end
192
+ end
193
+ ```
194
+
195
+ Remember that you can make campfire calls elsewhere too. You just won't
196
+ have a handle on message or room.
197
+
198
+ ```ruby
199
+ class UsersController < ApplicationController
200
+ include Rack::Campfire::Hooks
201
+
202
+ def create
203
+ # ...
204
+ rooms.first.speak "#{params[:user][:name] just signed up!"
205
+ end
206
+ end
207
+ ```
208
+
209
+ If you're doing this frequently, I'd recommend including the hooks in
210
+ ApplicationController.
211
+
212
+ Note: You may run into problems if Rack::Campfire is not in the final
213
+ position on the middleware stack. You can test this by running rake
214
+ middleware. You can use ```config.middleware.insert_after``` in these
215
+ cases.
216
+
217
+ # Contribution
218
+
219
+ Feel free to contribute. No commit is too small.
220
+
221
+ [@cpatuzzo](https://twitter.com/cpatuzzo)
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'rack', 'campfire')
@@ -0,0 +1,6 @@
1
+ require 'rack'
2
+ require 'tinder'
3
+
4
+ %w[logging coercion campfire hooks].each do |file|
5
+ require File.join(File.dirname(__FILE__), 'campfire', file)
6
+ end
@@ -0,0 +1,46 @@
1
+ class Rack::Campfire
2
+ include Logging, Coercion
3
+
4
+ def initialize(app, subdomain, api_key, rooms = nil)
5
+ @app = app
6
+ @campfire = campfire(subdomain, api_key)
7
+ @rooms = coerce_rooms(rooms)
8
+ listen
9
+ end
10
+
11
+ def call(env)
12
+ @app.call(env.merge(params))
13
+ end
14
+
15
+ private
16
+ def campfire(subdomain, api_key)
17
+ Tinder::Campfire.new(subdomain, :token => api_key)
18
+ end
19
+
20
+ def listen
21
+ Thread.abort_on_exception = true
22
+ @rooms.each { |r| Thread.new { r.listen { |m| respond(m, r) } } }
23
+ end
24
+
25
+ def respond(message, room)
26
+ log(message)
27
+ return if originated_from_me?(message)
28
+ env = mock_environment.merge('campfire.message' => message)
29
+ response = coerce_body(@app.call(env).last)
30
+ room.speak response unless response.empty?
31
+ end
32
+
33
+ def params
34
+ { 'campfire' => @campfire, 'campfire.rooms' => @rooms }
35
+ end
36
+
37
+ def mock_environment
38
+ Rack::MockRequest.env_for('/campfire')
39
+ end
40
+
41
+ def originated_from_me?(message)
42
+ return unless message.user
43
+ @me ||= @campfire.me.email_address
44
+ message.user.email_address == @me
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ class Rack::Campfire
2
+ module Coercion
3
+ def coerce_rooms(rooms)
4
+ case rooms
5
+ when :all
6
+ @campfire.rooms
7
+ when nil
8
+ [@campfire.rooms.first]
9
+ when Integer
10
+ [@campfire.find_room_by_id(rooms)]
11
+ when String
12
+ [@campfire.find_room_by_name(rooms)]
13
+ when Array
14
+ rooms.inject([]) do |array, room|
15
+ case room
16
+ when Integer
17
+ array += @campfire.find_room_by_id(room)
18
+ when String
19
+ array += @campfire.find_room_by_name(room)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def coerce_body(body)
26
+ if body.respond_to? :body
27
+ body.body
28
+ else
29
+ [body].join("\n")
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ class Rack::Campfire
2
+ module Hooks
3
+ def tinder
4
+ env['campfire']
5
+ end
6
+
7
+ def rooms
8
+ env['campfire.rooms']
9
+ end
10
+
11
+ def room
12
+ return unless message
13
+ rooms.detect { |r| r.room_id == message.room_id }
14
+ end
15
+
16
+ def message
17
+ env['campfire.message']
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ class Rack::Campfire
2
+ module Logging
3
+ def log(message)
4
+ return unless message.user
5
+ puts "[rack-campfire] #{message.user.name}: #{message.body}"
6
+ end
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-campfire
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Christopher Patuzzo
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-07-28 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rack
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: tinder
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ description: Rack middleware to facilitate Campfire control via a Rack application
49
+ email: chris.patuzzo@gmail.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files: []
55
+
56
+ files:
57
+ - README.md
58
+ - lib/rack/campfire/campfire.rb
59
+ - lib/rack/campfire/coercion.rb
60
+ - lib/rack/campfire/hooks.rb
61
+ - lib/rack/campfire/logging.rb
62
+ - lib/rack/campfire.rb
63
+ - lib/rack-campfire.rb
64
+ homepage: https://github.com/cpatuzzo/rack-campfire
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options: []
69
+
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project:
93
+ rubygems_version: 1.8.22
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Rack Campfire
97
+ test_files: []
98
+