Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
A simple Gem for reading shopify API call limits
Ruby
branch: master

This branch is even with christocracy:master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib
spec
.gitignore
Gemfile
README.rdoc
Rakefile
shopify-api-limits.gemspec

README.rdoc

shopify-api-limits

My friend Dave (aka “hunkybill”) posted a problem to me one day about ShopifyAPI call limits, offering a case of beer if I could find a solution: forums.shopify.com/categories/9/posts/49003

So in the HTTP headers, the ShopifyAPI will return to you in each API request how many calls you've made, as well as the maximum number of calls available.

Problem

ActiveResource does not make it easy to read the HTTP response headers, since the method #request in ActiveResource::Connection does not save a reference to the HTTP response:

# Makes a request to the remote service.
def request(method, path, *arguments)
  result = ActiveSupport::Notifications.instrument("request.active_resource") do |payload|
    payload[:method]      = method
    payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}"
    payload[:result]      = http.send(method, path, *arguments)
  end
  handle_response(result) # <-- right here:  handle_response returns an instance of HTTPResponse but doesn't save a ref to it!
rescue Timeout::Error => e
  raise TimeoutError.new(e.message)
rescue OpenSSL::SSL::SSLError => e
  raise SSLError.new(e.message)
end

Solution

Hack ActiveResource::Connection to introduce a new attr_reader :response and capture the returned instance of HTTPResponse provided by net/http from the #handle_response method.

module ActiveResource
  class Connection
    # HACK:  Add an attr_reader for response
    attr_reader :response

    # capture the original #handle_response as an unbound method instead of using alias 
    handle_response = self.instance_method(:handle_response)

    # re-implement #handle_response to capture the returned HTTPResponse to an instance var.    
    define_method(:handle_response) do |response| 
      @response = handle_response.bind(self).call(response)
    end 
  end
end

Now it's possible to access the HTTPResponse instance directly from ActiveResource, via:

foo = ActiveResource::Base.connection.response['http-header-param-foo']

Installation

gem "shopify_api"
gem "shopify-api-limits"

Usage

count_shop = ShopifyAPI.credit_used :shop
limit_shop = ShopifyAPI.credit_limit :shop

count_global = ShopifyAPI.credit_used :global
limit_global = ShopifyAPI.credit_limit :global

Generally, you shouldn't need to use the methods above directly – rather, they're used under-the-hood by the following helpful methods which don't require a scope (:shop/:global): If the :global scope has fewer calls available than the :local scope, the methods will operate upon the :global scope; otherwise, values will be returned based upon the :shop scope.

unless ShopifyAPI.credit_maxed?
  #make a ShopifyAPI call
end

until ShopifyAPI.credit_maxed? || stop_condition
  # make some ShopifyAPI calls
end

while ShopifyAPI.credit_left || stop_condition
  # make some ShopifyAPI calls
end

A special bonus for retrieving large recordsets > 250 records

Shopify places a hard limit of 250 on the number of records returned in a single request. There are ways around this, including one listed here bit.ly/kgwCRc which involves manually calculating the total & number of pages then iterating to make several API calls. This gem encapsulates this behaviour allowing you to make what feels like a single call to the ShopifyAPI. Simply set :params => {:limit => false}. False as in, “no limit”

For example, imagine a store which has 251 orders and you want to fetch them all. Since the maximum number of records returned in a single request is 250, 2 requests must be made to the API in order to fetch all 251 records.

records = ShopifyAPI::Order.all(:params => {:limit => false})
puts records.count
=> 251

Without {:limit => false}, the normal behaviour will operate (ie: just one request with 250 records returned):

records = ShopifyAPI::Order.all(:params => {:limit => 250})
puts records.count
=> 250

If you don't have enough API credits to perform the multiple requests to serve your desired recordset, a ShopifyAPI::Limits::Error will be raised:

puts ShopifyAPI::Order.count
=> 251

puts ShopifyAPI.credit_left
=> 1

begin
  rs = ShopifyAPI::Order.all(:params => {:limit => false})
rescue ShopifyAPI::Limits::Error
  puts "Uhoh...didn't have enough credits to do that.  Maybe you wanna' queue your task for a later date."
end
Something went wrong with that request. Please try again.