tenderlove-facebooker 1.0.16.20090319151701

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.
Files changed (124) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/COPYING.rdoc +19 -0
  3. data/Manifest.txt +123 -0
  4. data/README.rdoc +103 -0
  5. data/Rakefile +86 -0
  6. data/TODO.rdoc +4 -0
  7. data/examples/desktop_login.rb +14 -0
  8. data/facebooker.gemspec +38 -0
  9. data/generators/facebook/facebook_generator.rb +14 -0
  10. data/generators/facebook/templates/config/facebooker.yml +46 -0
  11. data/generators/facebook/templates/public/javascripts/facebooker.js +99 -0
  12. data/generators/facebook_controller/USAGE +33 -0
  13. data/generators/facebook_controller/facebook_controller_generator.rb +40 -0
  14. data/generators/facebook_controller/templates/controller.rb +7 -0
  15. data/generators/facebook_controller/templates/functional_test.rb +11 -0
  16. data/generators/facebook_controller/templates/helper.rb +2 -0
  17. data/generators/facebook_controller/templates/view.fbml.erb +2 -0
  18. data/generators/facebook_controller/templates/view.html.erb +2 -0
  19. data/generators/facebook_publisher/facebook_publisher_generator.rb +14 -0
  20. data/generators/facebook_publisher/templates/create_facebook_templates.rb +15 -0
  21. data/generators/facebook_publisher/templates/publisher.rb +3 -0
  22. data/generators/facebook_scaffold/USAGE +27 -0
  23. data/generators/facebook_scaffold/facebook_scaffold_generator.rb +118 -0
  24. data/generators/facebook_scaffold/templates/controller.rb +93 -0
  25. data/generators/facebook_scaffold/templates/facebook_style.css +2579 -0
  26. data/generators/facebook_scaffold/templates/functional_test.rb +89 -0
  27. data/generators/facebook_scaffold/templates/helper.rb +2 -0
  28. data/generators/facebook_scaffold/templates/layout.fbml.erb +6 -0
  29. data/generators/facebook_scaffold/templates/layout.html.erb +17 -0
  30. data/generators/facebook_scaffold/templates/style.css +74 -0
  31. data/generators/facebook_scaffold/templates/view_edit.fbml.erb +13 -0
  32. data/generators/facebook_scaffold/templates/view_edit.html.erb +18 -0
  33. data/generators/facebook_scaffold/templates/view_index.fbml.erb +24 -0
  34. data/generators/facebook_scaffold/templates/view_index.html.erb +24 -0
  35. data/generators/facebook_scaffold/templates/view_new.fbml.erb +12 -0
  36. data/generators/facebook_scaffold/templates/view_new.html.erb +17 -0
  37. data/generators/facebook_scaffold/templates/view_show.fbml.erb +10 -0
  38. data/generators/facebook_scaffold/templates/view_show.html.erb +10 -0
  39. data/generators/publisher/publisher_generator.rb +14 -0
  40. data/generators/xd_receiver/templates/xd_receiver.html +10 -0
  41. data/generators/xd_receiver/xd_receiver_generator.rb +9 -0
  42. data/init.rb +72 -0
  43. data/install.rb +12 -0
  44. data/lib/facebooker/adapters/adapter_base.rb +87 -0
  45. data/lib/facebooker/adapters/bebo_adapter.rb +75 -0
  46. data/lib/facebooker/adapters/facebook_adapter.rb +52 -0
  47. data/lib/facebooker/admin.rb +42 -0
  48. data/lib/facebooker/batch_request.rb +44 -0
  49. data/lib/facebooker/data.rb +57 -0
  50. data/lib/facebooker/feed.rb +78 -0
  51. data/lib/facebooker/logging.rb +51 -0
  52. data/lib/facebooker/mobile.rb +20 -0
  53. data/lib/facebooker/mock/service.rb +50 -0
  54. data/lib/facebooker/mock/session.rb +18 -0
  55. data/lib/facebooker/model.rb +135 -0
  56. data/lib/facebooker/models/affiliation.rb +10 -0
  57. data/lib/facebooker/models/album.rb +11 -0
  58. data/lib/facebooker/models/applicationproperties.rb +39 -0
  59. data/lib/facebooker/models/applicationrestrictions.rb +10 -0
  60. data/lib/facebooker/models/cookie.rb +10 -0
  61. data/lib/facebooker/models/education_info.rb +11 -0
  62. data/lib/facebooker/models/event.rb +28 -0
  63. data/lib/facebooker/models/friend_list.rb +16 -0
  64. data/lib/facebooker/models/group.rb +36 -0
  65. data/lib/facebooker/models/info_item.rb +10 -0
  66. data/lib/facebooker/models/info_section.rb +10 -0
  67. data/lib/facebooker/models/location.rb +8 -0
  68. data/lib/facebooker/models/notifications.rb +17 -0
  69. data/lib/facebooker/models/page.rb +27 -0
  70. data/lib/facebooker/models/photo.rb +12 -0
  71. data/lib/facebooker/models/tag.rb +12 -0
  72. data/lib/facebooker/models/user.rb +426 -0
  73. data/lib/facebooker/models/video.rb +9 -0
  74. data/lib/facebooker/models/work_info.rb +9 -0
  75. data/lib/facebooker/parser.rb +589 -0
  76. data/lib/facebooker/rails/controller.rb +304 -0
  77. data/lib/facebooker/rails/cucumber/world.rb +46 -0
  78. data/lib/facebooker/rails/cucumber.rb +28 -0
  79. data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
  80. data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
  81. data/lib/facebooker/rails/facebook_request_fix.rb +24 -0
  82. data/lib/facebooker/rails/facebook_session_handling.rb +69 -0
  83. data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
  84. data/lib/facebooker/rails/facebook_url_rewriting.rb +39 -0
  85. data/lib/facebooker/rails/helpers/fb_connect.rb +89 -0
  86. data/lib/facebooker/rails/helpers.rb +762 -0
  87. data/lib/facebooker/rails/integration_session.rb +38 -0
  88. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  89. data/lib/facebooker/rails/publisher.rb +526 -0
  90. data/lib/facebooker/rails/routing.rb +49 -0
  91. data/lib/facebooker/rails/test_helpers.rb +68 -0
  92. data/lib/facebooker/rails/utilities.rb +22 -0
  93. data/lib/facebooker/server_cache.rb +24 -0
  94. data/lib/facebooker/service.rb +94 -0
  95. data/lib/facebooker/session.rb +584 -0
  96. data/lib/facebooker/version.rb +9 -0
  97. data/lib/facebooker.rb +174 -0
  98. data/lib/net/http_multipart_post.rb +123 -0
  99. data/lib/tasks/facebooker.rake +18 -0
  100. data/lib/tasks/tunnel.rake +46 -0
  101. data/rails/init.rb +1 -0
  102. data/setup.rb +1585 -0
  103. data/templates/layout.erb +24 -0
  104. data/test/facebooker/adapters_test.rb +96 -0
  105. data/test/facebooker/admin_test.rb +102 -0
  106. data/test/facebooker/batch_request_test.rb +83 -0
  107. data/test/facebooker/data_test.rb +86 -0
  108. data/test/facebooker/logging_test.rb +43 -0
  109. data/test/facebooker/mobile_test.rb +45 -0
  110. data/test/facebooker/model_test.rb +123 -0
  111. data/test/facebooker/models/event_test.rb +15 -0
  112. data/test/facebooker/models/user_test.rb +295 -0
  113. data/test/facebooker/rails/publisher_test.rb +452 -0
  114. data/test/facebooker/rails_integration_test.rb +1312 -0
  115. data/test/facebooker/server_cache_test.rb +44 -0
  116. data/test/facebooker/session_test.rb +614 -0
  117. data/test/facebooker_test.rb +925 -0
  118. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  119. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  120. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  121. data/test/net/http_multipart_post_test.rb +52 -0
  122. data/test/rails_test_helper.rb +11 -0
  123. data/test/test_helper.rb +66 -0
  124. metadata +217 -0
