class Facebooker::Rails::Publisher

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

Constants

FULL
ONE_LINE

story sizes from the Facebooker API

SHORT

Attributes

_body[RW]
one_line_story_templates[RW]
short_story_templates[RW]

Public Instance Methods

announcement_notification?(from,body) click to toggle source
# File lib/facebooker/rails/publisher.rb, line 393
def announcement_notification?(from,body)
  from.nil? and body.is_a?(Notification)
end
default_url_options() click to toggle source
# File lib/facebooker/rails/publisher.rb, line 120
def default_url_options
  self.class.default_url_options
end
from(*args) click to toggle source
# File lib/facebooker/rails/publisher.rb, line 283
def from(*args)
  if args.size==0
    @from
  else
    @from=args.first
  end        
end
full_story_template(title=nil,body=nil,params={}) click to toggle source
# 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
image(src,target) click to toggle source
# 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
initialize_template_class(assigns) click to toggle source
# 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
logger() click to toggle source

nodoc needed for actionview

# File lib/facebooker/rails/publisher.rb, line 435
def logger
  RAILS_DEFAULT_LOGGER
end
method_missing(name,*args) click to toggle source
# 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
one_line_story_template(str) click to toggle source
# File lib/facebooker/rails/publisher.rb, line 325
def one_line_story_template(str)
  @one_line_story_templates ||= []
  @one_line_story_templates << str
end
profile_update?(body) click to toggle source
# File lib/facebooker/rails/publisher.rb, line 385
def profile_update?(body)
  body.is_a?(Profile)
end
protect_against_forgery?() click to toggle source

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
recipients(*args) click to toggle source
# File lib/facebooker/rails/publisher.rb, line 275
def recipients(*args)
  if args.size==0
    @recipients
  else
    @recipients=args.first
  end
end
ref_update?(body) click to toggle source
# File lib/facebooker/rails/publisher.rb, line 389
def ref_update?(body)
  body.is_a?(Ref)
end
render(opts) click to toggle source

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
request_comes_from_facebook?() click to toggle source

use facebook options everywhere

# File lib/facebooker/rails/publisher.rb, line 125
def request_comes_from_facebook?
  true
end
requires_from_user?(from,body) click to toggle source
# 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
send_as(option) click to toggle source
# 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
send_message(method) click to toggle source
# 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
short_story_template(title,body,params={}) click to toggle source
# 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(options = {}) click to toggle source

#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

Public Class Methods

controller_path() click to toggle source
# File lib/facebooker/rails/publisher.rb, line 558
def controller_path
  self.to_s.underscore
end
default_url_options() click to toggle source
# File lib/facebooker/rails/publisher.rb, line 116
def self.default_url_options
  {:host => Facebooker.canvas_server_base + Facebooker.facebook_path_prefix}
end
helper(*args) click to toggle source
# 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
inherited(child) click to toggle source
# 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
method_missing(name,*args) click to toggle source
# 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
new() click to toggle source
# 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
register_all_templates() click to toggle source
# 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
register_all_templates_on_all_applications() click to toggle source
# 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
respond_to?(method_symbol, include_private=false) click to toggle source
# 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
unregister_inactive_templates() click to toggle source
# 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