shopify_api 1.2.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +10 -0
- data/README.rdoc +6 -1
- data/RELEASING +16 -0
- data/lib/active_resource/connection_ext.rb +16 -0
- data/lib/shopify_api.rb +12 -533
- data/lib/shopify_api/cli.rb +9 -9
- data/lib/shopify_api/countable.rb +7 -0
- data/lib/shopify_api/events.rb +7 -0
- data/lib/shopify_api/json_format.rb +23 -0
- data/lib/shopify_api/limits.rb +76 -0
- data/lib/shopify_api/metafields.rb +18 -0
- data/lib/shopify_api/resources.rb +40 -0
- data/lib/shopify_api/resources/address.rb +4 -0
- data/lib/shopify_api/resources/application_charge.rb +9 -0
- data/lib/shopify_api/resources/article.rb +12 -0
- data/lib/shopify_api/resources/asset.rb +95 -0
- data/lib/shopify_api/resources/base.rb +6 -0
- data/lib/shopify_api/resources/billing_address.rb +4 -0
- data/lib/shopify_api/resources/blog.rb +10 -0
- data/lib/shopify_api/resources/collect.rb +5 -0
- data/lib/shopify_api/resources/comment.rb +13 -0
- data/lib/shopify_api/resources/country.rb +4 -0
- data/lib/shopify_api/resources/custom_collection.rb +19 -0
- data/lib/shopify_api/resources/customer.rb +4 -0
- data/lib/shopify_api/resources/customer_group.rb +4 -0
- data/lib/shopify_api/resources/event.rb +10 -0
- data/lib/shopify_api/resources/fulfillment.rb +5 -0
- data/lib/shopify_api/resources/image.rb +16 -0
- data/lib/shopify_api/resources/line_item.rb +4 -0
- data/lib/shopify_api/resources/metafield.rb +15 -0
- data/lib/shopify_api/resources/note_attribute.rb +4 -0
- data/lib/shopify_api/resources/option.rb +4 -0
- data/lib/shopify_api/resources/order.rb +25 -0
- data/lib/shopify_api/resources/page.rb +6 -0
- data/lib/shopify_api/resources/payment_details.rb +4 -0
- data/lib/shopify_api/resources/product.rb +33 -0
- data/lib/shopify_api/resources/product_search_engine.rb +4 -0
- data/lib/shopify_api/resources/province.rb +5 -0
- data/lib/shopify_api/resources/receipt.rb +4 -0
- data/lib/shopify_api/resources/recurring_application_charge.rb +23 -0
- data/lib/shopify_api/resources/redirect.rb +4 -0
- data/lib/shopify_api/resources/rule.rb +4 -0
- data/lib/shopify_api/resources/script_tag.rb +4 -0
- data/lib/shopify_api/resources/shipping_address.rb +4 -0
- data/lib/shopify_api/resources/shipping_line.rb +4 -0
- data/lib/shopify_api/resources/shop.rb +23 -0
- data/lib/shopify_api/resources/smart_collection.rb +10 -0
- data/lib/shopify_api/resources/tax_line.rb +4 -0
- data/lib/shopify_api/resources/theme.rb +4 -0
- data/lib/shopify_api/resources/transaction.rb +5 -0
- data/lib/shopify_api/resources/variant.rb +11 -0
- data/lib/shopify_api/resources/webhook.rb +4 -0
- data/lib/shopify_api/session.rb +165 -0
- data/shopify_api.gemspec +13 -92
- data/test/cli_test.rb +109 -0
- data/test/limits_test.rb +37 -0
- data/test/shopify_api_test.rb +13 -1
- metadata +76 -82
data/lib/shopify_api/cli.rb
CHANGED
@@ -8,10 +8,6 @@ module ShopifyAPI
|
|
8
8
|
class ConfigFileError < StandardError
|
9
9
|
end
|
10
10
|
|
11
|
-
tasks.keys.abbrev.each do |shortcut, command|
|
12
|
-
map shortcut => command.to_sym
|
13
|
-
end
|
14
|
-
|
15
11
|
desc "list", "list available connections"
|
16
12
|
def list
|
17
13
|
available_connections.each do |c|
|
@@ -55,7 +51,7 @@ module ShopifyAPI
|
|
55
51
|
file = config_file(connection)
|
56
52
|
if File.exist?(file)
|
57
53
|
if ENV['EDITOR'].present?
|
58
|
-
|
54
|
+
system(ENV['EDITOR'], file)
|
59
55
|
else
|
60
56
|
puts "Please set an editor in the EDITOR environment variable"
|
61
57
|
end
|
@@ -84,7 +80,7 @@ module ShopifyAPI
|
|
84
80
|
remove_file(default_symlink)
|
85
81
|
`ln -s #{target} #{default_symlink}`
|
86
82
|
else
|
87
|
-
no_config_file_error(
|
83
|
+
no_config_file_error(target)
|
88
84
|
end
|
89
85
|
end
|
90
86
|
if File.exist?(default_symlink)
|
@@ -107,7 +103,11 @@ module ShopifyAPI
|
|
107
103
|
ARGV.clear
|
108
104
|
IRB.start
|
109
105
|
end
|
110
|
-
|
106
|
+
|
107
|
+
tasks.keys.abbrev.each do |shortcut, command|
|
108
|
+
map shortcut => command.to_sym
|
109
|
+
end
|
110
|
+
|
111
111
|
private
|
112
112
|
|
113
113
|
def shop_config_dir
|
@@ -155,7 +155,7 @@ module ShopifyAPI
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def no_config_file_error(filename)
|
158
|
-
raise ConfigFileError, "There is no config file at #{
|
158
|
+
raise ConfigFileError, "There is no config file at #{filename}"
|
159
159
|
end
|
160
160
|
end
|
161
|
-
end
|
161
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
class Base
|
3
|
+
def encode(options = {})
|
4
|
+
same = dup
|
5
|
+
same.attributes = {self.class.element_name => same.attributes} if self.class.format.extension == 'json'
|
6
|
+
|
7
|
+
same.send("to_#{self.class.format.extension}", options)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Formats
|
12
|
+
module JsonFormat
|
13
|
+
def decode(json)
|
14
|
+
data = ActiveSupport::JSON.decode(json)
|
15
|
+
if data.is_a?(Hash) && data.keys.size == 1
|
16
|
+
data.values.first
|
17
|
+
else
|
18
|
+
data
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module ShopifyAPI
|
2
|
+
module Limits
|
3
|
+
def self.included(klass)
|
4
|
+
klass.send(:extend, ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
# Takes form num_requests_executed/max_requests
|
10
|
+
# Eg: 101/3000
|
11
|
+
CREDIT_LIMIT_HEADER_PARAM = {
|
12
|
+
:global => 'http_x_shopify_api_call_limit',
|
13
|
+
:shop => 'http_x_shopify_shop_api_call_limit'
|
14
|
+
}
|
15
|
+
|
16
|
+
##
|
17
|
+
# How many more API calls can I make?
|
18
|
+
# @return {Integer}
|
19
|
+
#
|
20
|
+
def credit_left
|
21
|
+
shop = credit_limit(:shop) - credit_used(:shop)
|
22
|
+
global = credit_limit(:global) - credit_used(:global)
|
23
|
+
shop < global ? shop : global
|
24
|
+
end
|
25
|
+
alias_method :available_calls, :credit_left
|
26
|
+
|
27
|
+
##
|
28
|
+
# Have I reached my API call limit?
|
29
|
+
# @return {Boolean}
|
30
|
+
#
|
31
|
+
def credit_maxed?
|
32
|
+
credit_left <= 0
|
33
|
+
end
|
34
|
+
alias_method :maxed?, :credit_maxed?
|
35
|
+
|
36
|
+
##
|
37
|
+
# How many total API calls can I make?
|
38
|
+
# NOTE: subtracting 1 from credit_limit because I think ShopifyAPI cuts off at 299/2999 or shop/global limits.
|
39
|
+
# @param {Symbol} scope [:shop|:global]
|
40
|
+
# @return {Integer}
|
41
|
+
#
|
42
|
+
def credit_limit(scope=:shop)
|
43
|
+
@api_credit_limit ||= {}
|
44
|
+
@api_credit_limit[scope] ||= api_credit_limit_param(scope).pop.to_i - 1
|
45
|
+
end
|
46
|
+
alias_method :call_limit, :credit_limit
|
47
|
+
|
48
|
+
##
|
49
|
+
# How many API calls have I made?
|
50
|
+
# @param {Symbol} scope [:shop|:global]
|
51
|
+
# @return {Integer}
|
52
|
+
#
|
53
|
+
def credit_used(scope=:shop)
|
54
|
+
api_credit_limit_param(scope).shift.to_i
|
55
|
+
end
|
56
|
+
alias_method :call_count, :credit_used
|
57
|
+
|
58
|
+
##
|
59
|
+
# @return {HTTPResonse}
|
60
|
+
#
|
61
|
+
def response
|
62
|
+
Shop.current unless ShopifyAPI::Base.connection.response
|
63
|
+
ShopifyAPI::Base.connection.response
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
##
|
69
|
+
# @return {Array}
|
70
|
+
#
|
71
|
+
def api_credit_limit_param(scope)
|
72
|
+
response[CREDIT_LIMIT_HEADER_PARAM[scope]].split('/')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ShopifyAPI
|
2
|
+
module Metafields
|
3
|
+
def metafields
|
4
|
+
Metafield.find(:all, :params => {:resource => self.class.collection_name, :resource_id => id})
|
5
|
+
end
|
6
|
+
|
7
|
+
def add_metafield(metafield)
|
8
|
+
raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
|
9
|
+
|
10
|
+
metafield.prefix_options = {
|
11
|
+
:resource => self.class.collection_name,
|
12
|
+
:resource_id => id
|
13
|
+
}
|
14
|
+
metafield.save
|
15
|
+
metafield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'shopify_api/resources/base'
|
2
|
+
require 'shopify_api/resources/address'
|
3
|
+
require 'shopify_api/resources/application_charge'
|
4
|
+
require 'shopify_api/resources/article'
|
5
|
+
require 'shopify_api/resources/asset'
|
6
|
+
require 'shopify_api/resources/billing_address'
|
7
|
+
require 'shopify_api/resources/blog'
|
8
|
+
require 'shopify_api/resources/collect'
|
9
|
+
require 'shopify_api/resources/comment'
|
10
|
+
require 'shopify_api/resources/country'
|
11
|
+
require 'shopify_api/resources/custom_collection'
|
12
|
+
require 'shopify_api/resources/customer_group'
|
13
|
+
require 'shopify_api/resources/customer'
|
14
|
+
require 'shopify_api/resources/event'
|
15
|
+
require 'shopify_api/resources/fulfillment'
|
16
|
+
require 'shopify_api/resources/image'
|
17
|
+
require 'shopify_api/resources/line_item'
|
18
|
+
require 'shopify_api/resources/metafield'
|
19
|
+
require 'shopify_api/resources/note_attribute'
|
20
|
+
require 'shopify_api/resources/option'
|
21
|
+
require 'shopify_api/resources/order'
|
22
|
+
require 'shopify_api/resources/page'
|
23
|
+
require 'shopify_api/resources/payment_details'
|
24
|
+
require 'shopify_api/resources/product'
|
25
|
+
require 'shopify_api/resources/product_search_engine'
|
26
|
+
require 'shopify_api/resources/province'
|
27
|
+
require 'shopify_api/resources/receipt'
|
28
|
+
require 'shopify_api/resources/recurring_application_charge'
|
29
|
+
require 'shopify_api/resources/redirect'
|
30
|
+
require 'shopify_api/resources/rule'
|
31
|
+
require 'shopify_api/resources/script_tag'
|
32
|
+
require 'shopify_api/resources/shipping_address'
|
33
|
+
require 'shopify_api/resources/shipping_line'
|
34
|
+
require 'shopify_api/resources/shop'
|
35
|
+
require 'shopify_api/resources/smart_collection'
|
36
|
+
require 'shopify_api/resources/tax_line'
|
37
|
+
require 'shopify_api/resources/theme'
|
38
|
+
require 'shopify_api/resources/transaction'
|
39
|
+
require 'shopify_api/resources/variant'
|
40
|
+
require 'shopify_api/resources/webhook'
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module ShopifyAPI
|
2
|
+
# Assets represent the files that comprise your theme.
|
3
|
+
# There are different buckets which hold different kinds
|
4
|
+
# of assets, each corresponding to one of the folders
|
5
|
+
# within a theme's zip file: "layout", "templates",
|
6
|
+
# "snippets", "assets", and "config". The full key of an
|
7
|
+
# asset always starts with the bucket name, and the path
|
8
|
+
# separator is a forward slash, like layout/theme.liquid
|
9
|
+
# or assets/bg-body.gif.
|
10
|
+
#
|
11
|
+
# Initialize with a key:
|
12
|
+
# asset = ShopifyAPI::Asset.new(:key => 'assets/special.css', :theme_id => 12345)
|
13
|
+
#
|
14
|
+
# Find by key:
|
15
|
+
# asset = ShopifyAPI::Asset.find('assets/image.png', :params => {:theme_id => 12345})
|
16
|
+
#
|
17
|
+
# Get the text or binary value:
|
18
|
+
# asset.value # decodes from attachment attribute if necessary
|
19
|
+
#
|
20
|
+
# You can provide new data for assets in a few different ways:
|
21
|
+
#
|
22
|
+
# * assign text data for the value directly:
|
23
|
+
# asset.value = "div.special {color:red;}"
|
24
|
+
#
|
25
|
+
# * provide binary data for the value:
|
26
|
+
# asset.attach(File.read('image.png'))
|
27
|
+
#
|
28
|
+
# * set a URL from which Shopify will fetch the value:
|
29
|
+
# asset.src = "http://mysite.com/image.png"
|
30
|
+
#
|
31
|
+
# * set a source key of another of your assets from which
|
32
|
+
# the value will be copied:
|
33
|
+
# asset.source_key = "assets/another_image.png"
|
34
|
+
class Asset < Base
|
35
|
+
self.primary_key = 'key'
|
36
|
+
self.prefix = "/admin/themes/:theme_id/"
|
37
|
+
|
38
|
+
def self.prefix(options={})
|
39
|
+
options[:theme_id].nil? ? "/admin/" : "/admin/themes/#{options[:theme_id]}/"
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.element_path(id, prefix_options = {}, query_options = nil) #:nodoc:
|
43
|
+
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
|
44
|
+
"#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
|
45
|
+
end
|
46
|
+
|
47
|
+
# find an asset by key:
|
48
|
+
# ShopifyAPI::Asset.find('layout/theme.liquid', :params => {:theme_id => 99})
|
49
|
+
def self.find(*args)
|
50
|
+
if args[0].is_a?(Symbol)
|
51
|
+
super
|
52
|
+
else
|
53
|
+
params = {:asset => {:key => args[0]}}
|
54
|
+
params = params.merge(args[1][:params]) if args[1] && args[1][:params]
|
55
|
+
path_prefix = params[:theme_id] ? "/admin/themes/#{params[:theme_id]}" : "/admin"
|
56
|
+
find(:one, :from => "#{path_prefix}/assets.#{format.extension}", :params => params)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# For text assets, Shopify returns the data in the 'value' attribute.
|
61
|
+
# For binary assets, the data is base-64-encoded and returned in the
|
62
|
+
# 'attachment' attribute. This accessor returns the data in both cases.
|
63
|
+
def value
|
64
|
+
attributes['value'] ||
|
65
|
+
(attributes['attachment'] ? Base64.decode64(attributes['attachment']) : nil)
|
66
|
+
end
|
67
|
+
|
68
|
+
def attach(data)
|
69
|
+
self.attachment = Base64.encode64(data)
|
70
|
+
end
|
71
|
+
|
72
|
+
def destroy
|
73
|
+
connection.delete(element_path(prefix_options.merge(:asset => {:key => key})), self.class.headers)
|
74
|
+
end
|
75
|
+
|
76
|
+
def new?
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
def method_missing(method_symbol, *arguments) #:nodoc:
|
81
|
+
if %w{value= attachment= src= source_key=}.include?(method_symbol)
|
82
|
+
wipe_value_attributes
|
83
|
+
end
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def wipe_value_attributes
|
90
|
+
%w{value attachment src source_key}.each do |attr|
|
91
|
+
attributes.delete(attr)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ShopifyAPI
|
2
|
+
class Comment < Base
|
3
|
+
def remove; load_attributes_from_response(post(:remove, {}, only_id)); end
|
4
|
+
def spam; load_attributes_from_response(post(:spam, {}, only_id)); end
|
5
|
+
def approve; load_attributes_from_response(post(:approve, {}, only_id)); end
|
6
|
+
def restore; load_attributes_from_response(post(:restore, {}, only_id)); end
|
7
|
+
def not_spam; load_attributes_from_response(post(:not_spam, {}, only_id)); end
|
8
|
+
|
9
|
+
def only_id
|
10
|
+
encode(:only => :id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ShopifyAPI
|
2
|
+
class CustomCollection < Base
|
3
|
+
include Events
|
4
|
+
include Metafields
|
5
|
+
|
6
|
+
def products
|
7
|
+
Product.find(:all, :params => {:collection_id => self.id})
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_product(product)
|
11
|
+
Collect.create(:collection_id => self.id, :product_id => product.id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove_product(product)
|
15
|
+
collect = Collect.find(:first, :params => {:collection_id => self.id, :product_id => product.id})
|
16
|
+
collect.destroy if collect
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|