Skip to content

Commit

Permalink
Added API limits code + tests (merged from shopify-api-limits)
Browse files Browse the repository at this point in the history
  • Loading branch information
David Underwood committed Aug 31, 2011
1 parent 14d017a commit 4ffdb62
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
16 changes: 16 additions & 0 deletions lib/active_resource/connection_ext.rb
@@ -0,0 +1,16 @@
require 'active_support/core_ext/module/aliasing'

module ActiveResource
class Connection

attr_reader :response

def handle_response_with_response_capture(response)
@response = handle_response_without_response_capture(response)
end

alias_method_chain :handle_response, :response_capture
# alias_method :handle_response_without_instance, :handle_response
# alias_method :handle_response, :handle_response_with_instance
end
end
5 changes: 5 additions & 0 deletions lib/shopify_api.rb
Expand Up @@ -2,8 +2,12 @@
require 'active_support/core_ext/class/attribute_accessors'
require 'digest/md5'
require 'base64'
require 'active_resource/connection_ext'
require 'shopify_api/limits'

module ShopifyAPI
include Limits

METAFIELD_ENABLED_CLASSES = %w( Order Product CustomCollection SmartCollection Page Blog Article Variant)
EVENT_ENABLED_CLASSES = %w( Order Product CustomCollection SmartCollection Page Blog Article )

Expand Down Expand Up @@ -547,4 +551,5 @@ class ScriptTag < Base
EVENT_ENABLED_CLASSES.each do |klass|
"ShopifyAPI::#{klass}".constantize.send(:include, Events)
end

end
76 changes: 76 additions & 0 deletions lib/shopify_api/limits.rb
@@ -0,0 +1,76 @@
module ShopifyAPI

This comment has been minimized.

Copy link
@christocracy

christocracy Sep 23, 2011

You just drop in my code without any acknowledgement at all?? Maybe a link in the file header to where it came from?

https://github.com/christocracy/shopify-api-limits/blob/master/lib/shopify-api-limits/shopify_api/limits.rb

This comment has been minimized.

Copy link
@davefp

davefp Sep 23, 2011

Contributor

Added this in a commit just now.

This comment has been minimized.

Copy link
@christocracy

christocracy Sep 23, 2011

Thanks. What was Mr. Underwood thinking? He's working for a well-known corporation: you can't just secretly lift code out of another's project. I spent a number of hours working on and supporting this for the benefit of the developer community. The least Shopify could do is honor me by politely asking permission and publicly stating where contribution came from. I realize this has been done now, but only after I discovered it myself.

This comment has been minimized.

Copy link
@ssoroka

ssoroka Sep 23, 2011

@christocracy : perhaps you could include a LICENSE file to help avoid future mix-ups and make your intentions clear. I'm sure the mistake was not out of malice.

This comment has been minimized.

Copy link
@christocracy

christocracy Sep 23, 2011

I'm positive there's no malice and It's certain, I didn't publish a LICENSE. If I were Oracle and you were Google, you could laugh in my face, HA!.

It's a smaller world we're in, more gentlemanly, non?

Gentleman say "Please sir, may I...", is all I'm saying.

This comment has been minimized.

Copy link
@ssoroka

ssoroka Sep 23, 2011

Our apologies; we definitely appreciate the contribution, and should have checked with you first.

We would like to make it up to you, gentlemen-style. We'll have something in your inbox shortly. :)

This comment has been minimized.

Copy link
@christocracy

christocracy Sep 23, 2011

While this code is very small, not having to devote development resources to build this internally saved Shopify real $. Were I billing for this, I probably would have sent and invoice for ~$2k. Were it built internally within Shopify, it probably would have cost about 3-4 days of dev and testing from a developer experienced enough to walk deep inside of ActiveResource to determine that ActiveResource inexplicably provides no reference to the Response instance with which to read the returned HTTP header params and how to elegantly remedy that without being broken by future changes to ActiveResource.

This comment has been minimized.

Copy link
@ssoroka

ssoroka Sep 23, 2011

If your intention was not for this code to be open-source, let us know and we can remove it from this project for you.

This comment has been minimized.

Copy link
@christocracy

christocracy Sep 23, 2011

If it's not clear, it's certainly open-source and free for use by Shopify. I'm honored.

This comment has been minimized.

Copy link
@wvanbergen

wvanbergen Sep 23, 2011

That's awesome, thanks again for your contribution!

This comment has been minimized.

