bernsno-shopify 0.3.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 BehindLogic
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,39 @@
1
+ # shopify
2
+
3
+ ## Links
4
+
5
+ * Gem: [http://gemcutter.org/gems/shopify](http://gemcutter.org/gems/shopify)
6
+ * Source: [http://github.com/dcparker/shopify](http://github.com/dcparker/shopify)
7
+ * Author: [Daniel Parker](http://github.com/dcparker) from [BehindLogic](http://behindlogic.com)
8
+ * Shopify API Documentation: [http://api.shopify.com/](http://api.shopify.com/)
9
+
10
+ ## Features
11
+
12
+ * Read any kind of data from Shopify, but no support built-in yet to save data back to Shopify.
13
+ * Thread-safe: You can connect to multiple shops in the same application.
14
+
15
+ ## Example Usage:
16
+
17
+ shop = Shopify.new('store_name', 'api-key', 'api-secret', 'auth-token')
18
+ order = shop.orders(:limit => 1)[0] # => gets first order
19
+ order.line_items # => the line items within that order
20
+ order.fulfillments # => gets all fulfillments related to this order
21
+ blogs = shop.blogs # => gets all blogs for this shop
22
+ articles = blogs[0].articles # => gets all the articles in this blog
23
+ articles[0].comments # => gets the comments for that article
24
+ shop.products # => get all products in this shop
25
+ ... and much more ... :)
26
+
27
+ ## Note on Patches/Pull Requests
28
+
29
+ * Fork the project.
30
+ * Make your feature addition or bug fix.
31
+ * Add tests for it. This is important so I don't break it in a
32
+ future version unintentionally.
33
+ * Commit, do not mess with rakefile, version, or history.
34
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
35
+ * Send me a pull request. Bonus points for topic branches.
36
+
37
+ ## Copyright
38
+
39
+ Copyright (c) 2009 BehindLogic. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "bernsno-shopify"
8
+ gem.summary = %Q{Communicate easily with Shopify.com's Restful API.}
9
+ gem.description = %Q{Communicate easily with Shopify.com's Restful API.}
10
+ gem.email = "bernsno@gmail.com"
11
+ gem.homepage = "http://github.com/bernsno/shopify"
12
+ gem.authors = ["BehindLogic","Bernsno"]
13
+ gem.add_dependency "extlib", ">= 0"
14
+ gem.add_dependency "httparty", ">= 0.5.0"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "shopify #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.1
@@ -0,0 +1,59 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{bernsno-shopify}
8
+ s.version = "0.3.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["BehindLogic", "Bernsno"]
12
+ s.date = %q{2010-04-05}
13
+ s.description = %q{Communicate easily with Shopify.com's Restful API.}
14
+ s.email = %q{bernsno@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.markdown",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "bernsno-shopify.gemspec",
27
+ "lib/shopify.rb",
28
+ "lib/shopify_api.yml",
29
+ "test/fixtures/collection.xml",
30
+ "test/helper.rb",
31
+ "test/test_shopify.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/bernsno/shopify}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.6}
37
+ s.summary = %q{Communicate easily with Shopify.com's Restful API.}
38
+ s.test_files = [
39
+ "test/helper.rb",
40
+ "test/test_shopify.rb"
41
+ ]
42
+
43
+ if s.respond_to? :specification_version then
44
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
48
+ s.add_runtime_dependency(%q<extlib>, [">= 0"])
49
+ s.add_runtime_dependency(%q<httparty>, [">= 0.5.0"])
50
+ else
51
+ s.add_dependency(%q<extlib>, [">= 0"])
52
+ s.add_dependency(%q<httparty>, [">= 0.5.0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<extlib>, [">= 0"])
56
+ s.add_dependency(%q<httparty>, [">= 0.5.0"])
57
+ end
58
+ end
59
+
data/lib/shopify.rb ADDED
@@ -0,0 +1,170 @@
1
+ require 'rubygems'
2
+ require 'bigdecimal'
3
+ require 'digest/md5'
4
+ require 'extlib'
5
+ require 'httparty'
6
+
7
+
8
+ # Class: Shopify
9
+ # Usage:
10
+ # shop = Shopify.new(host, api_key, password)
11
+ # shop.orders
12
+ class Shopify
13
+ attr_reader :host
14
+ include HTTParty
15
+
16
+ attr_accessor :base_uri
17
+
18
+ def initialize(host, api_key=nil, password=nil)
19
+ host.gsub!(/https?:\/\//, '') # remove http(s)://
20
+ @host = host.include?('.') ? host : "#{host}.myshopify.com" # extend url to myshopify.com if no host is given
21
+ @api_key = api_key
22
+ @password = password
23
+ setup
24
+ end
25
+
26
+ def needs_authorization?
27
+ ![@host, @api_key, @password].all?
28
+ end
29
+
30
+ def setup
31
+ unless needs_authorization?
32
+ @base_uri = "http://#{@host}/admin"
33
+ @basic_auth = {:username => @api_key, :password => @password}
34
+ @format = :xml
35
+ end
36
+ end
37
+ private :setup
38
+
39
+ def options(opts={})
40
+ {:base_uri => @base_uri, :basic_auth => @basic_auth, :format => @format}.merge(opts)
41
+ end
42
+
43
+
44
+ class ShopifyModel
45
+ class << self
46
+ def load_api_classes(api)
47
+ api.each_key do |klass_name|
48
+ next unless klass_name.is_a?(String)
49
+ singular = klass_name.singular
50
+ mode = singular == klass_name ? [:singular] : []
51
+ klass = Shopify.const_set(singular, Class.new(ShopifyModel))
52
+
53
+ # Set up the top_level or children_of setting
54
+ self == ShopifyModel ?
55
+ klass.send(:top_level, *mode) :
56
+ klass.send(:children_of, self)
57
+
58
+ # Set up the properties
59
+ klass.send(:attr_accessor, *api[klass_name].delete(:properties).split(', ').map {|s| s.to_sym})
60
+
61
+ # If there are any children to be had, set them up
62
+ klass.load_api_classes(api[klass_name])
63
+ end
64
+ end
65
+
66
+ def top_level(*options)
67
+ klass_name = self.name.gsub(/.*::/,'')
68
+ if options.include?(:singular)
69
+ # Defines the singular accessor in the Shopify object
70
+ # TODO: Make the object cache the results of queries, per set of parameters,
71
+ # and reload only when you include true as the first argument.
72
+ Shopify.class_eval "
73
+ def #{klass_name.snake_case}(query_params={})
74
+ json = Shopify.get(\"/#{klass_name.snake_case}.xml\", options(:query => query_params))
75
+ begin
76
+ #{klass_name}.instantiate(self, json['#{klass_name.snake_case}'])
77
+ rescue => e
78
+ warn \"Error: \#{e.inspect}\"
79
+ json
80
+ end
81
+ end
82
+ "
83
+ else
84
+ # Defines the plural accessor in the Shopify object
85
+ # TODO: Make the object cache the results of queries, per set of parameters,
86
+ # and reload only when you include true as the first argument.
87
+ Shopify.class_eval "
88
+ def #{klass_name.snake_case.pluralize}(query_params={})
89
+ json = Shopify.get(\"/#{klass_name.snake_case.pluralize}.xml\", options(:query => query_params))
90
+ if json['#{klass_name.snake_case.pluralize}']
91
+ json['#{klass_name.snake_case.pluralize}'].collect {|i| #{klass_name}.instantiate(self, i)}
92
+ else
93
+ json
94
+ end
95
+ end
96
+ def #{klass_name.snake_case}(id)
97
+ json = Shopify.get(\"/#{klass_name.snake_case.pluralize}/\#{id}.xml\", options)
98
+ if json['#{klass_name.snake_case}']
99
+ #{klass_name}.instantiate(self, json['#{klass_name.snake_case}'])
100
+ else
101
+ json
102
+ end
103
+ end
104
+ "
105
+ end
106
+ end
107
+ def children_of(parent_klass)
108
+ @parent = parent_klass
109
+ parent_klass_name = parent_klass.name.gsub(/.*::/,'')
110
+ klass_name = self.name.gsub(/.*::/,'')
111
+ # Defines the getter method in the parent class
112
+ # TODO: Make the object cache the results of queries, per set of parameters,
113
+ # and reload only when you include true as the first argument.
114
+ parent_klass.class_eval "
115
+ def #{klass_name.snake_case.pluralize}(query_params={})
116
+ @#{klass_name.snake_case.pluralize} ||= begin
117
+ json = Shopify.get(\"/#{parent_klass_name.snake_case.pluralize}/\#{id}/#{klass_name.snake_case.pluralize}.xml\", shop.options(:query => query_params))
118
+ case
119
+ when json['#{parent_klass_name.snake_case}_#{klass_name.snake_case.pluralize}']
120
+ json['#{parent_klass_name.snake_case}_#{klass_name.snake_case.pluralize}']
121
+ when json['#{klass_name.snake_case.pluralize}']
122
+ json['#{klass_name.snake_case.pluralize}']
123
+ else
124
+ json
125
+ end
126
+ end
127
+ @#{klass_name.snake_case.pluralize} = (@#{klass_name.snake_case.pluralize}.is_a?(Array) ? @#{klass_name.snake_case.pluralize}.collect {|i| #{klass_name}.instantiate(shop, i)} : [#{klass_name}.instantiate(shop, @#{klass_name.snake_case.pluralize})]) unless @#{klass_name.snake_case.pluralize}.is_a?(#{klass_name}) || @#{klass_name.snake_case.pluralize}.is_a?(Array) && @#{klass_name.snake_case.pluralize}[0].is_a?(#{klass_name})
128
+ @#{klass_name.snake_case.pluralize}
129
+ end
130
+ "
131
+ end
132
+ def is_child?
133
+ (@parent ||= nil)
134
+ end
135
+ end
136
+
137
+ def self.instantiate(shop, attrs={})
138
+ new(shop, attrs.merge('new_record' => false))
139
+ end
140
+ def initialize(shop, attrs={})
141
+ @new_record = true
142
+ attrs.each { |k,v| instance_variable_set("@#{k}", v) }
143
+ @shop = shop
144
+ end
145
+ attr_accessor :shop
146
+
147
+ def inspect
148
+ "<#{self.class.name} shop=#{@shop.host} #{instance_variables.reject {|i| i=='@shop' || instance_variable_get(i).to_s==''}.map {|i| "#{i}=#{instance_variable_get(i).inspect}"}.join(' ')}>"
149
+ end
150
+ end
151
+
152
+
153
+ ########################################
154
+ ## Load the Shopify Object Classes! ##
155
+ ########################################
156
+ ShopifyModel.load_api_classes YAML.load_file(File.dirname(__FILE__)+'/shopify_api.yml')
157
+
158
+
159
+ # Add some extra touches to a couple of the models
160
+ class Article < ShopifyModel
161
+ def comments(query_params={})
162
+ shop.comments(query_params.merge(:article_id => id, :blog_id => blog_id))
163
+ end
164
+ end
165
+ class CustomCollection < ShopifyModel
166
+ def products(query_params={})
167
+ shop.products(query_params.merge(:collection_id => id))
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,37 @@
1
+ ---
2
+ Blogs:
3
+ :properties: commentable, feedburner, feedburner_locations, handle, id, shop_id, title, updated_at
4
+ Articles:
5
+ :properties: author, blog_id, body, body_html, created_at, id, published_at, title, updated_at
6
+ Comments:
7
+ :properties: article_id, author, blog_id, body, body_html, created_at, email, id, ip, published_at, shop_id, status, updated_at, user_agent
8
+ Collects:
9
+ :properties: collection_id, featured, id, position, product_id
10
+ Countries:
11
+ :properties: code, id, name, tax
12
+ Provinces:
13
+ :properties: code, id, name, tax
14
+ CustomCollections:
15
+ :properties: body, body_html, handle, id, published_at, sort_order, title, updated_at
16
+ Orders:
17
+ :properties: buyer_accepts_marketing, closed_at, created_at, currency, email, financial_status, fulfillment_status, gateway, id, name, note, number, subtotal_price, taxes_included, token, total_discounts, total_line_items_price, total_price, total_tax, total_weight, updated_at, browser_ip, billing_address, shipping_address, line_items, shipping_line
18
+ LineItems:
19
+ :properties: fulfillment_service, grams, id, price, quantity, sku, title, variant_id, vendor, name, product_title
20
+ Fulfillments:
21
+ :properties: id, order_id, status, tracking_number, line_items, receipt
22
+ Transactions:
23
+ :properties: amount, authorization, created_at, kind, order_id, status, receipt
24
+ Pages:
25
+ :properties: author, body, body_html, created_at, handle, ip, published_at, shop_id, title, updated_at
26
+ Products:
27
+ :properties: body, body_html, created_at, handle, id, product_type, published_at, title, updated_at, vendor, tags, variants, images
28
+ Images:
29
+ :properties: id, position, product_id, src
30
+ Variants:
31
+ :properties: compare_at_price, fulfillment_service, grams, id, inventory_management, inventory_policy, inventory_quantity, position, price, product_id, sku, title
32
+ Redirects:
33
+ :properties: id, path, shop_id, target
34
+ Shop:
35
+ :properties: active_subscription_id, address1, city, country, created_at, domain, email, id, name, phone, province, public, source, zip, taxes_included, currency, timezone, shop_owner
36
+ SmartCollections:
37
+ :properties: body_html, handle, id, published_at, sort_order, template_suffix, title, updated_at, body
@@ -0,0 +1,26 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <smart-collections type="array">
3
+ <smart-collection>
4
+ <body-html></body-html>
5
+ <handle>name</handle>
6
+ <id type="integer">1234567</id>
7
+ <published-at type="datetime">2009-09-29T15:31:55-05:00</published-at>
8
+ <sort-order>created-desc</sort-order>
9
+ <template-suffix nil="true"></template-suffix>
10
+ <title>Name</title>
11
+ <updated-at type="datetime">2009-11-30T18:23:26-06:00</updated-at>
12
+ <body></body>
13
+ <rules type="array">
14
+ <rule>
15
+ <column>vendor</column>
16
+ <relation>equals</relation>
17
+ <condition>Name Person</condition>
18
+ </rule>
19
+ <rule>
20
+ <column>variant_inventory</column>
21
+ <relation>greater_than</relation>
22
+ <condition>0</condition>
23
+ </rule>
24
+ </rules>
25
+ </smart-collection>
26
+ </smart-collections>
data/test/helper.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'riot'
4
+ require 'fakeweb'
5
+
6
+ FakeWeb.allow_net_connect = false
7
+
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
10
+ require 'shopify'
11
+
12
+ class Test::Unit::TestCase
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ class TestShopify < Test::Unit::TestCase
4
+ context "A new connection" do
5
+ setup do
6
+ FakeWeb.register_uri(:get, "http://1234:5678@host.myshopify.com/admin/smart_collections.xml?title=name", :body => File.join(File.dirname(__FILE__), 'fixtures', 'collection.xml'))
7
+ Shopify.new("host", "1234", "5678").smart_collections(:title => "name").first
8
+ end
9
+ %w(body_html handle id published_at sort_order template_suffix title updated_at body).each do |property|
10
+ asserts("handle is name") { topic.instance_variables }.includes("@#{property}")
11
+ end
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bernsno-shopify
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 1
9
+ version: 0.3.1
10
+ platform: ruby
11
+ authors:
12
+ - BehindLogic
13
+ - Bernsno
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-04-05 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: extlib
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: httparty
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ - 5
43
+ - 0
44
+ version: 0.5.0
45
+ type: :runtime
46
+ version_requirements: *id002
47
+ description: Communicate easily with Shopify.com's Restful API.
48
+ email: bernsno@gmail.com
49
+ executables: []
50
+
51
+ extensions: []
52
+
53
+ extra_rdoc_files:
54
+ - LICENSE
55
+ - README.markdown
56
+ files:
57
+ - .document
58
+ - .gitignore
59
+ - LICENSE
60
+ - README.markdown
61
+ - Rakefile
62
+ - VERSION
63
+ - bernsno-shopify.gemspec
64
+ - lib/shopify.rb
65
+ - lib/shopify_api.yml
66
+ - test/fixtures/collection.xml
67
+ - test/helper.rb
68
+ - test/test_shopify.rb
69
+ has_rdoc: true
70
+ homepage: http://github.com/bernsno/shopify
71
+ licenses: []
72
+
73
+ post_install_message:
74
+ rdoc_options:
75
+ - --charset=UTF-8
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ requirements: []
93
+
94
+ rubyforge_project:
95
+ rubygems_version: 1.3.6
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Communicate easily with Shopify.com's Restful API.
99
+ test_files:
100
+ - test/helper.rb
101
+ - test/test_shopify.rb