rack-campfire 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +221 -0
- data/lib/rack-campfire.rb +1 -0
- data/lib/rack/campfire.rb +6 -0
- data/lib/rack/campfire/campfire.rb +46 -0
- data/lib/rack/campfire/coercion.rb +33 -0
- data/lib/rack/campfire/hooks.rb +20 -0
- data/lib/rack/campfire/logging.rb +8 -0
- metadata +98 -0
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,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
|
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
|
+
|