require 'addressable/uri' module Koala module Facebook class API # A light wrapper for collections returned from the Graph API. # It extends Array to allow you to page backward and forward through # result sets, and providing easy access to paging information. class GraphCollection < Array # The raw paging information from Facebook (next/previous URLs). attr_reader :paging # The raw summary information from Facebook (total counts). attr_reader :summary # @return [Koala::Facebook::GraphAPI] the api used to make requests. attr_reader :api # The entire raw response from Facebook. attr_reader :raw_response # The headers from the Facebook response attr_reader :headers # Initialize the array of results and store various additional paging-related information. # # @param [Koala::HTTPService::Response] response object wrapping the raw Facebook response # @param api the Graph {Koala::Facebook::API API} instance to use to make calls # (usually the API that made the original call). # # @return [Koala::Facebook::API::GraphCollection] an initialized GraphCollection # whose paging, summary, raw_response, and api attributes are populated. def initialize(response, api) super response.data["data"] @paging = response.data["paging"] @summary = response.data["summary"] @raw_response = response.data @api = api @headers = response.headers end # @private # Turn the response into a GraphCollection if they're pageable; # if not, return the data of the original response. # The Ads API (uniquely so far) returns a hash rather than an array when queried # with get_connections. def self.evaluate(response, api) return nil if response.nil? is_pageable?(response) ? self.new(response, api) : response.data end # response will always be an instance of Koala::HTTPService::Response # since that is what we get from Koala::Facebook::API#api def self.is_pageable?(response) response.data.is_a?(Hash) && response.data["data"].is_a?(Array) end # Retrieve the next page of results. # # @param [Hash] extra_params Some optional extra parameters for paging. For supported parameters see https://developers.facebook.com/docs/reference/api/pagination/ # # @example With optional extra params # wall = api.get_connections("me", "feed", since: 1379593891) # wall.next_page(since: 1379593891) # # @return a GraphCollection array of additional results (an empty array if there are no more results) def next_page(extra_params = {}) base, args = next_page_params base ? @api.get_page([base, args.merge(extra_params)]) : nil end # Retrieve the previous page of results. # # @param [Hash] extra_params Some optional extra parameters for paging. For supported parameters see https://developers.facebook.com/docs/reference/api/pagination/ # # @return a GraphCollection array of additional results (an empty array if there are no earlier results) def previous_page(extra_params = {}) base, args = previous_page_params base ? @api.get_page([base, args.merge(extra_params)]) : nil end # Arguments that can be sent to {Koala::Facebook::API#graph_call} to retrieve the next page of results. # # @example # @api.graph_call(*collection.next_page_params) # # @return an array of arguments, or nil if there are no more pages def next_page_params @paging && @paging["next"] ? parse_page_url(@paging["next"]) : nil end # Arguments that can be sent to {Koala::Facebook::API#graph_call} to retrieve the previous page of results. # # @example # @api.graph_call(*collection.previous_page_params) # # @return an array of arguments, or nil if there are no previous pages def previous_page_params @paging && @paging["previous"] ? parse_page_url(@paging["previous"]) : nil end # @private def parse_page_url(url) GraphCollection.parse_page_url(url) end # Parse the previous and next page URLs Facebook provides in pageable results. # You'll mainly need to use this when using a non-Rails framework (one without url_for); # to store paging information between page loads, pass the URL (from GraphCollection#paging) # and use parse_page_url to turn it into parameters useful for {Koala::Facebook::API#get_page}. # # @param url the paging URL to turn into graph_call parameters # # @return an array of parameters that can be provided via graph_call(*parsed_params) def self.parse_page_url(url) uri = Addressable::URI.parse(url) base = uri.path.sub(/^\//, '') params = CGI.parse(uri.query) new_params = {} params.each_pair do |key,value| new_params[key] = value.join "," end [base,new_params] end end end end end