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