sinbook 0.1.5
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 +107 -0
- data/examples/connect.rb +51 -0
- data/examples/simple.rb +60 -0
- data/lib/sinbook.rb +355 -0
- data/sinbook.gemspec +24 -0
- metadata +68 -0
data/README
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
sinbook: simple sinatra facebook extension in 300 lines of ruby
|
2
|
+
(c) 2009 Aman Gupta (tmm1)
|
3
|
+
|
4
|
+
=== Usage
|
5
|
+
|
6
|
+
require 'sinbook'
|
7
|
+
require 'sinatra'
|
8
|
+
|
9
|
+
facebook do
|
10
|
+
api_key '4579...cbb0'
|
11
|
+
secret '5106...2342'
|
12
|
+
app_id 81747826609
|
13
|
+
url 'http://apps.facebook.com/myappname'
|
14
|
+
callback 'http://myappserver.com'
|
15
|
+
end
|
16
|
+
|
17
|
+
get '/' do
|
18
|
+
fb.require_login!
|
19
|
+
"Hi <fb:name uid=#{fb[:user]} useyou=false />!"
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
=== Features
|
24
|
+
|
25
|
+
sinbook provides a simple `facebook` helper (also aliased to `fb`).
|
26
|
+
|
27
|
+
>> fb.valid?
|
28
|
+
=> true # valid (authenticated) request from facebook's servers
|
29
|
+
|
30
|
+
>> pp fb.params
|
31
|
+
{
|
32
|
+
:logged_out_facebook => false, # request came from a user logged into facebook
|
33
|
+
:added => true, # user is logged into our app
|
34
|
+
:user => 1234, # user's facebook uid
|
35
|
+
:friends => [1,2,3], # list of user's friends
|
36
|
+
...
|
37
|
+
}
|
38
|
+
|
39
|
+
>> fb[:user] # [] is aliased to params[]
|
40
|
+
=> 1234
|
41
|
+
|
42
|
+
>> fb.callback
|
43
|
+
=> 'http://apps.facebook.com/myappname'
|
44
|
+
|
45
|
+
>> fb.callback('/homepage')
|
46
|
+
=> 'http://apps.facebook.com/myappname/homepage'
|
47
|
+
|
48
|
+
>> fb.url('/images/static.gif')
|
49
|
+
=> 'http://myappserver.com/images/static.gif'
|
50
|
+
|
51
|
+
>> fb.appurl
|
52
|
+
=> 'http://apps.facebook.com/add.php?api_key=4579...cbb0'
|
53
|
+
|
54
|
+
>> fb.addurl
|
55
|
+
=> 'http://www.facebook.com/apps/application.php?id=81747826609'
|
56
|
+
|
57
|
+
>> fb.redirect('/welcome') # redirect using an fb:redirect tag
|
58
|
+
|
59
|
+
>> fb.require_login! # redirect to addurl page unless logged in
|
60
|
+
|
61
|
+
|
62
|
+
The helper can also be used to make API calls
|
63
|
+
|
64
|
+
>> fb.users.getInfo :uid => 1234, :fields => [:name]
|
65
|
+
=> [{'uid' => 1234, 'name' => 'Frank Sinatra'}]
|
66
|
+
|
67
|
+
>> fb.groups.get :uid => 1234
|
68
|
+
=> [{'name' => 'Sinatra Users'}]
|
69
|
+
|
70
|
+
>> fb.profile.setFBML :profile => 'hello world'
|
71
|
+
=> true
|
72
|
+
|
73
|
+
>> fb.profile.getFBML
|
74
|
+
=> 'hello world'
|
75
|
+
|
76
|
+
|
77
|
+
=== Local Development
|
78
|
+
|
79
|
+
To develop locally, use ssh to setup a reverse tunnel to your external server.
|
80
|
+
For example, set your callback url to http://myserver.com:4567/, and run:
|
81
|
+
|
82
|
+
ssh -gNR 4567:localhost:4567 me@myserver.com
|
83
|
+
|
84
|
+
Then, simply launch sinatra on your local machine. Facebook will make requests to
|
85
|
+
http://myserver.com:4567/ which will be forwarded to port 4567 on your local machine.
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
=== Other Options
|
90
|
+
|
91
|
+
facebook do
|
92
|
+
symbolize_keys true
|
93
|
+
end
|
94
|
+
|
95
|
+
>> fb.groups.get :uid => 1234
|
96
|
+
=> [{:name => 'Sinatra Users'}]
|
97
|
+
|
98
|
+
|
99
|
+
=== TODO
|
100
|
+
|
101
|
+
* Split out facebook api client so it can be used outside sinatra
|
102
|
+
* Add a batch mode for api calls:
|
103
|
+
|
104
|
+
groups, pics = fb.batch do |b|
|
105
|
+
b.groups.get :uid => 123
|
106
|
+
b.users.getInfo :uids => 123, :fields => [:pic_square]
|
107
|
+
end
|
data/examples/connect.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sinbook'
|
3
|
+
require 'sinatra'
|
4
|
+
|
5
|
+
facebook do
|
6
|
+
api_key '858593842e5a3cefe59b72ddc7ffdd56'
|
7
|
+
secret '56ce1b26bf48ac8bac927c7d280b18f8'
|
8
|
+
app_id 185945096655
|
9
|
+
url 'http://tmm1.net:4568/'
|
10
|
+
callback 'http://tmm1.net:4568/'
|
11
|
+
end
|
12
|
+
|
13
|
+
set :port, 4568
|
14
|
+
|
15
|
+
get '/' do
|
16
|
+
haml :main
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/receiver' do
|
20
|
+
%[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
21
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
22
|
+
<html xmlns="http://www.w3.org/1999/xhtml" >
|
23
|
+
<body>
|
24
|
+
<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js" type="text/javascript"></script>
|
25
|
+
</body>
|
26
|
+
</html>]
|
27
|
+
end
|
28
|
+
|
29
|
+
__END__
|
30
|
+
|
31
|
+
@@ layout
|
32
|
+
%html{:xmlns=>"http://www.w3.org/1999/xhtml", :'xmlns:fb'=>"http://www.facebook.com/2008/fbml"}
|
33
|
+
%head
|
34
|
+
%title Welcome to my Facebook Connect website!
|
35
|
+
%script{:type => 'text/javascript', :src => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US'}
|
36
|
+
%body
|
37
|
+
= yield
|
38
|
+
:javascript
|
39
|
+
FB.init("#{fb.api_key}", "/receiver")
|
40
|
+
|
41
|
+
@@ main
|
42
|
+
- if fb.valid? and fb[:user]
|
43
|
+
Hi,
|
44
|
+
%fb:profile-pic{:uid => fb[:user]}
|
45
|
+
%fb:name{:uid => fb[:user], :useyou => 'false', :firstnameonly => 'true'}
|
46
|
+
!
|
47
|
+
%br/
|
48
|
+
Do you want to <a href="javascript:FB.Connect.logoutAndRedirect('/')">logout</a>?
|
49
|
+
- else
|
50
|
+
Please login:
|
51
|
+
%fb:login-button{:onlogin => 'document.location.reload(true)'}
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sinbook'
|
3
|
+
require 'sinatra'
|
4
|
+
|
5
|
+
facebook do
|
6
|
+
api_key '45796747415d12227f52146b4444cbb0'
|
7
|
+
secret '5106c7409f18d7618dd03433a2f72342'
|
8
|
+
app_id 81747826609
|
9
|
+
url 'http://apps.facebook.com/sinatrafacebook'
|
10
|
+
callback 'http://tmm1.net:4567'
|
11
|
+
end
|
12
|
+
|
13
|
+
get '/' do
|
14
|
+
if not facebook.valid?
|
15
|
+
# not accessed via facebook, redirect to the facebook app url
|
16
|
+
redirect fb.url
|
17
|
+
|
18
|
+
elsif fb[:logged_out_facebook]
|
19
|
+
# user is not logged into facebook
|
20
|
+
"Hey there! This is an awesome facebook app, but you must login to facebook to see it."
|
21
|
+
|
22
|
+
elsif not fb[:added]
|
23
|
+
# user is logged into facebook, but not our app
|
24
|
+
|
25
|
+
if not fb[:canvas_user]
|
26
|
+
# user navigated to the app directly, we know nothing about them
|
27
|
+
"
|
28
|
+
Hey there, you should add this <b>awesome</b> app! <br>
|
29
|
+
Go <a href='#{fb.addurl}'>here</a> or just click <a href='#' requirelogin=true>here</a>! <br>
|
30
|
+
Or, if you don't want to add the app, click <a href='#{fb.url('/')}'>here</a> so I know who you are.
|
31
|
+
"
|
32
|
+
|
33
|
+
else
|
34
|
+
# user came via a feed/notification or clicked on a link in the app, so we know who they are
|
35
|
+
"
|
36
|
+
Hey <fb:name uid=#{fb[:canvas_user]} useyou=false />. <br>
|
37
|
+
All I know about you is that you have #{fb[:friends].size} friends. <br>
|
38
|
+
Maybe you'll <a href='#{fb.addurl}'>add this app</a> so I can tell you more?
|
39
|
+
"
|
40
|
+
end
|
41
|
+
|
42
|
+
elsif fb[:user]
|
43
|
+
# logged into facebook and our app, we can get all their info
|
44
|
+
"
|
45
|
+
Hey <fb:name uid=#{fb[:user]} useyou=false />! <br>
|
46
|
+
Check out the special <a href='#{fb.url('/members')}'>members-only area</a>. <br>
|
47
|
+
And don't forget to tell your #{fb[:friends].size} friends to add this app too!
|
48
|
+
"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
get '/members' do
|
53
|
+
fb.require_login!
|
54
|
+
|
55
|
+
groups = fb.groups.get :uid => fb[:user]
|
56
|
+
"
|
57
|
+
Hey there, now that you're a member I can tell what groups you're in on Facebook: <br>
|
58
|
+
#{groups.map{|g| g['name'] }.join('<br>')}
|
59
|
+
"
|
60
|
+
end
|
data/lib/sinbook.rb
ADDED
@@ -0,0 +1,355 @@
|
|
1
|
+
begin
|
2
|
+
require 'sinatra/base'
|
3
|
+
rescue LoadError
|
4
|
+
retry if require 'rubygems'
|
5
|
+
raise
|
6
|
+
end
|
7
|
+
|
8
|
+
module Sinatra
|
9
|
+
require 'digest/md5'
|
10
|
+
require 'yajl'
|
11
|
+
|
12
|
+
class FacebookObject
|
13
|
+
def initialize app
|
14
|
+
@app = app
|
15
|
+
|
16
|
+
@api_key = app.options.facebook_api_key
|
17
|
+
@secret = app.options.facebook_secret
|
18
|
+
@app_id = app.options.facebook_app_id
|
19
|
+
@url = app.options.facebook_url
|
20
|
+
@callback = app.options.facebook_callback
|
21
|
+
@symbolize_keys = app.options.facebook_symbolize_keys || false
|
22
|
+
end
|
23
|
+
attr_reader :app
|
24
|
+
attr_accessor :api_key, :secret
|
25
|
+
attr_writer :url, :callback, :app_id
|
26
|
+
|
27
|
+
def app_id
|
28
|
+
@app_id || self[:app_id]
|
29
|
+
end
|
30
|
+
|
31
|
+
def url postfix=nil
|
32
|
+
postfix ? "#{@url}#{postfix}" : @url
|
33
|
+
end
|
34
|
+
|
35
|
+
def callback postfix=nil
|
36
|
+
postfix ? "#{@callback}#{postfix}" : @callback
|
37
|
+
end
|
38
|
+
|
39
|
+
def addurl
|
40
|
+
"http://apps.facebook.com/add.php?api_key=#{self.api_key}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def appurl
|
44
|
+
"http://www.facebook.com/apps/application.php?id=#{self.app_id}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def require_login!
|
48
|
+
if valid?
|
49
|
+
redirect addurl unless params[:user]
|
50
|
+
else
|
51
|
+
app.redirect url
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def redirect url
|
56
|
+
url = self.url + url unless url =~ /^http/
|
57
|
+
app.body "<fb:redirect url='#{url}'/>"
|
58
|
+
throw :halt
|
59
|
+
end
|
60
|
+
|
61
|
+
def params
|
62
|
+
return {} unless valid?
|
63
|
+
app.env['facebook.params'] ||= \
|
64
|
+
app.env['facebook.vars'].inject({}) do |h,(k,v)|
|
65
|
+
s = k.to_sym
|
66
|
+
case k
|
67
|
+
when 'friends'
|
68
|
+
h[s] = v.split(',').map{|e|e.to_i}
|
69
|
+
when /time$/
|
70
|
+
h[s] = Time.at(v.to_f)
|
71
|
+
when 'expires'
|
72
|
+
v = v.to_i
|
73
|
+
h[s] = v>0 ? Time.at(v) : v
|
74
|
+
when 'user', 'app_id', 'canvas_user'
|
75
|
+
h[s] = v.to_i
|
76
|
+
when /^(logged_out|position_|in_|is_|added)/
|
77
|
+
h[s] = v=='1'
|
78
|
+
else
|
79
|
+
h[s] = v
|
80
|
+
end
|
81
|
+
h
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def [] key
|
86
|
+
params[key]
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid?
|
90
|
+
if app.params['fb_sig'] # canvas/iframe mode
|
91
|
+
prefix = 'fb_sig'
|
92
|
+
vars = app.params
|
93
|
+
elsif app.request.cookies[api_key] # fbconnect mode
|
94
|
+
prefix = api_key
|
95
|
+
vars = app.request.cookies
|
96
|
+
else
|
97
|
+
return false
|
98
|
+
end
|
99
|
+
|
100
|
+
if app.env['facebook.valid?'].nil?
|
101
|
+
fbvars = {}
|
102
|
+
sig = Digest::MD5.hexdigest(vars.map{|k,v|
|
103
|
+
if k =~ /^#{prefix}_(.+)$/
|
104
|
+
fbvars[$1] = v
|
105
|
+
"#{$1}=#{v}"
|
106
|
+
end
|
107
|
+
}.compact.sort.join+self.secret)
|
108
|
+
|
109
|
+
if app.env['facebook.valid?'] = (vars[prefix] == sig)
|
110
|
+
app.env['facebook.vars'] = fbvars
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
app.env['facebook.valid?']
|
115
|
+
end
|
116
|
+
|
117
|
+
class APIProxy
|
118
|
+
Types = %w[
|
119
|
+
admin
|
120
|
+
application
|
121
|
+
auth
|
122
|
+
batch
|
123
|
+
comments
|
124
|
+
connect
|
125
|
+
data
|
126
|
+
events
|
127
|
+
fbml
|
128
|
+
feed
|
129
|
+
fql
|
130
|
+
friends
|
131
|
+
groups
|
132
|
+
links
|
133
|
+
liveMessage
|
134
|
+
notes
|
135
|
+
notifications
|
136
|
+
pages
|
137
|
+
photos
|
138
|
+
profile
|
139
|
+
sms
|
140
|
+
status
|
141
|
+
stream
|
142
|
+
users
|
143
|
+
video
|
144
|
+
]
|
145
|
+
|
146
|
+
alias :__class__ :class
|
147
|
+
alias :__inspect__ :inspect
|
148
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
|
149
|
+
alias :inspect :__inspect__
|
150
|
+
|
151
|
+
def initialize name, obj
|
152
|
+
@name, @obj = name, obj
|
153
|
+
end
|
154
|
+
|
155
|
+
def method_missing method, opts = {}
|
156
|
+
@obj.request "#{@name}.#{method}", opts
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
APIProxy::Types.each do |n|
|
161
|
+
class_eval %[
|
162
|
+
def #{n}
|
163
|
+
(@proxies||={})[:#{n}] ||= APIProxy.new(:#{n}, self)
|
164
|
+
end
|
165
|
+
]
|
166
|
+
end
|
167
|
+
|
168
|
+
def request method, opts = {}
|
169
|
+
if method == 'photos.upload'
|
170
|
+
image = opts.delete :image
|
171
|
+
end
|
172
|
+
|
173
|
+
opts = { :api_key => self.api_key,
|
174
|
+
:call_id => Time.now.to_f,
|
175
|
+
:format => 'JSON',
|
176
|
+
:v => '1.0',
|
177
|
+
:session_key => %w[ photos.upload ].include?(method) ? nil : params[:session_key],
|
178
|
+
:method => method }.merge(opts)
|
179
|
+
|
180
|
+
args = opts.map{ |k,v|
|
181
|
+
next nil unless v
|
182
|
+
|
183
|
+
"#{k}=" + case v
|
184
|
+
when Hash
|
185
|
+
Yajl::Encoder.encode(v)
|
186
|
+
when Array
|
187
|
+
if k == :tags
|
188
|
+
Yajl::Encoder.encode(v)
|
189
|
+
else
|
190
|
+
v.join(',')
|
191
|
+
end
|
192
|
+
else
|
193
|
+
v.to_s
|
194
|
+
end
|
195
|
+
}.compact.sort
|
196
|
+
|
197
|
+
sig = Digest::MD5.hexdigest(args.join+self.secret)
|
198
|
+
|
199
|
+
if method == 'photos.upload'
|
200
|
+
data = MimeBoundary
|
201
|
+
data += opts.merge(:sig => sig).inject('') do |buf, (key, val)|
|
202
|
+
if val
|
203
|
+
buf << (MimePart % [key, val])
|
204
|
+
else
|
205
|
+
buf
|
206
|
+
end
|
207
|
+
end
|
208
|
+
data += MimeImage % ['upload.jpg', 'jpg', image.respond_to?(:read) ? image.read : image]
|
209
|
+
else
|
210
|
+
data = Array["sig=#{sig}", *args.map{|a| a.gsub('&','%26') }].join('&')
|
211
|
+
end
|
212
|
+
|
213
|
+
ret = self.class.request(data, method == 'photos.upload')
|
214
|
+
|
215
|
+
ret = if ['true', '1'].include? ret
|
216
|
+
true
|
217
|
+
elsif ['false', '0'].include? ret
|
218
|
+
false
|
219
|
+
elsif (n = Integer(ret) rescue nil)
|
220
|
+
n
|
221
|
+
else
|
222
|
+
Yajl::Parser.parse(ret, :symbolize_keys => @symbolize_keys)
|
223
|
+
end
|
224
|
+
|
225
|
+
raise Facebook::Error, ret['error_msg'] if ret.is_a? Hash and ret['error_code']
|
226
|
+
|
227
|
+
ret
|
228
|
+
end
|
229
|
+
|
230
|
+
MimeBoundary = "--SoMeTeXtWeWiLlNeVeRsEe\r\n"
|
231
|
+
MimePart = %[Content-Disposition: form-data; name="%s"\r\n\r\n%s\r\n] + MimeBoundary
|
232
|
+
MimeImage = %[Content-Disposition: form-data; filename="%s"\r\nContent-Type: image/%s\r\n\r\n%s\r\n] + MimeBoundary
|
233
|
+
|
234
|
+
require 'resolv'
|
235
|
+
API_SERVER = Resolv.getaddress('api.facebook.com')
|
236
|
+
@keepalive = false
|
237
|
+
|
238
|
+
def self.connect
|
239
|
+
sock = TCPSocket.new(API_SERVER, 80)
|
240
|
+
begin
|
241
|
+
timeout = [3,0].pack('l_2') # 3 seconds
|
242
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeout
|
243
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, timeout
|
244
|
+
rescue Exception => ex
|
245
|
+
# causes issues on solaris?
|
246
|
+
puts ex.inspect
|
247
|
+
end
|
248
|
+
sock
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.request data, mime=false
|
252
|
+
if @keepalive
|
253
|
+
@socket ||= connect
|
254
|
+
else
|
255
|
+
@socket = connect
|
256
|
+
end
|
257
|
+
|
258
|
+
@socket.print "POST /restserver.php HTTP/1.1\r\n"
|
259
|
+
@socket.print "Host: api.facebook.com\r\n"
|
260
|
+
@socket.print "Connection: keep-alive\r\n" if @keepalive
|
261
|
+
if mime
|
262
|
+
@socket.print "Content-Type: multipart/form-data; boundary=#{MimeBoundary[2..-3]}\r\n"
|
263
|
+
@socket.print "MIME-version: 1.0\r\n"
|
264
|
+
else
|
265
|
+
@socket.print "Content-Type: application/x-www-form-urlencoded\r\n"
|
266
|
+
end
|
267
|
+
@socket.print "Content-Length: #{data.length}\r\n"
|
268
|
+
@socket.print "\r\n#{data}\r\n"
|
269
|
+
@socket.print "\r\n\r\n"
|
270
|
+
|
271
|
+
buf = ''
|
272
|
+
headers = ''
|
273
|
+
headers_done = false
|
274
|
+
chunked = true
|
275
|
+
|
276
|
+
while true
|
277
|
+
line = @socket.gets
|
278
|
+
headers << line unless headers_done
|
279
|
+
raise Errno::ECONNRESET unless line
|
280
|
+
|
281
|
+
if line == "\r\n" # end of headers/chunk
|
282
|
+
unless headers_done
|
283
|
+
headers_done = true
|
284
|
+
if headers =~ /Encoding: chunked/i
|
285
|
+
chunked = true
|
286
|
+
else
|
287
|
+
len = headers[/Content-Length: (\d+)/i,1].to_i
|
288
|
+
buf = @socket.read(len)
|
289
|
+
break # done!
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
line = @socket.gets # get size of next chunk
|
294
|
+
if line.strip! == '0' # 0 sized chunk
|
295
|
+
@socket.gets # read last crlf
|
296
|
+
break # done!
|
297
|
+
end
|
298
|
+
|
299
|
+
buf << @socket.read(line.to_i(16)) # read in chunk
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
buf
|
304
|
+
rescue Errno::EPIPE, Errno::ECONNRESET
|
305
|
+
@socket = nil
|
306
|
+
retry
|
307
|
+
ensure
|
308
|
+
@socket.close if @socket and !@keepalive
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
module FacebookHelper
|
313
|
+
def facebook
|
314
|
+
env['facebook.helper'] ||= FacebookObject.new(self)
|
315
|
+
end
|
316
|
+
alias fb facebook
|
317
|
+
end
|
318
|
+
|
319
|
+
class FacebookSettings
|
320
|
+
def initialize app, &blk
|
321
|
+
@app = app
|
322
|
+
@app.set :facebook_symbolize_keys, false
|
323
|
+
instance_eval &blk
|
324
|
+
end
|
325
|
+
%w[ api_key secret app_id url callback symbolize_keys ].each do |param|
|
326
|
+
class_eval %[
|
327
|
+
def #{param} val, &blk
|
328
|
+
@app.set :facebook_#{param}, val
|
329
|
+
end
|
330
|
+
]
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
module Facebook
|
335
|
+
class Error < StandardError; end
|
336
|
+
|
337
|
+
def facebook &blk
|
338
|
+
FacebookSettings.new(self, &blk)
|
339
|
+
end
|
340
|
+
|
341
|
+
def self.registered app
|
342
|
+
app.helpers FacebookHelper
|
343
|
+
app.before(&method(:fix_request_method))
|
344
|
+
app.disable :sessions
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.fix_request_method app
|
348
|
+
if method = app.request.params['fb_sig_request_method']
|
349
|
+
app.request.env['REQUEST_METHOD'] = method
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
Application.register Facebook
|
355
|
+
end
|
data/sinbook.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
2
|
+
s.name = 'sinbook'
|
3
|
+
s.version = '0.1.5'
|
4
|
+
s.date = '2009-11-25'
|
5
|
+
s.summary = 'simple sinatra facebook extension in 300 lines of ruby'
|
6
|
+
s.description = 'A full-featured facebook extension for the sinatra webapp framework'
|
7
|
+
|
8
|
+
s.homepage = "http://github.com/tmm1/sinbook"
|
9
|
+
|
10
|
+
s.authors = ["Aman Gupta"]
|
11
|
+
s.email = "aman@tmm1.net"
|
12
|
+
|
13
|
+
s.add_dependency('yajl-ruby')
|
14
|
+
s.has_rdoc = false
|
15
|
+
|
16
|
+
# ruby -rpp -e' pp `git ls-files | grep -v examples`.split("\n") '
|
17
|
+
s.files = [
|
18
|
+
"README",
|
19
|
+
"sinbook.gemspec",
|
20
|
+
"lib/sinbook.rb",
|
21
|
+
"examples/simple.rb",
|
22
|
+
"examples/connect.rb"
|
23
|
+
]
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinbook
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aman Gupta
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-25 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: yajl-ruby
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: A full-featured facebook extension for the sinatra webapp framework
|
26
|
+
email: aman@tmm1.net
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- README
|
35
|
+
- sinbook.gemspec
|
36
|
+
- lib/sinbook.rb
|
37
|
+
- examples/simple.rb
|
38
|
+
- examples/connect.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/tmm1/sinbook
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.4
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: simple sinatra facebook extension in 300 lines of ruby
|
67
|
+
test_files: []
|
68
|
+
|