@@ -0,0 +1,38 @@
1
+ require 'action_controller/integration'
2
+
3
+ class Facebooker::Rails::IntegrationSession < ActionController::Integration::Session
4
+ include Facebooker::Rails::TestHelpers
5
+ attr_accessor :default_request_params, :canvas
6
+
7
+ def process(method, path, parameters = nil, headers = nil)
8
+ if canvas
9
+ parameters = facebook_params(@default_request_params.merge(parameters || {}))
10
+ end
11
+ super method, path, parameters, headers
12
+ end
13
+
14
+ def reset!
15
+ self.default_request_params = {:fb_sig_in_canvas => '1'}.with_indifferent_access
16
+ self.canvas = true
17
+ super
18
+ end
19
+
20
+ def get(path, parameters = nil, headers = nil)
21
+ if canvas
22
+ post path, (parameters || {}).merge('fb_sig_request_method' => 'GET'), headers
23
+ else
24
+ super path, parameters, headers
25
+ end
26
+ end
27
+
28
+ %w(put delete).each do |method|
29
+ define_method method do |*args|
30
+ if canvas
31
+ path, parameters, headers = *args
32
+ post path, (parameters || {}).merge('_method' => method.upcase), headers
33
+ else
34
+ super *args
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ module Facebooker
2
+ module Rails
3
+ module ProfilePublisherExtensions
4
+
5
+ ##
6
+ # returns true if Facebook is requesting the interface for a profile publisher
7
+ def wants_interface?
8
+ params[:method] == "publisher_getInterface"
9
+ end
10
+
11
+ ##
12
+ # render the interface for a publisher.
13
+ # fbml is the content in string form. Use render_to_string to get the content from a template
14
+ # publish_enabled controlls whether the post form is active by default. If it isn't, you'll need to use fbjs to activate it
15
+ # comment_enabled controls whether to include a comment box
16
+ def render_publisher_interface(fbml,publish_enabled=true,comment_enabled=false)
17
+ render :json=>{:content=>{:fbml=>fbml,:publishEnabled=>publish_enabled,:commentEnabled=>comment_enabled},
18
+ :method=>"publisher_getInterface"}
19
+ end
20
+
21
+ # render an error while publishing the template
22
+ # This can be used for validation errors
23
+ def render_publisher_error(title,body)
24
+ render :json=>{:errorCode=>1,:errorTitle=>title,:errorMessage=>body}.to_json
25
+ end
26
+
27
+ # render the response for a feed. This takes a user_action object like those returned from the Rails Publisher
28
+ # For instance, AttackPublisher.create_attack(@attack)
29
+ # The template must have been registered previously
30
+ def render_publisher_response(user_action)
31
+ render :json=>{:content=> {
32
+ :feed=>{
33
+ :template_id=>user_action.template_id,
34
+ :template_data=>user_action.data
35
+ }
36
+ },
37
+ :method=>"publisher_getFeedStory"
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,526 @@
1
+ module Facebooker
2
+ module Rails
3
+ # ActionMailer like module for publishing Facbook messages
4
+ #
5
+ # To use, create a subclass and define methods
6
+ # Each method should start by calling send_as to specify the type of message
7
+ # Valid options are :email and :notification, :user_action, :profile, :ref
8
+ #
9
+ #
10
+ # Below is an example of each type
11
+ #
12
+ # class TestPublisher < Facebooker::Rails::Publisher
13
+ # # The new message templates are supported as well
14
+ # # First, create a method that contains your templates:
15
+ # # You may include multiple one line story templates and short story templates
16
+ # # but only one full story template
17
+ # # Your most specific template should be first
18
+ # #
19
+ # # Before using, you must register your template by calling register. For this example
20
+ # # You would call TestPublisher.register_publish_action
21
+ # # Registering the template will store the template id returned from Facebook in the
22
+ # # facebook_templates table that is created when you create your first publisher
23
+ # def publish_action_template
24
+ # one_line_story_template "{*actor*} did stuff with {*friend*}"
25
+ # one_line_story_template "{*actor*} did stuff"
26
+ # short_story_template "{*actor*} has a title {*friend*}", render(:partial=>"short_body")
27
+ # short_story_template "{*actor*} has a title", render(:partial=>"short_body")
28
+ # full_story_template "{*actor*} has a title {*friend*}", render(:partial=>"full_body")
29
+ # action_links action_link("My text {*template_var*}","{*link_url*}")
30
+ # end
31
+ #
32
+ # # To send a registered template, you need to create a method to set the data
33
+ # # The publisher will look up the template id from the facebook_templates table
34
+ # def publish_action(f)
35
+ # send_as :user_action
36
+ # from f
37
+ # story_size SHORT # or ONE_LINE or FULL
38
+ # data :friend=>"Mike"
39
+ # end
40
+ #
41
+ #
42
+ # # Provide a from user to send a general notification
43
+ # # if from is nil, this will send an announcement
44
+ # def notification(to,f)
45
+ # send_as :notification
46
+ # recipients to
47
+ # from f
48
+ # fbml "Not"
49
+ # end
50
+ #
51
+ # def email(to,f)
52
+ # send_as :email
53
+ # recipients to
54
+ # from f
55
+ # title "Email"
56
+ # fbml 'text'
57
+ # text fbml
58
+ # end
59
+ # # This will render the profile in /users/profile.erb
60
+ # # it will set @user to user_to_update in the template
61
+ # # The mobile profile will be rendered from the app/views/test_publisher/_mobile.erb
62
+ # # template
63
+ # def profile_update(user_to_update,user_with_session_to_use)
64
+ # send_as :profile
65
+ # from user_with_session_to_use
66
+ # to user_to_update
67
+ # profile render(:action=>"/users/profile",:assigns=>{:user=>user_to_update})
68
+ # profile_action "A string"
69
+ # mobile_profile render(:partial=>"mobile",:assigns=>{:user=>user_to_update})
70
+ # end
71
+ #
72
+ # # Update the given handle ref with the content from a
73
+ # # template
74
+ # def ref_update(user)
75
+ # send_as :ref
76
+ # from user
77
+ # fbml render(:action=>"/users/profile",:assigns=>{:user=>user_to_update})
78
+ # handle "a_ref_handle"
79
+ # end
80
+ #
81
+ #
82
+ # To send a message, use ActionMailer like semantics
83
+ # TestPublisher.deliver_action(@user)
84
+ #
85
+ # For testing, you may want to create an instance of the underlying message without sending it
86
+ # TestPublisher.create_action(@user)
87
+ # will create and return an instance of Facebooker::Feeds::Action
88
+ #
89
+ # Publisher makes many helpers available, including the linking and asset helpers
90
+ class Publisher
91
+
92
+ #story sizes from the Facebooker API
93
+ ONE_LINE=1
94
+ SHORT=2
95
+ FULL=4
96
+
97
+ def initialize
98
+ @controller = PublisherController.new
99
+ end
100
+
101
+ # use facebook options everywhere
102
+ def request_comes_from_facebook?
103
+ true
104
+ end
105
+
106
+ class FacebookTemplate < ::ActiveRecord::Base
107
+
108
+
109
+ cattr_accessor :template_cache
110
+ self.template_cache = {}
111
+
112
+ def self.inspect(*args)
113
+ "FacebookTemplate"
114
+ end
115
+
116
+ def template_changed?(hash)
117
+ if respond_to?(:content_hash)
118
+ content_hash != hash
119
+ else
120
+ false
121
+ end
122
+ end
123
+
124
+ class << self
125
+
126
+ def register(klass,method)
127
+ publisher = setup_publisher(klass,method)
128
+ template_id = Facebooker::Session.create.register_template_bundle(publisher.one_line_story_templates,publisher.short_story_templates,publisher.full_story_template,publisher.action_links)
129
+ template = find_or_initialize_by_template_name(template_name(klass,method))
130
+ template.bundle_id = template_id
131
+ template.content_hash = hashed_content(klass,method) if template.respond_to?(:content_hash)
132
+ template.save!
133
+ cache(klass,method,template)
134
+ template
135
+ end
136
+
137
+ def for_class_and_method(klass,method)
138
+ find_cached(klass,method)
139
+ end
140
+ def bundle_id_for_class_and_method(klass,method)
141
+ for_class_and_method(klass,method).bundle_id
142
+ end
143
+
144
+ def cache(klass,method,template)
145
+ template_cache[template_name(klass,method)] = template
146
+ end
147
+
148
+ def clear_cache!
149
+ self.template_cache = {}
150
+ end
151
+
152
+ def find_cached(klass,method)
153
+ template_cache[template_name(klass,method)] || find_in_db(klass,method)
154
+ end
155
+
156
+ def find_in_db(klass,method)
157
+ template = find_by_template_name(template_name(klass,method))
158
+ if template and template.template_changed?(hashed_content(klass,method))
159
+ template.destroy
160
+ template = nil
161
+ end
162
+
163
+ if template.nil?
164
+ template = register(klass,method)
165
+ end
166
+ template
167
+ end
168
+
169
+ def setup_publisher(klass,method)
170
+ publisher = klass.new
171
+ publisher.send method + '_template'
172
+ publisher
173
+ end
174
+
175
+ def hashed_content(klass, method)
176
+ publisher = setup_publisher(klass,method)
177
+ # sort the Hash elements (in the short_story and full_story) before generating MD5
178
+ Digest::MD5.hexdigest [publisher.one_line_story_templates,
179
+ (publisher.short_story_templates and publisher.short_story_templates.collect{|ss| ss.to_a.sort_by{|e| e[0].to_s}}),
180
+ (publisher.full_story_template and publisher.full_story_template.to_a.sort_by{|e| e[0].to_s})
181
+ ].to_json
182
+ end
183
+
184
+ def template_name(klass,method)
185
+ "#{klass.name}::#{method}"
186
+ end
187
+ end
188
+ end
189
+
190
+ class_inheritable_accessor :master_helper_module
191
+ attr_accessor :one_line_story_templates, :short_story_templates, :action_links
192
+
193
+ cattr_accessor :skip_registry
194
+ self.skip_registry = false
195
+
196
+
197
+ class InvalidSender < StandardError; end
198
+ class UnknownBodyType < StandardError; end
199
+ class UnspecifiedBodyType < StandardError; end
200
+ class Email
201
+ attr_accessor :title
202
+ attr_accessor :text
203
+ attr_accessor :fbml
204
+ end
205
+
206
+ class Notification
207
+ attr_accessor :fbml
208
+ end
209
+
210
+ class Profile
211
+ attr_accessor :profile
212
+ attr_accessor :profile_action
213
+ attr_accessor :mobile_profile
214
+ attr_accessor :profile_main
215
+ end
216
+ class Ref
217
+ attr_accessor :handle
218
+ attr_accessor :fbml
219
+ end
220
+ class UserAction
221
+ attr_accessor :data
222
+ attr_accessor :target_ids
223
+ attr_accessor :body_general
224
+ attr_accessor :template_id
225
+ attr_accessor :template_name
226
+ attr_accessor :story_size
227
+ def target_ids=(val)
228
+ @target_ids = val.is_a?(Array) ? val.join(",") : val
229
+ end
230
+ def data_hash
231
+ default_data = story_size.nil? ? {} : {:story_size=>story_size}
232
+ default_data.merge(data||{})
233
+ end
234
+ end
235
+
236
+ cattr_accessor :ignore_errors
237
+ attr_accessor :_body
238
+
239
+ def recipients(*args)
240
+ if args.size==0
241
+ @recipients
242
+ else
243
+ @recipients=args.first
244
+ end
245
+ end
246
+
247
+ def from(*args)
248
+ if args.size==0
249
+ @from
250
+ else
251
+ @from=args.first
252
+ end
253
+ end
254
+
255
+
256
+ def send_as(option)
257
+ self._body=case option
258
+ when :action
259
+ Facebooker::Feed::Action.new
260
+ when :story
261
+ Facebooker::Feed::Story.new
262
+ when :templatized_action
263
+ Facebooker::Feed::TemplatizedAction.new
264
+ when :notification
265
+ Notification.new
266
+ when :email
267
+ Email.new
268
+ when :profile
269
+ Profile.new
270
+ when :ref
271
+ Ref.new
272
+ when :user_action
273
+ UserAction.new
274
+ else
275
+ raise UnknownBodyType.new("Unknown type to publish")
276
+ end
277
+ end
278
+
279
+ def full_story_template(title=nil,body=nil,params={})
280
+ if title.nil?
281
+ @full_story_template
282
+ else
283
+ @full_story_template=params.merge(:template_title=>title, :template_body=>body)
284
+ end
285
+ end
286
+
287
+ def one_line_story_template(str)
288
+ @one_line_story_templates ||= []
289
+ @one_line_story_templates << str
290
+ end
291
+
292
+ def short_story_template(title,body,params={})
293
+ @short_story_templates ||= []
294
+ @short_story_templates << params.merge(:template_title=>title, :template_body=>body)
295
+ end
296
+
297
+ def action_links(*links)
298
+ if links.blank?
299
+ @action_links
300
+ else
301
+ @action_links = links
302
+ end
303
+ end
304
+
305
+ def method_missing(name,*args)
306
+ if args.size==1 and self._body.respond_to?("#{name}=")
307
+ self._body.send("#{name}=",*args)
308
+ elsif self._body.respond_to?(name)
309
+ self._body.send(name,*args)
310
+ else
311
+ super
312
+ end
313
+ end
314
+
315
+ # work around the fact that facebook cares about the order of the keys in the hash
316
+ class ImageHolder
317
+ attr_accessor :src,:href
318
+ def initialize(src,href)
319
+ self.src=src
320
+ self.href=href
321
+ end
322
+
323
+ def to_json
324
+ "{\"src\":#{src.to_json}, \"href\":#{href.to_json}}"
325
+ end
326
+ end
327
+
328
+ def image(src,target)
329
+ ImageHolder.new(image_path(src),target.respond_to?(:to_str) ? target : url_for(target))
330
+ end
331
+
332
+ def action_link(text,target)
333
+ {:text=>text, :href=>target}
334
+ end
335
+
336
+ def requires_from_user?(from,body)
337
+ ! (announcement_notification?(from,body) or ref_update?(body) or profile_update?(body))
338
+ end
339
+
340
+ def profile_update?(body)
341
+ body.is_a?(Profile)
342
+ end
343
+
344
+ def ref_update?(body)
345
+ body.is_a?(Ref)
346
+ end
347
+
348
+ def announcement_notification?(from,body)
349
+ from.nil? and body.is_a?(Notification)
350
+ end
351
+
352
+ def send_message(method)
353
+ @recipients = @recipients.is_a?(Array) ? @recipients : [@recipients]
354
+ if from.nil? and @recipients.size==1 and requires_from_user?(from,_body)
355
+ @from = @recipients.first
356
+ end
357
+ # notifications can
358
+ # omit the from address
359
+ raise InvalidSender.new("Sender must be a Facebooker::User") unless from.is_a?(Facebooker::User) || !requires_from_user?(from,_body)
360
+ case _body
361
+ when Facebooker::Feed::TemplatizedAction,Facebooker::Feed::Action
362
+ from.publish_action(_body)
363
+ when Facebooker::Feed::Story
364
+ @recipients.each {|r| r.publish_story(_body)}
365
+ when Notification
366
+ (from.nil? ? Facebooker::Session.create : from.session).send_notification(@recipients,_body.fbml)
367
+ when Email
368
+ from.session.send_email(@recipients,
369
+ _body.title,
370
+ _body.text,
371
+ _body.fbml)
372
+ when Profile
373
+ # If recipient and from aren't the same person, create a new user object using the
374
+ # userid from recipient and the session from from
375
+ @from = Facebooker::User.new(Facebooker::User.cast_to_facebook_id(@recipients.first),Facebooker::Session.create)
376
+ @from.set_profile_fbml(_body.profile, _body.mobile_profile, _body.profile_action, _body.profile_main)
377
+ when Ref
378
+ Facebooker::Session.create.server_cache.set_ref_handle(_body.handle,_body.fbml)
379
+ when UserAction
380
+ @from.session.publish_user_action(_body.template_id,_body.data_hash,_body.target_ids,_body.body_general)
381
+ else
382
+ raise UnspecifiedBodyType.new("You must specify a valid send_as")
383
+ end
384
+ end
385
+
386
+ # nodoc
387
+ # needed for actionview
388
+ def logger
389
+ RAILS_DEFAULT_LOGGER
390
+ end
391
+
392
+ # nodoc
393
+ # delegate to action view. Set up assigns and render
394
+ def render(opts)
395
+ opts = opts.dup
396
+ body = opts.delete(:assigns) || {}
397
+ initialize_template_class(body.dup.merge(:controller=>self)).render(opts)
398
+ end
399
+
400
+
401
+ def initialize_template_class(assigns)
402
+ template_root = "#{RAILS_ROOT}/app/views"
403
+ controller_root = File.join(template_root,self.class.controller_path)
404
+ #only do this on Rails 2.1
405
+ if ActionController::Base.respond_to?(:append_view_path)
406
+ # only add the view path once
407
+ ActionController::Base.append_view_path(controller_root) unless ActionController::Base.view_paths.include?(controller_root)
408
+ end
409
+ returning ActionView::Base.new([template_root,controller_root], assigns, self) do |template|
410
+ template.controller=self
411
+ template.extend(self.class.master_helper_module)
412
+ def template.request_comes_from_facebook?
413
+ true
414
+ end
415
+ end
416
+ end
417
+
418
+
419
+ self.master_helper_module = Module.new
420
+ self.master_helper_module.module_eval do
421
+ # url_helper delegates to @controller,
422
+ # so we need to define that in the template
423
+ # we make it point to the publisher
424
+ include ActionView::Helpers::UrlHelper
425
+ include ActionView::Helpers::TextHelper
426
+ include ActionView::Helpers::TagHelper
427
+ include ActionView::Helpers::FormHelper
428
+ include ActionView::Helpers::FormTagHelper
429
+ include ActionView::Helpers::AssetTagHelper
430
+ include Facebooker::Rails::Helpers
431
+
432
+ #define this for the publisher views
433
+ def protect_against_forgery?
434
+ @paf ||= ActionController::Base.new.send(:protect_against_forgery?)
435
+ end
436
+
437
+ # url_for calls in publishers tend to want full paths
438
+ def url_for(options = {})
439
+ super(options.kind_of?(Hash) ? {:only_path => false}.update(options) : options)
440
+ end
441
+ end
442
+ ActionController::Routing::Routes.named_routes.install(self.master_helper_module)
443
+ include self.master_helper_module
444
+ class <<self
445
+
446
+ def register_all_templates
447
+ all_templates = instance_methods.grep(/_template$/) - %w(short_story_template full_story_template one_line_story_template)
448
+ all_templates.each do |template|
449
+ template_name=template.sub(/_template$/,"")
450
+ puts "Registering #{template_name}"
451
+ send("register_"+template_name)
452
+ end
453
+ end
454
+
455
+ def method_missing(name,*args)
456
+ should_send = false
457
+ method = ''
458
+ if md = /^create_(.*)$/.match(name.to_s)
459
+ method = md[1]
460
+ elsif md = /^deliver_(.*)$/.match(name.to_s)
461
+ method = md[1]
462
+ should_send = true
463
+ elsif md = /^register_(.*)$/.match(name.to_s)
464
+ return FacebookTemplate.register(self, md[1])
465
+ else
466
+ super
467
+ end
468
+
469
+ #now create the item
470
+ (publisher=new).send(method,*args)
471
+ case publisher._body
472
+ when UserAction
473
+ publisher._body.template_name = method
474
+ publisher._body.template_id = FacebookTemplate.bundle_id_for_class_and_method(self,method)
475
+ end
476
+
477
+ should_send ? publisher.send_message(method) : publisher._body
478
+ end
479
+
480
+ def default_url_options
481
+ {:host => Facebooker.canvas_server_base + Facebooker.facebook_path_prefix}
482
+ end
483
+
484
+ def controller_path
485
+ self.to_s.underscore
486
+ end
487
+
488
+ def helper(*args)
489
+ args.each do |arg|
490
+ case arg
491
+ when Symbol,String
492
+ add_template_helper("#{arg.to_s.classify}Helper".constantize)
493
+ when Module
494
+ add_template_helper(arg)
495
+ end
496
+ end
497
+ end
498
+
499
+ def add_template_helper(helper_module) #:nodoc:
500
+ master_helper_module.send :include,helper_module
501
+ include master_helper_module
502
+ end
503
+
504
+
505
+ def inherited(child)
506
+ super
507
+ child.master_helper_module=Module.new
508
+ child.master_helper_module.__send__(:include,self.master_helper_module)
509
+ child.send(:include, child.master_helper_module)
510
+ FacebookTemplate.clear_cache!
511
+ end
512
+
513
+ end
514
+ class PublisherController
515
+ include Facebooker::Rails::Publisher.master_helper_module
516
+ include ActionController::UrlWriter
517
+
518
+ def self.default_url_options(*args)
519
+ Facebooker::Rails::Publisher.default_url_options(*args)
520
+ end
521
+
522
+ end
523
+
524
+ end
525
+ end
526
+ end
@@ -0,0 +1,49 @@
1
+ module Facebooker
2
+ module Rails
3
+ module Routing
4
+ module RouteSetExtensions
5
+ def self.included(base)
6
+ base.alias_method_chain :extract_request_environment, :facebooker
7
+ end
8
+
9
+ def extract_request_environment_with_facebooker(request)
10
+ env = extract_request_environment_without_facebooker(request)
11
+ env.merge :canvas => (request.parameters[:fb_sig_in_canvas]=="1")
12
+ end
13
+ end
14
+ module MapperExtensions
15
+
16
+ # Generates pseudo-resource routes. Since everything is a POST, routes can't be identified
17
+ # using HTTP verbs. Therefore, the action is appended to the beginning of each named route,
18
+ # except for index.
19
+ #
20
+ # Example:
21
+ # map.facebook_resources :profiles
22
+ #
23
+ # Generates the following routes:
24
+ #
25
+ # new_profile POST /profiles/new {:controller=>"profiles", :action=>"new"}
26
+ # profiles POST /profiles/index {:controller=>"profiles", :action=>"index"}
27
+ # show_profile POST /profiles/:id/show {:controller=>"profiles", :action=>"show"}
28
+ # create_profile POST /profiles/create {:controller=>"profiles", :action=>"create"}
29
+ # edit_profile POST /profiles/:id/edit {:controller=>"profiles", :action=>"edit"}
30
+ # update_profile POST /profiles/:id/update {:controller=>"profiles", :action=>"update"}
31
+ # destroy_profile POST /profiles/:id/destroy {:controller=>"profiles", :action=>"destroy"}
32
+ #
33
+ def facebook_resources(name_sym)
34
+ name = name_sym.to_s
35
+
36
+ with_options :controller => name, :conditions => { :method => :post } do |map|
37
+ map.named_route("new_#{name.singularize}", "#{name}/new", :action => 'new')
38
+ map.named_route(name, "#{name}/index", :action => 'index')
39
+ map.named_route("show_#{name.singularize}", "#{name}/:id/show", :action => 'show', :id => /\d+/)
40
+ map.named_route("create_#{name.singularize}", "#{name}/create", :action => 'create')
41
+ map.named_route("edit_#{name.singularize}", "#{name}/:id/edit", :action => 'edit', :id => /\d+/)
42
+ map.named_route("update_#{name.singularize}", "#{name}/:id/update", :action => 'update', :id => /\d+/)
43
+ map.named_route("destroy_#{name.singularize}", "#{name}/:id/destroy", :action => 'destroy', :id => /\d+/)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end