spiffy_stores_api 4.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +10 -0
  4. data/.travis.yml +14 -0
  5. data/CHANGELOG +3 -0
  6. data/Gemfile +5 -0
  7. data/Gemfile_ar40 +5 -0
  8. data/Gemfile_ar41 +5 -0
  9. data/Gemfile_ar50 +5 -0
  10. data/Gemfile_ar_master +5 -0
  11. data/README.md +200 -0
  12. data/Rakefile +39 -0
  13. data/lib/active_resource/base_ext.rb +21 -0
  14. data/lib/active_resource/connection_ext.rb +10 -0
  15. data/lib/active_resource/detailed_log_subscriber.rb +19 -0
  16. data/lib/active_resource/disable_prefix_check.rb +36 -0
  17. data/lib/active_resource/json_errors.rb +31 -0
  18. data/lib/active_resource/to_query.rb +10 -0
  19. data/lib/spiffy_stores_api.rb +29 -0
  20. data/lib/spiffy_stores_api/connection.rb +33 -0
  21. data/lib/spiffy_stores_api/countable.rb +14 -0
  22. data/lib/spiffy_stores_api/json_format.rb +18 -0
  23. data/lib/spiffy_stores_api/limits.rb +86 -0
  24. data/lib/spiffy_stores_api/metafields.rb +19 -0
  25. data/lib/spiffy_stores_api/resources.rb +2 -0
  26. data/lib/spiffy_stores_api/resources/address.rb +4 -0
  27. data/lib/spiffy_stores_api/resources/article.rb +20 -0
  28. data/lib/spiffy_stores_api/resources/base.rb +86 -0
  29. data/lib/spiffy_stores_api/resources/blog.rb +9 -0
  30. data/lib/spiffy_stores_api/resources/collect.rb +5 -0
  31. data/lib/spiffy_stores_api/resources/country.rb +4 -0
  32. data/lib/spiffy_stores_api/resources/customer.rb +24 -0
  33. data/lib/spiffy_stores_api/resources/image.rb +16 -0
  34. data/lib/spiffy_stores_api/resources/metafield.rb +12 -0
  35. data/lib/spiffy_stores_api/resources/order.rb +23 -0
  36. data/lib/spiffy_stores_api/resources/page.rb +5 -0
  37. data/lib/spiffy_stores_api/resources/product.rb +32 -0
  38. data/lib/spiffy_stores_api/resources/province.rb +5 -0
  39. data/lib/spiffy_stores_api/resources/script_tag.rb +4 -0
  40. data/lib/spiffy_stores_api/resources/standard_collection.rb +18 -0
  41. data/lib/spiffy_stores_api/resources/store.rb +18 -0
  42. data/lib/spiffy_stores_api/resources/super_collection.rb +14 -0
  43. data/lib/spiffy_stores_api/resources/variant.rb +8 -0
  44. data/lib/spiffy_stores_api/resources/webhook.rb +4 -0
  45. data/lib/spiffy_stores_api/session.rb +145 -0
  46. data/lib/spiffy_stores_api/version.rb +3 -0
  47. data/spiffy_stores_api.gemspec +34 -0
  48. data/test/active_resource/json_errors_test.rb +19 -0
  49. data/test/article_test.rb +73 -0
  50. data/test/base_test.rb +112 -0
  51. data/test/blog_test.rb +8 -0
  52. data/test/collect_test.rb +9 -0
  53. data/test/countable_test.rb +13 -0
  54. data/test/customer_test.rb +50 -0
  55. data/test/fixtures/article.json +15 -0
  56. data/test/fixtures/articles.json +39 -0
  57. data/test/fixtures/asset.json +9 -0
  58. data/test/fixtures/assets.json +136 -0
  59. data/test/fixtures/authors.json +1 -0
  60. data/test/fixtures/blog.json +12 -0
  61. data/test/fixtures/blogs.json +13 -0
  62. data/test/fixtures/collect.json +10 -0
  63. data/test/fixtures/custom_collection.json +17 -0
  64. data/test/fixtures/customers.json +64 -0
  65. data/test/fixtures/customers_search.json +66 -0
  66. data/test/fixtures/image.json +10 -0
  67. data/test/fixtures/images.json +20 -0
  68. data/test/fixtures/metafield.json +13 -0
  69. data/test/fixtures/metafields.json +26 -0
  70. data/test/fixtures/o_auth_revoke.json +5 -0
  71. data/test/fixtures/order.json +297 -0
  72. data/test/fixtures/orders.json +299 -0
  73. data/test/fixtures/product.json +116 -0
  74. data/test/fixtures/redirect.json +7 -0
  75. data/test/fixtures/script_tag.json +10 -0
  76. data/test/fixtures/script_tags.json +18 -0
  77. data/test/fixtures/smart_collection.json +21 -0
  78. data/test/fixtures/store.json +35 -0
  79. data/test/fixtures/tags.json +1 -0
  80. data/test/fixtures/transaction.json +29 -0
  81. data/test/fixtures/variant.json +23 -0
  82. data/test/fixtures/variants.json +88 -0
  83. data/test/fixtures/webhook.json +10 -0
  84. data/test/fixtures/webhooks.json +18 -0
  85. data/test/metafield_test.rb +46 -0
  86. data/test/store_test.rb +58 -0
  87. data/test/test_helper.rb +90 -0
  88. metadata +219 -0
