module Bearcat class ApiArray include Enumerable attr_reader :status, :headers, :members def self.process_response(response, api_client) if response.body.is_a?(Array) ApiArray.new(response, api_client) elsif key = array_key(response) ApiArray.new(response, api_client, key) else response.body end end def initialize(response, api_client, array_key = nil) @api_client = api_client @array_key = array_key @page_count = 100 case response.status when 200..206 @members = process_body(response) @status = response.status @headers = response.headers @method = response.env[:method] init_pages(@headers['Link']) end end def [](i) @members[i] end def last @members.last end def each(&block) @members.each { |member| block.call(member) } end def pages? @link_hash['next'] || @link_hash['prev'] end def next_page load_page('next') end def prev_page load_page('prev') end def first_page load_page('first') end def last_page load_page('last') end def each_page(page_count = 50, &block) @page_count = page_count block.call(@members) while @link_hash['next'] response = get_page(@link_hash['next']) @headers = response.headers @status = response.status @method = response.env[:method] @members = process_body(response) init_pages(@headers[:link]) block.call(@members) end @link_hash = {} end def all_pages!(page_count = 50) if pages? @page_count = page_count response = get_page(@link_hash['first']) @headers = response.headers @status = response.status @method = response.env[:method] init_pages(@headers[:link]) @members = process_body(response) while @link_hash['next'] response = get_page(@link_hash['next']) @headers = response.headers @status = response.status @method = response.env[:method] @members.concat(process_body(response)) init_pages(@headers[:link]) end @link_hash = {} end self end private def init_pages(link_header) @link_hash = {} if @headers.has_key? 'Link' links = link_header.split(/,\s?/) links.each do |link| link_parts = link.split(/;\s?/) url = link_parts.shift.strip.gsub(/^<|>$/, '') rel = link_parts.find { |part| part.gsub('"', '').split('=').first == 'rel' } @link_hash[rel.gsub('"', '').split('=').last] = url end end end def get_page(url, params = {}) params['per_page'] = @page_count unless params.key?('per_page') || !@page_count query = URI.parse(url).query p = CGI.parse(query) u = url.gsub("?#{query}", '') # strip value out of array if value is an array and key doesn't have [] (parameter is not an array parameter) p.each { |k, v| p[k] = v.first if v.is_a?(Array) && k !~ /\[\]$/ } # remove [] from key names, this is copied from rails' {}.transform_keys! p.keys.each { |k| p[k.delete('[]')] = p.delete(k) } # merge params p.merge!(params) @api_client.connection.send(:get) do |r| r.url(u, p) end end def load_page(rel) if @link_hash.has_key? rel response = get_page(@link_hash[rel]) ApiArray.process_response(response, @api_client) end end #TODO: This is a quick fix for JSONAPI responses if we need to do this for anything else we need to do this a better way def self.array_key(response) key = nil if response.env[:method] == :get path = response.env[:url].path if path.match(/.*\/(courses||groups)\/\d+\/conferences/) key = 'conferences' elsif path.match(/.*\/accounts\/(?:(?:\d+~)?\d+|self)\/terms/) key = 'enrollment_terms' end end key end def process_body(response) if response.body.is_a?(Array) response.body elsif response.body.is_a?(Hash) && @array_key response.body[@array_key] end end end end