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 +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
|
+
|