Copy link
@davefp

davefp Sep 26, 2011

Contributor

Just want to clear a few things up: I publicly announced that I had merged your code in a blog post I wrote at the time of the commit. There was nothing secret about the inclusion. After discussing your request for inclusion as the author of this code, I've also started a CONTRIBUTORS file in the root of this project where you are listed. It was definitely not my aim to 'steal' your code.

Link to blog article: http://www.shopify.com/technology/4044762-state-of-the-api-september-12th-2011

As mentioned above, please consider using a license on your work in future so that people know how you wish to be attributed. Thanks.

This comment has been minimized.

Copy link
@christocracy

christocracy via email Sep 26, 2011

module Limits
def self.included(klass)
klass.send(:extend, ClassMethods)
end

module ClassMethods

# Takes form num_requests_executed/max_requests
# Eg: 101/3000
CREDIT_LIMIT_HEADER_PARAM = {
:global => 'http_x_shopify_api_call_limit',
:shop => 'http_x_shopify_shop_api_call_limit'
}

##
# How many more API calls can I make?
# @return {Integer}
#
def credit_left
shop = credit_limit(:shop) - credit_used(:shop)
global = credit_limit(:global) - credit_used(:global)
shop < global ? shop : global
end
alias_method :available_calls, :credit_left

This comment has been minimized.

Copy link
@christocracy

christocracy Sep 23, 2011

You probably don't need to alias any of these methods anymore. I only did that because I changed the API after a very early gem version. They're not documented anymore.


##
# Have I reached my API call limit?
# @return {Boolean}
#
def credit_maxed?
credit_left <= 0
end
alias_method :maxed?, :credit_maxed?

##
# How many total API calls can I make?
# NOTE: subtracting 1 from credit_limit because I think ShopifyAPI cuts off at 299/2999 or shop/global limits.
# @param {Symbol} scope [:shop|:global]
# @return {Integer}
#
def credit_limit(scope=:shop)
@api_credit_limit ||= {}
@api_credit_limit[scope] ||= api_credit_limit_param(scope).pop.to_i - 1
end
alias_method :call_limit, :credit_limit

##
# How many API calls have I made?
# @param {Symbol} scope [:shop|:global]
# @return {Integer}
#
def credit_used(scope=:shop)
api_credit_limit_param(scope).shift.to_i
end
alias_method :call_count, :credit_used

##
# @return {HTTPResonse}
#
def response
Shop.current unless ShopifyAPI::Base.connection.response
ShopifyAPI::Base.connection.response
end

private

##
# @return {Array}
#
def api_credit_limit_param(scope)
response[CREDIT_LIMIT_HEADER_PARAM[scope]].split('/')
end
end
end
end
37 changes: 37 additions & 0 deletions test/limits_test.rb
@@ -0,0 +1,37 @@
require 'test_helper'
require 'mocha'

class LimitsTest < Test::Unit::TestCase
def setup
ShopifyAPI::Base.site = "test.myshopify.com"
@header_hash = {'http_x_shopify_api_call_limit' => '150/3000',
'http_x_shopify_shop_api_call_limit' => '100/300'}
ShopifyAPI::Base.connection.expects(:response).at_least(0).returns(@header_hash)
end

context "Limits" do
should "fetch limit total" do
assert_equal(299, ShopifyAPI.credit_limit(:shop))
assert_equal(2999, ShopifyAPI.credit_limit(:global))
end

should "fetch used calls" do
assert_equal(100, ShopifyAPI.credit_used(:shop))
assert_equal(150, ShopifyAPI.credit_used(:global))
end

should "calculate remaining calls" do
assert_equal(199, ShopifyAPI.credit_left)
end

should "flag maxed out credits" do
assert !ShopifyAPI.maxed?
@header_hash = {'http_x_shopify_api_call_limit' => '2999/3000',
'http_x_shopify_shop_api_call_limit' => '299/300'}
ShopifyAPI::Base.connection.expects(:response).at_least(1).returns(@header_hash)
assert ShopifyAPI.maxed?

This comment has been minimized.

Copy link
@christocracy

christocracy Sep 23, 2011

You're using aliased method #maxed here, which is meant to be deprecated. Use #credit_maxed? instead.
You should only use methods prefixed with "credit_" (#credit_used, #credit_left, #credit_limit, #credit_maxed?). Get rid of aliases.

end
end


end

0 comments on commit 4ffdb62

Please sign in to comment.