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/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'
|