123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- require 'active_resource'
- require 'active_support/core_ext/class/attribute_accessors'
- require 'digest/md5'
- require 'base64'
- module ShopifyAPI
- METAFIELD_ENABLED_CLASSES = %w( Order Product CustomCollection SmartCollection Page Blog Article Variant)
- EVENT_ENABLED_CLASSES = %w( Order Product CustomCollection SmartCollection Page Blog Article )
-
- module Countable
- def count(options = {})
- Integer(get(:count, options))
- end
- end
-
- module Metafields
- def metafields
- Metafield.find(:all, :params => {:resource => self.class.collection_name, :resource_id => id})
- end
-
- def add_metafield(metafield)
- raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
-
- metafield.prefix_options = {
- :resource => self.class.collection_name,
- :resource_id => id
- }
- metafield.save
- metafield
- end
- end
- module Events
- def events
- Event.find(:all, :params => {:resource => self.class.collection_name, :resource_id => id})
- end
- end
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- class Session
- cattr_accessor :api_key
- cattr_accessor :secret
- cattr_accessor :protocol
- self.protocol = 'https'
- attr_accessor :url, :token, :name
-
- def self.setup(params)
- params.each { |k,value| send("#{k}=", value) }
- end
- def initialize(url, token = nil, params = nil)
- self.url, self.token = url, token
- if params
- unless self.class.validate_signature(params) && params[:timestamp].to_i > 24.hours.ago.utc.to_i
- raise "Invalid Signature: Possible malicious login"
- end
- end
- self.class.prepare_url(self.url)
- end
-
- def shop
- Shop.current
- end
-
- def create_permission_url
- return nil if url.blank? || api_key.blank?
- "http://#{url}/admin/api/auth?api_key=#{api_key}"
- end
-
-
-
- def site
- "#{protocol}://#{api_key}:#{computed_password}@#{url}/admin"
- end
- def valid?
- url.present? && token.present?
- end
- private
-
-
-
- def computed_password
- Digest::MD5.hexdigest(secret + token.to_s)
- end
-
- def self.prepare_url(url)
- return nil if url.blank?
- url.gsub!(/https?:\/\//, '')
- url.concat(".myshopify.com") unless url.include?('.')
- end
-
- def self.validate_signature(params)
- return false unless signature = params[:signature]
- sorted_params = params.except(:signature, :action, :controller).collect{|k,v|"#{k}=#{v}"}.sort.join
- Digest::MD5.hexdigest(secret + sorted_params) == signature
- end
- end
-
- class Base < ActiveResource::Base
- extend Countable
- end
-
-
- class Shop < Base
- def self.current
- find(:one, :from => "/admin/shop.#{format.extension}")
- end
-
- def metafields
- Metafield.find(:all)
- end
-
- def add_metafield(metafield)
- raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
- metafield.save
- metafield
- end
-
- def events
- Event.find(:all)
- end
- end
-
-
- class CustomCollection < Base
- def products
- Product.find(:all, :params => {:collection_id => self.id})
- end
-
- def add_product(product)
- Collect.create(:collection_id => self.id, :product_id => product.id)
- end
-
- def remove_product(product)
- collect = Collect.find(:first, :params => {:collection_id => self.id, :product_id => product.id})
- collect.destroy if collect
- end
- end
-
- class SmartCollection < Base
- def products
- Product.find(:all, :params => {:collection_id => self.id})
- end
- end
-
-
- class Collect < Base
- end
- class ShippingAddress < Base
- end
- class BillingAddress < Base
- end
- class LineItem < Base
- end
- class ShippingLine < Base
- end
- class NoteAttribute < Base
- end
- class Order < Base
- def close; load_attributes_from_response(post(:close, {}, only_id)); end
- def open; load_attributes_from_response(post(:open, {}, only_id)); end
- def cancel(options = {})
- load_attributes_from_response(post(:cancel, options, only_id))
- end
- def transactions
- Transaction.find(:all, :params => { :order_id => id })
- end
- def capture(amount = "")
- Transaction.create(:amount => amount, :kind => "capture", :order_id => id)
- end
- def only_id
- encode(:only => :id, :include => [], :methods => [], :fields => [])
- end
- end
- class Product < Base
-
-
- def self.share; post :share; end
- def self.unshare; delete :share; end
-
- def price_range
- prices = variants.collect(&:price)
- format = "%0.2f"
- if prices.min != prices.max
- "#{format % prices.min} - #{format % prices.max}"
- else
- format % prices.min
- end
- end
-
- def collections
- CustomCollection.find(:all, :params => {:product_id => self.id})
- end
-
- def smart_collections
- SmartCollection.find(:all, :params => {:product_id => self.id})
- end
-
- def add_to_collection(collection)
- collection.add_product(self)
- end
-
- def remove_from_collection(collection)
- collection.remove_product(self)
- end
- end
-
- class Variant < Base
- self.prefix = "/admin/products/:product_id/"
-
- def self.prefix(options={})
- options[:product_id].nil? ? "/admin/" : "/admin/products/#{options[:product_id]}/"
- end
- end
-
- class Image < Base
- self.prefix = "/admin/products/:product_id/"
-
-
- [:pico, :icon, :thumb, :small, :compact, :medium, :large, :grande, :original].each do |m|
- reg_exp_match = "/\\1_#{m}.\\2"
- define_method(m) { src.gsub(/\/(.*)\.(\w{2,4})/, reg_exp_match) }
- end
-
- def attach_image(data, filename = nil)
- attributes['attachment'] = Base64.encode64(data)
- attributes['filename'] = filename unless filename.nil?
- end
- end
- class Transaction < Base
- self.prefix = "/admin/orders/:order_id/"
- end
-
- class Fulfillment < Base
- self.prefix = "/admin/orders/:order_id/"
- end
- class Country < Base
- end
- class Page < Base
- end
-
- class Blog < Base
- def articles
- Article.find(:all, :params => { :blog_id => id })
- end
- end
-
- class Article < Base
- self.prefix = "/admin/blogs/:blog_id/"
-
- def comments
- Comment.find(:all, :params => { :article_id => id })
- end
- end
-
- class Metafield < Base
- self.prefix = "/admin/:resource/:resource_id/"
-
-
- def self.prefix(options={})
- options[:resource].nil? ? "/admin/" : "/admin/#{options[:resource]}/#{options[:resource_id]}/"
- end
-
- def value
- return if attributes["value"].nil?
- attributes["value_type"] == "integer" ? attributes["value"].to_i : attributes["value"]
- end
- end
- class Comment < Base
- def remove; load_attributes_from_response(post(:remove, {}, only_id)); end
- def ham; load_attributes_from_response(post(:ham, {}, only_id)); end
- def spam; load_attributes_from_response(post(:spam, {}, only_id)); end
- def approve; load_attributes_from_response(post(:approve, {}, only_id)); end
- def restore; load_attributes_from_response(post(:restore, {}, only_id)); end
- def not_spam; load_attributes_from_response(post(:not_spam, {}, only_id)); end
- def only_id
- encode(:only => :id)
- end
- end
- class Province < Base
- self.prefix = "/admin/countries/:country_id/"
- end
-
- class Redirect < Base
- end
-
- class Webhook < Base
- end
-
- class Event < Base
- self.prefix = "/admin/:resource/:resource_id/"
-
-
- def self.prefix(options={})
- options[:resource].nil? ? "/admin/" : "/admin/#{options[:resource]}/#{options[:resource_id]}/"
- end
- end
-
- class Customer < Base
- end
- class CustomerGroup < Base
- end
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- class Asset < Base
- self.primary_key = 'key'
-
-
-
- def self.find(*args)
- if args[0].is_a?(Symbol)
- super
- else
- params = {:asset => {:key => args[0]}}
- params = params.merge(args[1][:params]) if args[1] && args[1][:params]
- find(:one, :from => "/admin/assets.#{format.extension}", :params => params)
- end
- end
-
-
-
-
- def value
- attributes['value'] ||
- (attributes['attachment'] ? Base64.decode64(attributes['attachment']) : nil)
- end
-
- def attach(data)
- self.attachment = Base64.encode64(data)
- end
-
- def destroy
- connection.delete(element_path(:asset => {:key => key}), self.class.headers)
- end
-
- def new?
- false
- end
-
- def self.element_path(id, prefix_options = {}, query_options = nil)
- prefix_options, query_options = split_options(prefix_options) if query_options.nil?
- "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
- end
-
- def method_missing(method_symbol, *arguments)
- if %w{value= attachment= src= source_key=}.include?(method_symbol)
- wipe_value_attributes
- end
- super
- end
-
- private
-
- def wipe_value_attributes
- %w{value attachment src source_key}.each do |attr|
- attributes.delete(attr)
- end
- end
- end
-
- class RecurringApplicationCharge < Base
- undef_method :test
- def self.current
- find(:all).find{|charge| charge.status == 'active'}
- end
-
- def cancel
- load_attributes_from_response(self.destroy)
- end
-
- def activate
- load_attributes_from_response(post(:activate))
- end
- end
- class ApplicationCharge < Base
- undef_method :test
- def activate
- load_attributes_from_response(post(:activate))
- end
- end
- class ProductSearchEngine < Base
- end
-
- class ScriptTag < Base
- end
-
-
- METAFIELD_ENABLED_CLASSES.each do |klass|
- "ShopifyAPI::#{klass}".constantize.send(:include, Metafields)
- end
-
-
- EVENT_ENABLED_CLASSES.each do |klass|
- "ShopifyAPI::#{klass}".constantize.send(:include, Events)
- end
- end
|