ActionMailer like module for publishing Facbook messages
To use, create a subclass and define methods Each method should start by calling #send_as to specify the type of message Valid options are :email and :notification, :user_action, :profile, :ref, :publish_stream
Below is an example of each type
class TestPublisher < Facebooker::Rails::Publisher # The new message templates are supported as well # First, create a method that contains your templates: # You may include multiple one line story templates and short story templates # but only one full story template # Your most specific template should be first # # Before using, you must register your template by calling register. For this example # You would call TestPublisher.register_publish_action # Registering the template will store the template id returned from Facebook in the # facebook_templates table that is created when you create your first publisher def publish_action_template one_line_story_template "{*actor*} did stuff with {*friend*}" one_line_story_template "{*actor*} did stuff" short_story_template "{*actor*} has a title {*friend*}", render(:partial=>"short_body") short_story_template "{*actor*} has a title", render(:partial=>"short_body") full_story_template "{*actor*} has a title {*friend*}", render(:partial=>"full_body") action_links action_link("My text {*template_var*}","{*link_url*}") end # To send a registered template, you need to create a method to set the data # The publisher will look up the template id from the facebook_templates table def publish_action(f) send_as :user_action from f story_size SHORT # or ONE_LINE or FULL data :friend=>"Mike" end # Provide a from user to send a general notification # if from is nil, this will send an announcement def notification(to,f) send_as :notification recipients to from f fbml "Not" end def email(to,f) send_as :email recipients to from f title "Email" fbml 'text' text fbml end # This will render the profile in /users/profile.fbml.erb # it will set @user to user_to_update in the template # The mobile profile will be rendered from the app/views/test_publisher/_mobile.erb # template def profile_update(user_to_update,user_with_session_to_use) send_as :profile from user_with_session_to_use recipients user_to_update profile render(:file=>"users/profile.fbml.erb",:assigns=>{:user=>user_to_update}) profile_action "A string" mobile_profile render(:partial=>"mobile",:assigns=>{:user=>user_to_update}) end # Update the given handle ref with the content from a # template def ref_update(user) send_as :ref from user fbml render(:file=>"users/profile",:assigns=>{:user=>user_to_update}) handle "a_ref_handle" end # Publish a post into the stream on the user's Wall and News Feed. def publish_stream(user_with_session_to_use, user_to_update, params) send_as :publish_stream from user_with_session_to_use target user_to_update attachment params[:attachment] message params[:message] action_links params[:action_links] end
To send a message, use ActionMailer like semantics
TestPublisher.deliver_action(@user)
For testing, you may want to create an instance of the underlying message without sending it
TestPublisher.create_action(@user)
will create and return an instance of Facebooker::Feeds::Action
Publisher makes many helpers available, including the linking and asset helpers
story sizes from the Facebooker API
# File lib/facebooker/rails/publisher.rb, line 377 def action_link(text,target) {:text=>text, :href=>target} end
# File lib/facebooker/rails/publisher.rb, line 335 def action_links(*links) if self._body and self._body.respond_to?(:action_links) self._body.send(:action_links,*links) end if links.blank? @action_links else @action_links = *links end end
# File lib/facebooker/rails/publisher.rb, line 393 def announcement_notification?(from,body) from.nil? and body.is_a?(Notification) end
# File lib/facebooker/rails/publisher.rb, line 120 def default_url_options self.class.default_url_options end
# File lib/facebooker/rails/publisher.rb, line 283 def from(*args) if args.size==0 @from else @from=args.first end end
# File lib/facebooker/rails/publisher.rb, line 317 def full_story_template(title=nil,body=nil,params={}) if title.nil? @full_story_template else @full_story_template=params.merge(:template_title=>title, :template_body=>body) end end
# File lib/facebooker/rails/publisher.rb, line 373 def image(src,target) ImageHolder.new(image_path(src),target.respond_to?(:to_str) ? target : url_for(target)) end
# File lib/facebooker/rails/publisher.rb, line 448 def initialize_template_class(assigns) template_root = "#{RAILS_ROOT}/app/views" controller_root = File.join(template_root,self.class.controller_path) #only do this on Rails 2.1 if ActionController::Base.respond_to?(:append_view_path) # only add the view path once unless ActionController::Base.view_paths.include?(controller_root) ActionController::Base.append_view_path(controller_root) ActionController::Base.append_view_path(controller_root+"/..") end view_paths = ActionController::Base.view_paths else view_paths = [template_root, controller_root] end returning ActionView::Base.new(view_paths, assigns, self) do |template| template.controller=self template.extend(self.class.master_helper_module) def template.request_comes_from_facebook? true end end end
nodoc needed for actionview
# File lib/facebooker/rails/publisher.rb, line 435 def logger RAILS_DEFAULT_LOGGER end
# File lib/facebooker/rails/publisher.rb, line 346 def method_missing(name,*args) if args.size==1 and self._body.respond_to?("#{name}=") self._body.send("#{name}=",*args) elsif self._body.respond_to?(name) self._body.send(name,*args) else super end end
# File lib/facebooker/rails/publisher.rb, line 325 def one_line_story_template(str) @one_line_story_templates ||= [] @one_line_story_templates << str end
# File lib/facebooker/rails/publisher.rb, line 385 def profile_update?(body) body.is_a?(Profile) end
define this for the publisher views
# File lib/facebooker/rails/publisher.rb, line 487 def protect_against_forgery? @paf ||= ActionController::Base.new.send(:protect_against_forgery?) end
# File lib/facebooker/rails/publisher.rb, line 275 def recipients(*args) if args.size==0 @recipients else @recipients=args.first end end
# File lib/facebooker/rails/publisher.rb, line 389 def ref_update?(body) body.is_a?(Ref) end
nodoc delegate to action view. Set up assigns and render
# File lib/facebooker/rails/publisher.rb, line 441 def render(opts) opts = opts.dup body = opts.delete(:assigns) || {} initialize_template_class(body.dup.merge(:controller=>self)).render(opts) end
use facebook options everywhere
# File lib/facebooker/rails/publisher.rb, line 125 def request_comes_from_facebook? true end
# File lib/facebooker/rails/publisher.rb, line 381 def requires_from_user?(from,body) ! (announcement_notification?(from,body) or ref_update?(body) or profile_update?(body)) end
# File lib/facebooker/rails/publisher.rb, line 292 def send_as(option) self._body=case option when :action Facebooker::Feed::Action.new when :story Facebooker::Feed::Story.new when :templatized_action Facebooker::Feed::TemplatizedAction.new when :notification Notification.new when :email Email.new when :profile Profile.new when :ref Ref.new when :user_action UserAction.new when :publish_stream StreamPost.new else raise UnknownBodyType.new("Unknown type to publish") end end
# File lib/facebooker/rails/publisher.rb, line 397 def send_message(method) @recipients = @recipients.is_a?(Array) ? @recipients : [@recipients] if from.nil? and @recipients.size==1 and requires_from_user?(from,_body) @from = @recipients.first end # notifications can # omit the from address raise InvalidSender.new("Sender must be a Facebooker::User") unless from.is_a?(Facebooker::User) || !requires_from_user?(from,_body) case _body when Facebooker::Feed::TemplatizedAction,Facebooker::Feed::Action from.publish_action(_body) when Facebooker::Feed::Story @recipients.each {|r| r.publish_story(_body)} when Notification (from.nil? ? Facebooker::Session.create : from.session).send_notification(@recipients,_body.fbml) when Email from.session.send_email(@recipients, _body.title, _body.text, _body.fbml) when Profile # If recipient and from aren't the same person, create a new user object using the # userid from recipient and the session from from @from = Facebooker::User.new(Facebooker::User.cast_to_facebook_id(@recipients.first),Facebooker::Session.create) @from.set_profile_fbml(_body.profile, _body.mobile_profile, _body.profile_action, _body.profile_main) when Ref Facebooker::Session.create.server_cache.set_ref_handle(_body.handle,_body.fbml) when UserAction @from.session.publish_user_action(_body.template_id,_body.data_hash,_body.target_ids,_body.body_general,_body.story_size) when Facebooker::StreamPost @from.publish_to(_body.target, {:attachment => _body.attachment, :action_links => @action_links, :message => _body.message }) else raise UnspecifiedBodyType.new("You must specify a valid send_as") end end
# File lib/facebooker/rails/publisher.rb, line 330 def short_story_template(title,body,params={}) @short_story_templates ||= [] @short_story_templates << params.merge(:template_title=>title, :template_body=>body) end
#url_for calls in publishers tend to want full paths
# File lib/facebooker/rails/publisher.rb, line 492 def url_for(options = {}) super(options.kind_of?(Hash) ? {:only_path => false}.update(options) : options) end
# File lib/facebooker/rails/publisher.rb, line 558 def controller_path self.to_s.underscore end
# File lib/facebooker/rails/publisher.rb, line 116 def self.default_url_options {:host => Facebooker.canvas_server_base + Facebooker.facebook_path_prefix} end
# File lib/facebooker/rails/publisher.rb, line 562 def helper(*args) args.each do |arg| case arg when Symbol,String add_template_helper("#{arg.to_s.camelcase}Helper".constantize) when Module add_template_helper(arg) end end end
# File lib/facebooker/rails/publisher.rb, line 579 def inherited(child) super child.master_helper_module=Module.new child.master_helper_module.__send__(:include,self.master_helper_module) child.send(:include, child.master_helper_module) FacebookTemplate.clear_cache! end
# File lib/facebooker/rails/publisher.rb, line 533 def method_missing(name,*args) should_send = false method = '' if md = /^create_(.*)$/.match(name.to_s) method = md[1] elsif md = /^deliver_(.*)$/.match(name.to_s) method = md[1] should_send = true elsif md = /^register_(.*)$/.match(name.to_s) return FacebookTemplate.register(self, md[1]) else super end #now create the item (publisher=new).send(method,*args) case publisher._body when UserAction publisher._body.template_name = method publisher._body.template_id ||= FacebookTemplate.bundle_id_for_class_and_method(self,method) end should_send ? publisher.send_message(method) : publisher._body end
# File lib/facebooker/rails/publisher.rb, line 107 def initialize @from = nil @full_story_template = nil @recipients = nil @action_links = nil @controller = PublisherController.new(self) @action_links = nil end
# File lib/facebooker/rails/publisher.rb, line 507 def register_all_templates all_templates = instance_methods.grep(/_template$/) - %w(short_story_template full_story_template one_line_story_template) all_templates.each do |template| template_name=template.sub(/_template$/,"") puts "Registering #{template_name}" send("register_"+template_name) end end
# File lib/facebooker/rails/publisher.rb, line 500 def register_all_templates_on_all_applications Facebooker.with_all_applications do puts "Registering templates for #{Facebooker.api_key}" register_all_templates end end
# File lib/facebooker/rails/publisher.rb, line 525 def respond_to?(method_symbol, include_private=false) if match = /^(create|deliver|register)_([_a-z]\w*)/.match(method_symbol.to_s) instance_methods.include?(match[2]) else super(method_symbol, include_private) end end
# File lib/facebooker/rails/publisher.rb, line 516 def unregister_inactive_templates session = Facebooker::Session.create active_template_ids = FacebookTemplate.all.map(&:bundle_id) all_template_ids = session.active_template_bundles.map {|t| t["template_bundle_id"]} (all_template_ids - active_template_ids).each do |template_bundle_id| session.deactivate_template_bundle_by_id(template_bundle_id) end end