shopify_api 1.2.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/CHANGELOG
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== Version 2.0.0
|
2
|
+
|
3
|
+
* Bump to 2.0.0 as this release breaks Rails 2 compatibility; we're now officially only supporting Rails 3. Rails 2 devs can follow the rails2 tag in this repo to know where we broke off
|
4
|
+
* Refactored resources into their own source files
|
5
|
+
* Added API limits functionality
|
6
|
+
* Patched ActiveResource issue with roots in JSON
|
7
|
+
* Added pending, cancelled, accepted, and declined convenience methods to ShopifyAPI::RecurringApplicationCharge
|
8
|
+
* ShopifyAPI::Session#temp now available as a convenience method to support temporarily switching to other shops when making calls
|
9
|
+
* Fixes to `shopify console` CLI tool
|
10
|
+
|
1
11
|
== Version 1.2.5
|
2
12
|
|
3
13
|
* Fix for Article#comments
|
data/README.rdoc
CHANGED
@@ -50,6 +50,11 @@ ShopifyAPI uses ActiveResource to communicate with the REST web service. ActiveR
|
|
50
50
|
shop = ShopifyAPI::Shop.current
|
51
51
|
latest_orders = ShopifyAPI::Order.find(:all)
|
52
52
|
|
53
|
+
Alternatively, you can use #temp to initialize a Session and execute a command which also handles temporarily setting ActiveResource::Base.site:
|
54
|
+
|
55
|
+
latest_orders = ShopifyAPI::Session.temp("yourshopname.myshopify.com", token) { ShopifyAPI::Order.find(:all) }
|
56
|
+
|
57
|
+
|
53
58
|
== Copyright
|
54
59
|
|
55
|
-
Copyright (c)
|
60
|
+
Copyright (c) 2011 "JadedPixel inc.". See LICENSE for details.
|
data/RELEASING
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Releasing ShopifyAPI
|
2
|
+
|
3
|
+
1. Check the Semantic Versioning page for info on how to version the new release: http://semver.org
|
4
|
+
2. Update the version of ShopifyAPI in shopify_api.gemspec
|
5
|
+
3. Add a CHANGELOG entry for the new release with the date
|
6
|
+
4. Commit the changes with a commit message like "Packaging for release X.Y.Z"
|
7
|
+
5. Tag the release with the version (Leave REV blank for HEAD or provide a SHA)
|
8
|
+
$ git tag vX.Y.Z REV
|
9
|
+
6. Push out the changes
|
10
|
+
$ git push
|
11
|
+
7. Push out the tags
|
12
|
+
$ git push --tags
|
13
|
+
8. Build the new .gem from the updated .gemspec
|
14
|
+
$ gem build shopify_api.gemspec
|
15
|
+
9. Publish the Gem to gemcutter
|
16
|
+
$ gem push shopify_api-X.Y.Z.gem
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_support/core_ext/module/aliasing'
|
2
|
+
|
3
|
+
module ActiveResource
|
4
|
+
class Connection
|
5
|
+
|
6
|
+
attr_reader :response
|
7
|
+
|
8
|
+
def handle_response_with_response_capture(response)
|
9
|
+
@response = handle_response_without_response_capture(response)
|
10
|
+
end
|
11
|
+
|
12
|
+
alias_method_chain :handle_response, :response_capture
|
13
|
+
# alias_method :handle_response_without_instance, :handle_response
|
14
|
+
# alias_method :handle_response, :handle_response_with_instance
|
15
|
+
end
|
16
|
+
end
|
data/lib/shopify_api.rb
CHANGED
@@ -1,540 +1,19 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
1
3
|
require 'active_resource'
|
2
4
|
require 'active_support/core_ext/class/attribute_accessors'
|
3
5
|
require 'digest/md5'
|
4
6
|
require 'base64'
|
7
|
+
require 'active_resource/connection_ext'
|
8
|
+
require 'shopify_api/limits'
|
9
|
+
require 'shopify_api/json_format'
|
5
10
|
|
6
11
|
module ShopifyAPI
|
7
|
-
|
8
|
-
EVENT_ENABLED_CLASSES = %w( Order Product CustomCollection SmartCollection Page Blog Article )
|
9
|
-
|
10
|
-
module Countable
|
11
|
-
def count(options = {})
|
12
|
-
Integer(get(:count, options))
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
module Metafields
|
17
|
-
def metafields
|
18
|
-
Metafield.find(:all, :params => {:resource => self.class.collection_name, :resource_id => id})
|
19
|
-
end
|
20
|
-
|
21
|
-
def add_metafield(metafield)
|
22
|
-
raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
|
23
|
-
|
24
|
-
metafield.prefix_options = {
|
25
|
-
:resource => self.class.collection_name,
|
26
|
-
:resource_id => id
|
27
|
-
}
|
28
|
-
metafield.save
|
29
|
-
metafield
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
module Events
|
34
|
-
def events
|
35
|
-
Event.find(:all, :params => {:resource => self.class.collection_name, :resource_id => id})
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
#
|
40
|
-
# The Shopify API authenticates each call via HTTP Authentication, using
|
41
|
-
# * the application's API key as the username, and
|
42
|
-
# * a hex digest of the application's shared secret and an
|
43
|
-
# authentication token as the password.
|
44
|
-
#
|
45
|
-
# Generation & acquisition of the beforementioned looks like this:
|
46
|
-
#
|
47
|
-
# 0. Developer (that's you) registers Application (and provides a
|
48
|
-
# callback url) and receives an API key and a shared secret
|
49
|
-
#
|
50
|
-
# 1. User visits Application and are told they need to authenticate the
|
51
|
-
# application first for read/write permission to their data (needs to
|
52
|
-
# happen only once). User is asked for their shop url.
|
53
|
-
#
|
54
|
-
# 2. Application redirects to Shopify : GET <user's shop url>/admin/api/auth?api_key=<API key>
|
55
|
-
# (See Session#create_permission_url)
|
56
|
-
#
|
57
|
-
# 3. User logs-in to Shopify, approves application permission request
|
58
|
-
#
|
59
|
-
# 4. Shopify redirects to the Application's callback url (provided during
|
60
|
-
# registration), including the shop's name, and an authentication token in the parameters:
|
61
|
-
# GET client.com/customers?shop=snake-oil.myshopify.com&t=a94a110d86d2452eb3e2af4cfb8a3828
|
62
|
-
#
|
63
|
-
# 5. Authentication password computed using the shared secret and the
|
64
|
-
# authentication token (see Session#computed_password)
|
65
|
-
#
|
66
|
-
# 6. Profit!
|
67
|
-
# (API calls can now authenticate through HTTP using the API key, and
|
68
|
-
# computed password)
|
69
|
-
#
|
70
|
-
# LoginController and ShopifyLoginProtection use the Session class to set Shopify::Base.site
|
71
|
-
# so that all API calls are authorized transparently and end up just looking like this:
|
72
|
-
#
|
73
|
-
# # get 3 products
|
74
|
-
# @products = ShopifyAPI::Product.find(:all, :params => {:limit => 3})
|
75
|
-
#
|
76
|
-
# # get latest 3 orders
|
77
|
-
# @orders = ShopifyAPI::Order.find(:all, :params => {:limit => 3, :order => "created_at DESC" })
|
78
|
-
#
|
79
|
-
# As an example of what your LoginController should look like, take a look
|
80
|
-
# at the following:
|
81
|
-
#
|
82
|
-
# class LoginController < ApplicationController
|
83
|
-
# def index
|
84
|
-
# # Ask user for their #{shop}.myshopify.com address
|
85
|
-
# end
|
86
|
-
#
|
87
|
-
# def authenticate
|
88
|
-
# redirect_to ShopifyAPI::Session.new(params[:shop]).create_permission_url
|
89
|
-
# end
|
90
|
-
#
|
91
|
-
# # Shopify redirects the logged-in user back to this action along with
|
92
|
-
# # the authorization token t.
|
93
|
-
# #
|
94
|
-
# # This token is later combined with the developer's shared secret to form
|
95
|
-
# # the password used to call API methods.
|
96
|
-
# def finalize
|
97
|
-
# shopify_session = ShopifyAPI::Session.new(params[:shop], params[:t])
|
98
|
-
# if shopify_session.valid?
|
99
|
-
# session[:shopify] = shopify_session
|
100
|
-
# flash[:notice] = "Logged in to shopify store."
|
101
|
-
#
|
102
|
-
# return_address = session[:return_to] || '/home'
|
103
|
-
# session[:return_to] = nil
|
104
|
-
# redirect_to return_address
|
105
|
-
# else
|
106
|
-
# flash[:error] = "Could not log in to Shopify store."
|
107
|
-
# redirect_to :action => 'index'
|
108
|
-
# end
|
109
|
-
# end
|
110
|
-
#
|
111
|
-
# def logout
|
112
|
-
# session[:shopify] = nil
|
113
|
-
# flash[:notice] = "Successfully logged out."
|
114
|
-
#
|
115
|
-
# redirect_to :action => 'index'
|
116
|
-
# end
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
class Session
|
120
|
-
cattr_accessor :api_key
|
121
|
-
cattr_accessor :secret
|
122
|
-
cattr_accessor :protocol
|
123
|
-
self.protocol = 'https'
|
124
|
-
|
125
|
-
attr_accessor :url, :token, :name
|
126
|
-
|
127
|
-
def self.setup(params)
|
128
|
-
params.each { |k,value| send("#{k}=", value) }
|
129
|
-
end
|
130
|
-
|
131
|
-
def initialize(url, token = nil, params = nil)
|
132
|
-
self.url, self.token = url, token
|
133
|
-
|
134
|
-
if params
|
135
|
-
unless self.class.validate_signature(params) && params[:timestamp].to_i > 24.hours.ago.utc.to_i
|
136
|
-
raise "Invalid Signature: Possible malicious login"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
self.class.prepare_url(self.url)
|
141
|
-
end
|
142
|
-
|
143
|
-
def shop
|
144
|
-
Shop.current
|
145
|
-
end
|
146
|
-
|
147
|
-
def create_permission_url
|
148
|
-
return nil if url.blank? || api_key.blank?
|
149
|
-
"http://#{url}/admin/api/auth?api_key=#{api_key}"
|
150
|
-
end
|
151
|
-
|
152
|
-
# Used by ActiveResource::Base to make all non-authentication API calls
|
153
|
-
#
|
154
|
-
# (ShopifyAPI::Base.site set in ShopifyLoginProtection#shopify_session)
|
155
|
-
def site
|
156
|
-
"#{protocol}://#{api_key}:#{computed_password}@#{url}/admin"
|
157
|
-
end
|
158
|
-
|
159
|
-
def valid?
|
160
|
-
url.present? && token.present?
|
161
|
-
end
|
162
|
-
|
163
|
-
private
|
164
|
-
|
165
|
-
# The secret is computed by taking the shared_secret which we got when
|
166
|
-
# registring this third party application and concating the request_to it,
|
167
|
-
# and then calculating a MD5 hexdigest.
|
168
|
-
def computed_password
|
169
|
-
Digest::MD5.hexdigest(secret + token.to_s)
|
170
|
-
end
|
171
|
-
|
172
|
-
def self.prepare_url(url)
|
173
|
-
return nil if url.blank?
|
174
|
-
url.gsub!(/https?:\/\//, '') # remove http:// or https://
|
175
|
-
url.concat(".myshopify.com") unless url.include?('.') # extend url to myshopify.com if no host is given
|
176
|
-
end
|
177
|
-
|
178
|
-
def self.validate_signature(params)
|
179
|
-
return false unless signature = params[:signature]
|
180
|
-
|
181
|
-
sorted_params = params.except(:signature, :action, :controller).collect{|k,v|"#{k}=#{v}"}.sort.join
|
182
|
-
Digest::MD5.hexdigest(secret + sorted_params) == signature
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
class Base < ActiveResource::Base
|
187
|
-
extend Countable
|
188
|
-
end
|
189
|
-
|
190
|
-
# Shop object. Use Shop.current to receive
|
191
|
-
# the shop.
|
192
|
-
class Shop < Base
|
193
|
-
def self.current
|
194
|
-
find(:one, :from => "/admin/shop.#{format.extension}")
|
195
|
-
end
|
196
|
-
|
197
|
-
def metafields
|
198
|
-
Metafield.find(:all)
|
199
|
-
end
|
200
|
-
|
201
|
-
def add_metafield(metafield)
|
202
|
-
raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
|
203
|
-
metafield.save
|
204
|
-
metafield
|
205
|
-
end
|
206
|
-
|
207
|
-
def events
|
208
|
-
Event.find(:all)
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
# Custom collection
|
213
|
-
#
|
214
|
-
class CustomCollection < Base
|
215
|
-
def products
|
216
|
-
Product.find(:all, :params => {:collection_id => self.id})
|
217
|
-
end
|
218
|
-
|
219
|
-
def add_product(product)
|
220
|
-
Collect.create(:collection_id => self.id, :product_id => product.id)
|
221
|
-
end
|
222
|
-
|
223
|
-
def remove_product(product)
|
224
|
-
collect = Collect.find(:first, :params => {:collection_id => self.id, :product_id => product.id})
|
225
|
-
collect.destroy if collect
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
class SmartCollection < Base
|
230
|
-
def products
|
231
|
-
Product.find(:all, :params => {:collection_id => self.id})
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
# For adding/removing products from custom collections
|
236
|
-
class Collect < Base
|
237
|
-
end
|
238
|
-
|
239
|
-
class ShippingAddress < Base
|
240
|
-
end
|
241
|
-
|
242
|
-
class BillingAddress < Base
|
243
|
-
end
|
244
|
-
|
245
|
-
class LineItem < Base
|
246
|
-
end
|
247
|
-
|
248
|
-
class ShippingLine < Base
|
249
|
-
end
|
250
|
-
|
251
|
-
class NoteAttribute < Base
|
252
|
-
end
|
253
|
-
|
254
|
-
class Order < Base
|
255
|
-
def close; load_attributes_from_response(post(:close, {}, only_id)); end
|
256
|
-
def open; load_attributes_from_response(post(:open, {}, only_id)); end
|
257
|
-
|
258
|
-
def cancel(options = {})
|
259
|
-
load_attributes_from_response(post(:cancel, options, only_id))
|
260
|
-
end
|
261
|
-
|
262
|
-
def transactions
|
263
|
-
Transaction.find(:all, :params => { :order_id => id })
|
264
|
-
end
|
265
|
-
|
266
|
-
def capture(amount = "")
|
267
|
-
Transaction.create(:amount => amount, :kind => "capture", :order_id => id)
|
268
|
-
end
|
269
|
-
|
270
|
-
def only_id
|
271
|
-
encode(:only => :id, :include => [], :methods => [], :fields => [])
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
class Product < Base
|
276
|
-
|
277
|
-
# Share all items of this store with the
|
278
|
-
# shopify marketplace
|
279
|
-
def self.share; post :share; end
|
280
|
-
def self.unshare; delete :share; end
|
281
|
-
|
282
|
-
# compute the price range
|
283
|
-
def price_range
|
284
|
-
prices = variants.collect(&:price)
|
285
|
-
format = "%0.2f"
|
286
|
-
if prices.min != prices.max
|
287
|
-
"#{format % prices.min} - #{format % prices.max}"
|
288
|
-
else
|
289
|
-
format % prices.min
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
def collections
|
294
|
-
CustomCollection.find(:all, :params => {:product_id => self.id})
|
295
|
-
end
|
296
|
-
|
297
|
-
def smart_collections
|
298
|
-
SmartCollection.find(:all, :params => {:product_id => self.id})
|
299
|
-
end
|
300
|
-
|
301
|
-
def add_to_collection(collection)
|
302
|
-
collection.add_product(self)
|
303
|
-
end
|
304
|
-
|
305
|
-
def remove_from_collection(collection)
|
306
|
-
collection.remove_product(self)
|
307
|
-
end
|
308
|
-
end
|
309
|
-
|
310
|
-
class Variant < Base
|
311
|
-
self.prefix = "/admin/products/:product_id/"
|
312
|
-
|
313
|
-
def self.prefix(options={})
|
314
|
-
options[:product_id].nil? ? "/admin/" : "/admin/products/#{options[:product_id]}/"
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
class Image < Base
|
319
|
-
self.prefix = "/admin/products/:product_id/"
|
320
|
-
|
321
|
-
# generate a method for each possible image variant
|
322
|
-
[:pico, :icon, :thumb, :small, :compact, :medium, :large, :grande, :original].each do |m|
|
323
|
-
reg_exp_match = "/\\1_#{m}.\\2"
|
324
|
-
define_method(m) { src.gsub(/\/(.*)\.(\w{2,4})/, reg_exp_match) }
|
325
|
-
end
|
326
|
-
|
327
|
-
def attach_image(data, filename = nil)
|
328
|
-
attributes['attachment'] = Base64.encode64(data)
|
329
|
-
attributes['filename'] = filename unless filename.nil?
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
|
-
class Transaction < Base
|
334
|
-
self.prefix = "/admin/orders/:order_id/"
|
335
|
-
end
|
336
|
-
|
337
|
-
class Fulfillment < Base
|
338
|
-
self.prefix = "/admin/orders/:order_id/"
|
339
|
-
end
|
340
|
-
|
341
|
-
class Country < Base
|
342
|
-
end
|
343
|
-
|
344
|
-
class Page < Base
|
345
|
-
end
|
346
|
-
|
347
|
-
class Blog < Base
|
348
|
-
def articles
|
349
|
-
Article.find(:all, :params => { :blog_id => id })
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
class Article < Base
|
354
|
-
self.prefix = "/admin/blogs/:blog_id/"
|
355
|
-
|
356
|
-
def comments
|
357
|
-
Comment.find(:all, :params => { :article_id => id })
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
class Metafield < Base
|
362
|
-
self.prefix = "/admin/:resource/:resource_id/"
|
363
|
-
|
364
|
-
# Hack to allow both Shop and other Metafields in through the same AR class
|
365
|
-
def self.prefix(options={})
|
366
|
-
options[:resource].nil? ? "/admin/" : "/admin/#{options[:resource]}/#{options[:resource_id]}/"
|
367
|
-
end
|
368
|
-
|
369
|
-
def value
|
370
|
-
return if attributes["value"].nil?
|
371
|
-
attributes["value_type"] == "integer" ? attributes["value"].to_i : attributes["value"]
|
372
|
-
end
|
373
|
-
|
374
|
-
end
|
375
|
-
|
376
|
-
class Comment < Base
|
377
|
-
def remove; load_attributes_from_response(post(:remove, {}, only_id)); end
|
378
|
-
def ham; load_attributes_from_response(post(:ham, {}, only_id)); end
|
379
|
-
def spam; load_attributes_from_response(post(:spam, {}, only_id)); end
|
380
|
-
def approve; load_attributes_from_response(post(:approve, {}, only_id)); end
|
381
|
-
def restore; load_attributes_from_response(post(:restore, {}, only_id)); end
|
382
|
-
def not_spam; load_attributes_from_response(post(:not_spam, {}, only_id)); end
|
383
|
-
|
384
|
-
def only_id
|
385
|
-
encode(:only => :id)
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
class Province < Base
|
390
|
-
self.prefix = "/admin/countries/:country_id/"
|
391
|
-
end
|
392
|
-
|
393
|
-
class Redirect < Base
|
394
|
-
end
|
395
|
-
|
396
|
-
class Webhook < Base
|
397
|
-
end
|
398
|
-
|
399
|
-
class Event < Base
|
400
|
-
self.prefix = "/admin/:resource/:resource_id/"
|
401
|
-
|
402
|
-
# Hack to allow both Shop and other Events in through the same AR class
|
403
|
-
def self.prefix(options={})
|
404
|
-
options[:resource].nil? ? "/admin/" : "/admin/#{options[:resource]}/#{options[:resource_id]}/"
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
class Customer < Base
|
409
|
-
end
|
410
|
-
|
411
|
-
class CustomerGroup < Base
|
412
|
-
end
|
413
|
-
|
414
|
-
# Assets represent the files that comprise your theme.
|
415
|
-
# There are different buckets which hold different kinds
|
416
|
-
# of assets, each corresponding to one of the folders
|
417
|
-
# within a theme's zip file: layout, templates, and
|
418
|
-
# assets. The full key of an asset always starts with the
|
419
|
-
# bucket name, and the path separator is a forward slash,
|
420
|
-
# like layout/theme.liquid or assets/bg-body.gif.
|
421
|
-
#
|
422
|
-
# Initialize with a key:
|
423
|
-
# asset = ShopifyAPI::Asset.new(:key => 'assets/special.css')
|
424
|
-
#
|
425
|
-
# Find by key:
|
426
|
-
# asset = ShopifyAPI::Asset.find('assets/image.png')
|
427
|
-
#
|
428
|
-
# Get the text or binary value:
|
429
|
-
# asset.value # decodes from attachment attribute if necessary
|
430
|
-
#
|
431
|
-
# You can provide new data for assets in a few different ways:
|
432
|
-
#
|
433
|
-
# * assign text data for the value directly:
|
434
|
-
# asset.value = "div.special {color:red;}"
|
435
|
-
#
|
436
|
-
# * provide binary data for the value:
|
437
|
-
# asset.attach(File.read('image.png'))
|
438
|
-
#
|
439
|
-
# * set a URL from which Shopify will fetch the value:
|
440
|
-
# asset.src = "http://mysite.com/image.png"
|
441
|
-
#
|
442
|
-
# * set a source key of another of your assets from which
|
443
|
-
# the value will be copied:
|
444
|
-
# asset.source_key = "assets/another_image.png"
|
445
|
-
class Asset < Base
|
446
|
-
self.primary_key = 'key'
|
447
|
-
|
448
|
-
# find an asset by key:
|
449
|
-
# ShopifyAPI::Asset.find('layout/theme.liquid')
|
450
|
-
def self.find(*args)
|
451
|
-
if args[0].is_a?(Symbol)
|
452
|
-
super
|
453
|
-
else
|
454
|
-
params = {:asset => {:key => args[0]}}
|
455
|
-
params = params.merge(args[1][:params]) if args[1] && args[1][:params]
|
456
|
-
find(:one, :from => "/admin/assets.#{format.extension}", :params => params)
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
# For text assets, Shopify returns the data in the 'value' attribute.
|
461
|
-
# For binary assets, the data is base-64-encoded and returned in the
|
462
|
-
# 'attachment' attribute. This accessor returns the data in both cases.
|
463
|
-
def value
|
464
|
-
attributes['value'] ||
|
465
|
-
(attributes['attachment'] ? Base64.decode64(attributes['attachment']) : nil)
|
466
|
-
end
|
467
|
-
|
468
|
-
def attach(data)
|
469
|
-
self.attachment = Base64.encode64(data)
|
470
|
-
end
|
471
|
-
|
472
|
-
def destroy #:nodoc:
|
473
|
-
connection.delete(element_path(:asset => {:key => key}), self.class.headers)
|
474
|
-
end
|
475
|
-
|
476
|
-
def new? #:nodoc:
|
477
|
-
false
|
478
|
-
end
|
479
|
-
|
480
|
-
def self.element_path(id, prefix_options = {}, query_options = nil) #:nodoc:
|
481
|
-
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
|
482
|
-
"#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
|
483
|
-
end
|
484
|
-
|
485
|
-
def method_missing(method_symbol, *arguments) #:nodoc:
|
486
|
-
if %w{value= attachment= src= source_key=}.include?(method_symbol)
|
487
|
-
wipe_value_attributes
|
488
|
-
end
|
489
|
-
super
|
490
|
-
end
|
491
|
-
|
492
|
-
private
|
493
|
-
|
494
|
-
def wipe_value_attributes
|
495
|
-
%w{value attachment src source_key}.each do |attr|
|
496
|
-
attributes.delete(attr)
|
497
|
-
end
|
498
|
-
end
|
499
|
-
end
|
500
|
-
|
501
|
-
class RecurringApplicationCharge < Base
|
502
|
-
undef_method :test
|
503
|
-
|
504
|
-
def self.current
|
505
|
-
find(:all).find{|charge| charge.status == 'active'}
|
506
|
-
end
|
507
|
-
|
508
|
-
def cancel
|
509
|
-
load_attributes_from_response(self.destroy)
|
510
|
-
end
|
511
|
-
|
512
|
-
def activate
|
513
|
-
load_attributes_from_response(post(:activate))
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
class ApplicationCharge < Base
|
518
|
-
undef_method :test
|
519
|
-
|
520
|
-
def activate
|
521
|
-
load_attributes_from_response(post(:activate))
|
522
|
-
end
|
523
|
-
end
|
524
|
-
|
525
|
-
class ProductSearchEngine < Base
|
526
|
-
end
|
527
|
-
|
528
|
-
class ScriptTag < Base
|
529
|
-
end
|
530
|
-
|
531
|
-
# Include Metafields module in all enabled classes
|
532
|
-
METAFIELD_ENABLED_CLASSES.each do |klass|
|
533
|
-
"ShopifyAPI::#{klass}".constantize.send(:include, Metafields)
|
534
|
-
end
|
535
|
-
|
536
|
-
# Include Events module in all enabled classes
|
537
|
-
EVENT_ENABLED_CLASSES.each do |klass|
|
538
|
-
"ShopifyAPI::#{klass}".constantize.send(:include, Events)
|
539
|
-
end
|
12
|
+
include Limits
|
540
13
|
end
|
14
|
+
|
15
|
+
require 'shopify_api/events'
|
16
|
+
require 'shopify_api/metafields'
|
17
|
+
require 'shopify_api/countable'
|
18
|
+
require 'shopify_api/resources'
|
19
|
+
require 'shopify_api/session'
|