This is long overdue, and probably an image that facebook should have in their docs. Here’s exactly how a wall post is structured:

as an aside you are discouraged from using the message field unless the content is user generated.
This is long overdue, and probably an image that facebook should have in their docs. Here’s exactly how a wall post is structured:

as an aside you are discouraged from using the message field unless the content is user generated.
This is really just a condensed version of yesterday’s post, with a little more emphasis on implementation than actual function.
I found way too many posts saying that you cannot or should not overrride ActiveRecord/ActiveModel implementations of method_missing. So here’s the quick and easy way to do it without losing any functionality.
def method_missing(meth, *args, &block) begin #send methods to superclass if possible. super rescue begin #custom implementation goes here. rescue # this is a workaround so it won't get stuck in a busy loop. raise "NoMethodError" end end end
This morning I started writing an app that requires facebook access. I have done this several times over (mostly during my time at gtrot) but today I came up with something I really like. It does everything you would expect, cool syntax included. I assume the same strategy would work with ActiveRecord but I am not 100% sure about that just yet.
my_facebook_account = FacebookUser.new({:access_token=>"...", :fbid=>"..."}) #accessing my own attributes: my_friends = my_facebook_account.friends my_checkins = my_facebook_account.checkins #accessing my friends information just requires that you change the fbid, and continue to use the same access_token my_friends_account = FacebookUser.new({:access_token=>"...same as above...", :fbid=>"...friends id goes here.."}) my_friends_account.checkins
As with everything else on the site this code is licensed only for non-commercial use only, if you would like to use this code commercially get in touch with me.
And now for the class:
Fair warning: if you edit this class and end up causing an exception (say, NoMethodError) you could end up stuck in a busy loop.
class FacebookUser # Includes for active model include ActiveModel::Validations include ActiveModel::Conversion extend ActiveModel::Naming # http get/post require 'net/http' require 'uri' attr_accessor :access_token, :fbid validates_presence_of :access_token, :fbid # class constant for timeouts TIMEOUT = 30 # class constants for the facebook api root FB_ROOT = "http://graph.facebook.com" FB_ROOT_S = "https://graph.facebook.com" def initialize(attributes = {}) attributes.each do |key, value| send("#{key}=", value) end end # required for active model support def persisted? false end # override ActiveModel::method_missing without losing functionality def method_missing(meth, *args, &block) begin super rescue response = https_with_token "#{FB_ROOT_S}/#{fbid}/#{meth}" unless response["data"].nil? response["data"] else response end end end # handle user endpoint def me https_with_token "#{FB_ROOT_S}/#{fbid}" end protected def https_with_token url do_http "#{url}?access_token=#{access_token}" end def http_without_token url do_http "#{url}" end def do_http url use_ssl = false if url.start_with? "https:" use_ssl = true end uri = URI.parse "#{url}" http = Net::HTTP.new uri.host, uri.port http.open_timeout = TIMEOUT http.read_timeout = TIMEOUT if url.start_with? "https:" http.use_ssl = true http.ssl_timeout = TIMEOUT end # facebook will return content gzipped most of the time request = Net::HTTP::Get.new(uri.request_uri, { "accept-encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" }) response = http.request(request) # exception handling in case facebook doesn't responding with gzip begin response.body = Zlib::GzipReader.new(StringIO.new(response.body)).read rescue # nothing to do here, if that fails the response isn't gzipped or is malformed. end ActiveSupport::JSON.decode response.body end end
ActiveRecord::Base.uncached do #all operations here will not use the cache. end
TyPhighter 0.0.4 has been released, it can be found on Github
require "TyPhighter/version" module TyPhighter class TyPhighter require 'net/http' require 'uri' require 'thread' @running_threads = nil @finished_threads = nil @results = nil def self.test_method this_object = TyPhighter.new request_objects = [{:url => "http://www.google.com/"},{:url => "http://www.yahoo.com"},{:url => "http://www.bing.com"}] params = {} params = request_objects this_object.new_threaded_http params end def initialize @running_threads = ThreadGroup.new @finished_threads = ThreadGroup.new end ## # Required params: # :request_objects - contains a linear array of request objects: # => [{ :url => "http://www.google.com", :post_args => {"key" => "value"} }, {:url => "http://www.yahoo.com", :post_args => {"key" => "value"}, :options => {:timeout => 5} }] # => # # Optional params: # :blocking - true or false, if false the request will not block # :port # :timeout - Defaults to 10 seconds # :headers - Defaults to empty # :ssl_verify - Defaults to true ##/ def new_threaded_http params params = check_params params new_threads = [] semaphore = Mutex.new results = {} params.each do |request_object| new_threads << Thread.new do this_thread = Thread.current puts request_object.to_s if request_object[:url].start_with? "https" use_ssl = true else use_ssl = false end this_thread[:uri] = URI.parse(request_object[:url]) this_thread[:http] = Net::HTTP.new(this_thread[:uri].host, this_thread[:uri].port) this_thread[:http].use_ssl = use_ssl this_thread[:http].open_timeout = request_object[:timeout] this_thread[:http].read_timeout = request_object[:timeout] this_thread[:blocking] = request_object[:blocking] if use_ssl == true this_thread[:http].ssl_timeout = request_object[:timeout] end if request_object[:post_args].nil? if request_object[:options][:headers].nil? this_thread[:request] = Net::HTTP::Get.new(this_thread['uri'].request_uri) else this_thread[:request] = Net::HTTP::Get.new(this_thread['uri'].request_uri, request_object[:options][:headers]) end else if request_object[:options][:headers].nil? this_thread[:request] = Net::HTTP::Post.new(this_thread['uri'].request_uri) else this_thread[:request] = Net::HTTP::Post.new(this_thread['uri'].request_uri, request_object[:options][:headers]) end this_thread[:request].set_form_data(request_object[:post_args]) end #warn "\nMaking request: " + this_thread[:request].to_s this_thread[:response] = this_thread[:http].request(this_thread[:request]) return_hash = {} return_hash[:body] = this_thread[:response].body semaphore.synchronize { results[request_object[:url]] = return_hash[:body] } if request_object[:options][:blocking] == true this_thread.join end end end new_threads.each do |thread| if thread.alive? @running_threads.add(thread) else warn "Thread finished: " + thread.to_s @finished_threads.add(thread) end end @running_threads.list.each do |thread| if thread.alive? thread.join end end #results.each do |k,v| # puts "key: " + k[0,50] # puts "value: " + v[0,50] #end results end ## # Returns true if all threads have completed, false otherwise ## def threads_complete @threads.each do |thread| if thread.alive? return true end end return false end def get_data end def block_and_wait_for_threads end private def check_params params #puts params[:request_objects] if params.nil? raise "Must pass params" end unless params.kind_of? Array raise "params must be an array." else params.each do |request_object| request_object = check_request_object request_object end end return params end def check_request_object request_object unless request_object.kind_of? Hash raise "request objects must be hash: " + request_object.to_s end if request_object[:options].nil? request_object[:options] = {} warn "Failed to pass options for: " + request_object.to_s end if request_object[:url].end_with? "/" warn "url should not contain trailing slash(/): " + request_object.to_s end if request_object[:timeout].nil? request_object[:options][:timeout] = 10 warn "Failed to pass params[:timeout], defaulting to 10 seconds." end return request_object end end end