@@ -0,0 +1,32 @@
1
+ module SpiffyStoresAPI
2
+ class Product < Base
3
+ include Metafields
4
+
5
+ # compute the price range
6
+ def price_range
7
+ prices = variants.collect(&:price).collect(&:to_f)
8
+ format = "%0.2f"
9
+ if prices.min != prices.max
10
+ "#{format % prices.min} - #{format % prices.max}"
11
+ else
12
+ format % prices.min
13
+ end
14
+ end
15
+
16
+ def collections
17
+ StandardCollection.find(:all, :params => {:product_id => self.id})
18
+ end
19
+
20
+ def super_collections
21
+ SmartCollection.find(:all, :params => {:product_id => self.id})
22
+ end
23
+
24
+ def add_to_collection(collection)
25
+ collection.add_product(self)
26
+ end
27
+
28
+ def remove_from_collection(collection)
29
+ collection.remove_product(self)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ module SpiffyStoresAPI
2
+ class Province < Base
3
+ init_prefix :country
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module SpiffyStoresAPI
2
+ class ScriptTag < Base
3
+ end
4
+ end
@@ -0,0 +1,18 @@
1
+ module SpiffyStoresAPI
2
+ class StandardCollection < Base
3
+ include Metafields
4
+
5
+ def products
6
+ Product.find(:all, :params => {:collection_id => self.id})
7
+ end
8
+
9
+ def add_product(product)
10
+ Collect.create(:collection_id => self.id, :product_id => product.id)
11
+ end
12
+
13
+ def remove_product(product)
14
+ collect = Collect.find(:first, :params => {:collection_id => self.id, :product_id => product.id})
15
+ collect.destroy if collect
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module SpiffyStoresAPI
2
+ # Store object. Use Store.current to retrieve the store settings.
3
+ class Store < Base
4
+ def self.current(options={})
5
+ find(:one, options.merge({from: "/api/store.#{format.extension}"}))
6
+ end
7
+
8
+ def metafields(**options)
9
+ Metafield.find(:all, params: options)
10
+ end
11
+
12
+ def add_metafield(metafield)
13
+ raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
14
+ metafield.save
15
+ metafield
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ module SpiffyStoresAPI
2
+ class SuperCollection < Base
3
+ include Metafields
4
+
5
+ def products
6
+ Product.find(:all, :params => {:collection_id => self.id})
7
+ end
8
+
9
+ def order(options={})
10
+ load_attributes_from_response(put(:order, options, only_id))
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module SpiffyStoresAPI
2
+ class Variant < Base
3
+ include Metafields
4
+ include DisablePrefixCheck
5
+
6
+ conditional_prefix :product
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ module SpiffyStoresAPI
2
+ class Webhook < Base
3
+ end
4
+ end
@@ -0,0 +1,145 @@
1
+ require 'openssl'
2
+ require 'rack'
3
+
4
+ module SpiffyStoresAPI
5
+
6
+ class ValidationException < StandardError
7
+ end
8
+
9
+ class Session
10
+ cattr_accessor :api_key, :secret, :protocol, :spiffy_stores_domain, :port
11
+ self.protocol = 'https'
12
+ self.spiffy_stores_domain = 'spiffystores.com'
13
+
14
+ attr_accessor :url, :token, :shop, :name, :extra
15
+
16
+ class << self
17
+
18
+ def setup(params)
19
+ params.each { |k,value| public_send("#{k}=", value) }
20
+ end
21
+
22
+ def temp(domain, token, &block)
23
+ session = new(domain, token)
24
+ original_site = SpiffyStoresAPI::Base.site.to_s
25
+ original_token = SpiffyStoresAPI::Base.headers['Authorization'].try(:gsub, /^Bearer /i, '')
26
+ original_session = new(original_site, original_token)
27
+
28
+ begin
29
+ SpiffyStoresAPI::Base.activate_session(session)
30
+ yield
31
+ ensure
32
+ SpiffyStoresAPI::Base.activate_session(original_session)
33
+ end
34
+ end
35
+
36
+ def prepare_url(url)
37
+ return nil if url.blank?
38
+ # remove http:// or https://
39
+ url = url.strip.gsub(/\Ahttps?:\/\//, '')
40
+ # extract host, removing any username, password or path
41
+ store = URI.parse("https://#{url}").host
42
+ # extract subdomain of .spiffystores.com
43
+ if idx = store.index(".")
44
+ store = store.slice(0, idx)
45
+ end
46
+ return nil if store.empty?
47
+ store = "#{store}.#{spiffy_stores_domain}"
48
+ port ? "#{store}:#{port}" : store
49
+ rescue URI::InvalidURIError
50
+ nil
51
+ end
52
+
53
+ def validate_signature(params)
54
+ params = params.with_indifferent_access
55
+ return false unless signature = params[:hmac]
56
+
57
+ calculated_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new(), secret, encoded_params_for_signature(params))
58
+
59
+ Rack::Utils.secure_compare(calculated_signature, signature)
60
+ end
61
+
62
+ private
63
+
64
+ def encoded_params_for_signature(params)
65
+ params = params.except(:signature, :hmac, :action, :controller)
66
+ params.map{|k,v| "#{URI.escape(k.to_s, '&=%')}=#{URI.escape(v.to_s, '&%')}"}.sort.join('&')
67
+ end
68
+ end
69
+
70
+ def initialize(url, token = nil, shop = nil, extra = {})
71
+ self.url = self.class.prepare_url(url)
72
+ self.token = token
73
+ self.shop = shop
74
+ self.extra = extra
75
+ end
76
+
77
+ def create_permission_url(scope, redirect_uri = nil)
78
+ params = {:client_id => api_key, :scope => scope.join(',')}
79
+ params[:redirect_uri] = redirect_uri if redirect_uri
80
+ "#{site}/admin/oauth/authorize?#{parameterize(params)}"
81
+ end
82
+
83
+ def request_token(params)
84
+ return token if token
85
+
86
+ unless self.class.validate_signature(params) && params[:timestamp].to_i > 24.hours.ago.utc.to_i
87
+ raise SpiffyStoresAPI::ValidationException, "Invalid Signature: Possible malicious login"
88
+ end
89
+
90
+ response = access_token_request(params['code'])
91
+ if response.code == "200"
92
+ self.extra = JSON.parse(response.body)
93
+ self.token = extra.delete('access_token')
94
+
95
+ if expires_in = extra.delete('expires_in')
96
+ extra['expires_at'] = Time.now.utc.to_i + expires_in
97
+ end
98
+ token
99
+ else
100
+ raise RuntimeError, response.msg
101
+ end
102
+ end
103
+
104
+ def store
105
+ Store.current
106
+ end
107
+
108
+ def site
109
+ "#{protocol}://#{url}/api"
110
+ end
111
+
112
+ def valid?
113
+ url.present? && token.present?
114
+ end
115
+
116
+ def expires_in
117
+ return unless expires_at.present?
118
+ [0, expires_at.to_i - Time.now.utc.to_i].max
119
+ end
120
+
121
+ def expires_at
122
+ return unless extra.present?
123
+ @expires_at ||= Time.at(extra['expires_at']).utc
124
+ end
125
+
126
+ def expired?
127
+ return false if expires_in.nil?
128
+ expires_in <= 0
129
+ end
130
+
131
+ private
132
+ def parameterize(params)
133
+ URI.escape(params.collect{|k,v| "#{k}=#{v}"}.join('&'))
134
+ end
135
+
136
+ def access_token_request(code)
137
+ uri = URI.parse("#{protocol}://#{url}/admin/oauth/token")
138
+ https = Net::HTTP.new(uri.host, uri.port)
139
+ https.use_ssl = true
140
+ request = Net::HTTP::Post.new(uri.request_uri)
141
+ request.set_form_data({"client_id" => api_key, "client_secret" => secret, "code" => code})
142
+ https.request(request)
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,3 @@
1
+ module SpiffyStoresAPI
2
+ VERSION = "4.11.0"
3
+ end
@@ -0,0 +1,34 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "spiffy_stores_api/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = %q{spiffy_stores_api}
6
+ s.version = SpiffyStoresAPI::VERSION
7
+ s.author = "Spiffy Stores"
8
+
9
+ s.summary = %q{The SpiffyStores API gem is a lightweight gem for accessing the Spiffy Stores admin REST web services}
10
+ s.description = %q{The SpiffyStores API gem allows Ruby developers to programmatically access the admin section of SpiffyStores stores. The API is implemented as JSON or XML over HTTP using all four verbs (GET/POST/PUT/DELETE). Each resource, like Order, Product, or Collection, has its own URL and is manipulated in isolation.}
11
+ s.email = %q{brian@spiffy.com.au}
12
+ s.homepage = %q{https://www.spiffystores.com.au}
13
+
14
+ s.extra_rdoc_files = [
15
+ "README.md"
16
+ ]
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+
21
+ s.rdoc_options = ["--charset=UTF-8"]
22
+ s.summary = %q{SpiffyStoresAPI is a lightweight gem for accessing the Spiffy Stores admin REST web services}
23
+ s.license = "MIT"
24
+
25
+ s.required_ruby_version = ">= 2.2"
26
+
27
+ s.add_runtime_dependency("activeresource", ">= 3.0.0")
28
+ s.add_runtime_dependency("rack")
29
+
30
+ s.add_development_dependency("mocha", ">= 0.9.8")
31
+ s.add_development_dependency("fakeweb")
32
+ s.add_development_dependency("minitest", ">= 4.0")
33
+ s.add_development_dependency("rake")
34
+ end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveResource
4
+ class JsonErrorsTest < Test::Unit::TestCase
5
+
6
+ def test_parsing_of_error_json_hash
7
+ @model = SpiffyStoresAPI::Order.new
8
+ @model.errors.from_json({errors: {name: ['missing']}}.to_json)
9
+ assert_equal ['missing'], @model.errors[:name]
10
+ end
11
+
12
+ def test_parsing_of_error_json_plain_string
13
+ @model = SpiffyStoresAPI::Order.new
14
+ @model.errors.from_json({errors: 'some generic error'}.to_json)
15
+ assert_equal ['some generic error'], @model.errors[:base]
16
+ assert_equal 'some generic error', @model.errors.full_messages.to_sentence
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,73 @@
1
+ require 'test_helper'
2
+
3
+ class ArticleTest < Test::Unit::TestCase
4
+
5
+ def test_create_article
6
+ fake "blogs/1008414260/articles", :method => :post, :body => load_fixture('article')
7
+ article = SpiffyStoresAPI::Article.new(:blog_id => 1008414260)
8
+ article.save
9
+ assert_equal "First Post", article.title
10
+ end
11
+
12
+ def test_get_article
13
+ fake "articles/6242736", :method => :get, :body => load_fixture('article')
14
+ article = SpiffyStoresAPI::Article.find(6242736)
15
+ assert_equal "First Post", article.title
16
+ assert_equal 1008414260, article.blog_id
17
+ end
18
+
19
+ def test_get_articles
20
+ fake "articles", :method => :get, :body => load_fixture('articles')
21
+ articles = SpiffyStoresAPI::Article.all
22
+ assert_equal 3, articles.length
23
+ assert_equal 1008414260, articles.first.blog_id
24
+ end
25
+
26
+ def test_get_articles_namespaced
27
+ fake "blogs/1008414260/articles", :method => :get, :body => load_fixture('articles')
28
+ articles = SpiffyStoresAPI::Article.find(:all, :params => {:blog_id => 1008414260})
29
+ assert_equal 3, articles.length
30
+ assert_equal 1008414260, articles.first.blog_id
31
+ end
32
+
33
+ def test_get_article_namespaced
34
+ fake "blogs/1008414260/articles/6242736", :method => :get, :body => load_fixture('article')
35
+ article = SpiffyStoresAPI::Article.find(6242736, :params => {:blog_id => 1008414260})
36
+ assert_equal "First Post", article.title
37
+ assert_equal 1008414260, article.blog_id
38
+ end
39
+
40
+ def test_get_authors
41
+ fake "articles/authors", :method => :get, :body => load_fixture('authors')
42
+ authors = SpiffyStoresAPI::Article.authors
43
+ assert_equal "Spiffy Stores", authors.first
44
+ assert_equal "development shop", authors.last
45
+ end
46
+
47
+ def test_get_authors_for_blog_id
48
+ fake "blogs/1008414260/articles/authors", :method => :get, :body => load_fixture('authors')
49
+ authors = SpiffyStoresAPI::Article.authors(:blog_id => 1008414260)
50
+ assert_equal 3, authors.length
51
+ end
52
+
53
+ # def test_get_tags
54
+ # fake "articles/tags", :method => :get, :body => load_fixture('tags')
55
+ # tags = SpiffyStoresAPI::Article.tags
56
+ # assert_equal "consequuntur", tags.first
57
+ # assert_equal "repellendus", tags.last
58
+ # end
59
+
60
+ # def test_get_tags_for_blog_id
61
+ # fake "blogs/1008414260/articles/tags", :method => :get, :body => load_fixture('tags')
62
+ # tags = SpiffyStoresAPI::Article.tags(:blog_id => 1008414260)
63
+ # assert_equal "consequuntur", tags.first
64
+ # assert_equal "repellendus", tags.last
65
+ # end
66
+ #
67
+ # def test_get_popular_tags
68
+ # fake "articles/tags.json?limit=1&popular=1", :extension => false, :method => :get, :body => load_fixture('tags')
69
+ # tags = SpiffyStoresAPI::Article.tags(:popular => 1, :limit => 1)
70
+ # assert_equal 3, tags.length
71
+ # end
72
+
73
+ end
@@ -0,0 +1,112 @@
1
+ require 'test_helper'
2
+
3
+
4
+ class BaseTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @session1 = SpiffyStoresAPI::Session.new('shop1.spiffystores.com', 'token1')
8
+ @session2 = SpiffyStoresAPI::Session.new('shop2.spiffystores.com', 'token2')
9
+ end
10
+
11
+ def teardown
12
+ clear_header('X-Custom')
13
+ end
14
+
15
+ test '#activate_session should set site and headers for given session' do
16
+ SpiffyStoresAPI::Base.activate_session @session1
17
+
18
+ assert_nil ActiveResource::Base.site
19
+ assert_equal 'https://shop1.spiffystores.com/api', SpiffyStoresAPI::Base.site.to_s
20
+ assert_equal 'https://shop1.spiffystores.com/api', SpiffyStoresAPI::Store.site.to_s
21
+
22
+ assert_nil ActiveResource::Base.headers['Authorization']
23
+ assert_equal 'Bearer token1', SpiffyStoresAPI::Base.headers['Authorization']
24
+ assert_equal 'Bearer token1', SpiffyStoresAPI::Store.headers['Authorization']
25
+ end
26
+
27
+ test '#clear_session should clear site and headers from Base' do
28
+ SpiffyStoresAPI::Base.activate_session @session1
29
+ SpiffyStoresAPI::Base.clear_session
30
+
31
+ assert_nil ActiveResource::Base.site
32
+ assert_nil SpiffyStoresAPI::Base.site
33
+ assert_nil SpiffyStoresAPI::Store.site
34
+
35
+ assert_nil ActiveResource::Base.headers['Authorization']
36
+ assert_nil SpiffyStoresAPI::Base.headers['Authorization']
37
+ assert_nil SpiffyStoresAPI::Store.headers['Authorization']
38
+ end
39
+
40
+ test '#activate_session with one session, then clearing and activating with another session should send request to correct shop' do
41
+ SpiffyStoresAPI::Base.activate_session @session1
42
+ SpiffyStoresAPI::Base.clear_session
43
+ SpiffyStoresAPI::Base.activate_session @session2
44
+
45
+ assert_nil ActiveResource::Base.site
46
+ assert_equal 'https://shop2.spiffystores.com/api', SpiffyStoresAPI::Base.site.to_s
47
+ assert_equal 'https://shop2.spiffystores.com/api', SpiffyStoresAPI::Store.site.to_s
48
+
49
+ assert_nil ActiveResource::Base.headers['Authorization']
50
+ assert_equal 'Bearer token2', SpiffyStoresAPI::Base.headers['Authorization']
51
+ assert_equal 'Bearer token2', SpiffyStoresAPI::Store.headers['Authorization']
52
+ end
53
+
54
+ test '#activate_session with nil raises an InvalidSessionError' do
55
+ assert_raises SpiffyStoresAPI::Base::InvalidSessionError do
56
+ SpiffyStoresAPI::Base.activate_session nil
57
+ end
58
+ end
59
+
60
+ test "#delete should send custom headers with request" do
61
+ SpiffyStoresAPI::Base.activate_session @session1
62
+ SpiffyStoresAPI::Base.headers['X-Custom'] = 'abc'
63
+ SpiffyStoresAPI::Base.connection.expects(:delete).with('/api/bases/1.json', has_entry('X-Custom', 'abc'))
64
+ SpiffyStoresAPI::Base.delete "1"
65
+ end
66
+
67
+ test "#headers includes the User-Agent" do
68
+ assert_not_includes ActiveResource::Base.headers.keys, 'User-Agent'
69
+ assert_includes SpiffyStoresAPI::Base.headers.keys, 'User-Agent'
70
+ thread = Thread.new do
71
+ assert_includes SpiffyStoresAPI::Base.headers.keys, 'User-Agent'
72
+ end
73
+ thread.join
74
+ end
75
+
76
+ if ActiveResource::VERSION::MAJOR >= 4
77
+ test "#headers propagates changes to subclasses" do
78
+ SpiffyStoresAPI::Base.headers['X-Custom'] = "the value"
79
+ assert_equal "the value", SpiffyStoresAPI::Base.headers['X-Custom']
80
+ assert_equal "the value", SpiffyStoresAPI::Product.headers['X-Custom']
81
+ end
82
+
83
+ test "#headers clears changes to subclasses" do
84
+ SpiffyStoresAPI::Base.headers['X-Custom'] = "the value"
85
+ assert_equal "the value", SpiffyStoresAPI::Product.headers['X-Custom']
86
+ SpiffyStoresAPI::Base.headers['X-Custom'] = nil
87
+ assert_nil SpiffyStoresAPI::Product.headers['X-Custom']
88
+ end
89
+ end
90
+
91
+ if ActiveResource::VERSION::MAJOR >= 5 || (ActiveResource::VERSION::MAJOR >= 4 && ActiveResource::VERSION::PRE == "threadsafe")
92
+ test "#headers set in the main thread affect spawned threads" do
93
+ SpiffyStoresAPI::Base.headers['X-Custom'] = "the value"
94
+ Thread.new do
95
+ assert_equal "the value", SpiffyStoresAPI::Base.headers['X-Custom']
96
+ end.join
97
+ end
98
+
99
+ test "#headers set in spawned threads do not affect the main thread" do
100
+ Thread.new do
101
+ SpiffyStoresAPI::Base.headers['X-Custom'] = "the value"
102
+ end.join
103
+ assert_nil SpiffyStoresAPI::Base.headers['X-Custom']
104
+ end
105
+ end
106
+
107
+ def clear_header(header)
108
+ [ActiveResource::Base, SpiffyStoresAPI::Base, SpiffyStoresAPI::Product].each do |klass|
109
+ klass.headers.delete(header)
110
+ end
111
+ end
112
+ end