rasin 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/.gitignore +6 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.rdoc +49 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +54 -0
- data/README.rdoc +166 -0
- data/asin.gemspec +36 -0
- data/lib/asin.rb +8 -0
- data/lib/asin/client.rb +398 -0
- data/lib/asin/configuration.rb +105 -0
- data/lib/asin/simple_cart.rb +54 -0
- data/lib/asin/simple_item.rb +44 -0
- data/lib/asin/simple_node.rb +44 -0
- data/lib/asin/version.rb +3 -0
- data/rakefile.rb +11 -0
- data/spec/asin.yml +4 -0
- data/spec/browse_node_spec.rb +17 -0
- data/spec/cart_spec.rb +150 -0
- data/spec/cassettes/asin/asin_cart_with_an_existing_cart_should_add_items_to_a_cart.yml +68 -0
- data/spec/cassettes/asin/asin_cart_with_an_existing_cart_should_clear_a_cart.yml +66 -0
- data/spec/cassettes/asin/asin_cart_with_an_existing_cart_should_get_a_cart.yml +67 -0
- data/spec/cassettes/asin/asin_cart_with_an_existing_cart_should_update_a_cart.yml +70 -0
- data/spec/cassettes/asin/browse_node_should_lookup_a_browse_node.yml +38 -0
- data/spec/cassettes/asin/cart_should_create_a_cart.yml +36 -0
- data/spec/cassettes/asin/lookup_and_search_should_have_metadata.yml +81 -0
- data/spec/cassettes/asin/lookup_and_search_should_lookup_a_book.yml +81 -0
- data/spec/cassettes/asin/lookup_and_search_should_lookup_multiple_books.yml +131 -0
- data/spec/cassettes/asin/lookup_and_search_should_lookup_multiple_response_groups.yml +43 -0
- data/spec/cassettes/asin/lookup_and_search_should_return_a_custom_item_class.yml +81 -0
- data/spec/cassettes/asin/lookup_and_search_should_return_a_mash_value.yml +81 -0
- data/spec/cassettes/asin/lookup_and_search_should_return_a_rash_value.yml +81 -0
- data/spec/cassettes/asin/lookup_and_search_should_return_a_raw_value.yml +81 -0
- data/spec/cassettes/asin/lookup_and_search_should_search_keywords_a_book_with_fulltext.yml +538 -0
- data/spec/cassettes/asin/lookup_and_search_should_search_keywords_and_handle_a_single_result.yml +72 -0
- data/spec/cassettes/asin/lookup_and_search_should_search_keywords_never_mind_music.yml +119 -0
- data/spec/cassettes/asin/lookup_and_search_should_search_music.yml +37 -0
- data/spec/cassettes/asin/lookup_and_search_should_search_never_mind_music.yml +122 -0
- data/spec/config_spec.rb +53 -0
- data/spec/search_spec.rb +101 -0
- data/spec/spec_helper.rb +47 -0
- metadata +197 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
== 0.7.0
|
2
|
+
|
3
|
+
* jruby compatible
|
4
|
+
* loosen gem dependencies
|
5
|
+
|
6
|
+
== 0.6.1
|
7
|
+
|
8
|
+
* fix error when passing nil to config values
|
9
|
+
|
10
|
+
== 0.6.0
|
11
|
+
|
12
|
+
* change lookup method - pull request https://github.com/phoet/asin/pull/8
|
13
|
+
|
14
|
+
== 0.5.1
|
15
|
+
|
16
|
+
* fix for https://github.com/phoet/asin/issues/7
|
17
|
+
|
18
|
+
== 0.5.0
|
19
|
+
|
20
|
+
* move client to own file
|
21
|
+
* use autoload
|
22
|
+
* support for Hashie::Rash
|
23
|
+
* new method browse_node
|
24
|
+
|
25
|
+
== 0.4.0
|
26
|
+
|
27
|
+
* add configuration option for item/cart class
|
28
|
+
* add more functionality to item class
|
29
|
+
|
30
|
+
== 0.4.0.beta1
|
31
|
+
|
32
|
+
* added cart operations
|
33
|
+
* added yml configuration
|
34
|
+
|
35
|
+
== 0.3.0
|
36
|
+
|
37
|
+
* add search_keywords method
|
38
|
+
* open up search method to be more flexible
|
39
|
+
|
40
|
+
== 0.2.0
|
41
|
+
|
42
|
+
* rails initializer configuration
|
43
|
+
* rpsec for tests
|
44
|
+
|
45
|
+
== 0.1.0
|
46
|
+
|
47
|
+
* add logger
|
48
|
+
* use HTTPI as HTTP-adapter
|
49
|
+
* use bundler for dependencies
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
asin (0.7.0)
|
5
|
+
crack (~> 0.3)
|
6
|
+
hashie (~> 1.1)
|
7
|
+
httpi (~> 0.9)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
addressable (2.2.6)
|
13
|
+
crack (0.3.1)
|
14
|
+
diff-lcs (1.1.3)
|
15
|
+
fuubar (0.0.6)
|
16
|
+
rspec (~> 2.0)
|
17
|
+
rspec-instafail (~> 0.1.8)
|
18
|
+
ruby-progressbar (~> 0.0.10)
|
19
|
+
hashie (1.1.0)
|
20
|
+
httpclient (2.2.3)
|
21
|
+
httpi (0.9.5)
|
22
|
+
rack
|
23
|
+
rack (1.4.1)
|
24
|
+
rake (0.9.2.2)
|
25
|
+
rash (0.3.1)
|
26
|
+
hashie (~> 1.1.0)
|
27
|
+
rspec (2.7.0)
|
28
|
+
rspec-core (~> 2.7.0)
|
29
|
+
rspec-expectations (~> 2.7.0)
|
30
|
+
rspec-mocks (~> 2.7.0)
|
31
|
+
rspec-core (2.7.1)
|
32
|
+
rspec-expectations (2.7.0)
|
33
|
+
diff-lcs (~> 1.1.2)
|
34
|
+
rspec-instafail (0.1.9)
|
35
|
+
rspec-mocks (2.7.0)
|
36
|
+
ruby-progressbar (0.0.10)
|
37
|
+
vcr (1.11.3)
|
38
|
+
webmock (1.7.7)
|
39
|
+
addressable (~> 2.2, > 2.2.5)
|
40
|
+
crack (>= 0.1.7)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
java
|
44
|
+
ruby
|
45
|
+
|
46
|
+
DEPENDENCIES
|
47
|
+
asin!
|
48
|
+
fuubar (~> 0.0.5)
|
49
|
+
httpclient (~> 2.2.3)
|
50
|
+
rake (~> 0.9.2.2)
|
51
|
+
rash (~> 0.3.1)
|
52
|
+
rspec (~> 2.7.0)
|
53
|
+
vcr (~> 1.11.3)
|
54
|
+
webmock (~> 1.7.7)
|
data/README.rdoc
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
== Infos
|
2
|
+
|
3
|
+
Status: http://stillmaintained.com/phoet/asin.png
|
4
|
+
Build: http://travis-ci.org/phoet/asin.png
|
5
|
+
|
6
|
+
ASIN is a simple, extensible wrapper for parts of the REST-API of Amazon Product Advertising API (aka Associates Web Service aka Amazon E-Commerce Service).
|
7
|
+
|
8
|
+
For more information on the REST calls, have a look at the whole Amazon E-Commerce-API[http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html].
|
9
|
+
|
10
|
+
Have a look at the RDOC[http://rdoc.info/projects/phoet/asin] for this project, if you like browsing some docs.
|
11
|
+
|
12
|
+
The gem runs smoothly with Rails 3 and is tested against multiple rubies. See +.travis.yml+ for details.
|
13
|
+
|
14
|
+
== Installation
|
15
|
+
|
16
|
+
gem install asin
|
17
|
+
gem install httpclient # optional, see HTTPI
|
18
|
+
gem install rash # optional, see Response Configuration
|
19
|
+
|
20
|
+
or in your Gemfile:
|
21
|
+
|
22
|
+
gem 'asin'
|
23
|
+
gem 'httpclient' # optional, see HTTPI
|
24
|
+
gem 'rash' # optional, see Response Configuration
|
25
|
+
|
26
|
+
== Configuration
|
27
|
+
|
28
|
+
Rails style initializer (config/initializers/asin.rb):
|
29
|
+
|
30
|
+
ASIN::Configuration.configure do |config|
|
31
|
+
config.secret = 'your-secret'
|
32
|
+
config.key = 'your-key'
|
33
|
+
end
|
34
|
+
|
35
|
+
YAML style configuration:
|
36
|
+
|
37
|
+
ASIN::Configuration.configure :yaml => 'config/asin.yml'
|
38
|
+
|
39
|
+
Inline style configuration:
|
40
|
+
|
41
|
+
ASIN::Configuration.configure :secret => 'your-secret', :key => 'your-key'
|
42
|
+
# or
|
43
|
+
client.configure :secret => 'your-secret', :key => 'your-key'
|
44
|
+
|
45
|
+
Have a look at ASIN::Configuration class for all the details.
|
46
|
+
|
47
|
+
===API Changes
|
48
|
+
|
49
|
+
With the latest version of the Product Advertising API you need to include your associate_tag[https://affiliate-program.amazon.com/gp/advertising/api/detail/api-changes.html].
|
50
|
+
|
51
|
+
ASIN::Configuration.configure do |config|
|
52
|
+
config.secret = 'your-secret'
|
53
|
+
config.key = 'your-key'
|
54
|
+
config.associate_tag = 'your-tag'
|
55
|
+
end
|
56
|
+
|
57
|
+
== Usage
|
58
|
+
|
59
|
+
ASIN is designed as a module, so you can include it into any object you like:
|
60
|
+
|
61
|
+
# require and include
|
62
|
+
require 'asin'
|
63
|
+
include ASIN::Client
|
64
|
+
|
65
|
+
# lookup an ASIN
|
66
|
+
lookup '1430218150'
|
67
|
+
|
68
|
+
But you can also use the +instance+ method to get a proxy-object:
|
69
|
+
|
70
|
+
# just require
|
71
|
+
require 'asin'
|
72
|
+
|
73
|
+
# create an ASIN client
|
74
|
+
client = ASIN::Client.instance
|
75
|
+
|
76
|
+
# lookup an item with the amazon standard identification number (asin)
|
77
|
+
items = client.lookup '1430218150'
|
78
|
+
|
79
|
+
# have a look at the title of the item
|
80
|
+
items.first.title
|
81
|
+
=> Learn Objective-C on the Mac (Learn Series)
|
82
|
+
|
83
|
+
# search for any kind of stuff on amazon with keywords
|
84
|
+
items = search_keywords 'Learn', 'Objective-C'
|
85
|
+
items.first.title
|
86
|
+
=> "Learn Objective-C on the Mac (Learn Series)"
|
87
|
+
|
88
|
+
# search for any kind of stuff on amazon with custom parameters
|
89
|
+
search :Keywords => 'Learn Objective-C', :SearchIndex => :Books
|
90
|
+
items.first.title
|
91
|
+
=> "Learn Objective-C on the Mac (Learn Series)"
|
92
|
+
|
93
|
+
# access the internal data representation (Hashie::Mash)
|
94
|
+
item.raw.ItemAttributes.ListPrice.FormattedPrice
|
95
|
+
=> $39.99
|
96
|
+
|
97
|
+
There is an additional set of methods to support AWS cart operations:
|
98
|
+
|
99
|
+
client = ASIN::Client.instance
|
100
|
+
|
101
|
+
# create a cart with an item
|
102
|
+
cart = client.create_cart({:asin => '1430218150', :quantity => 1})
|
103
|
+
cart.items
|
104
|
+
=> [<#Hashie::Mash ASIN="1430218150" CartItemId="U3G241HVLLB8N6" ... >]
|
105
|
+
|
106
|
+
# get an already existing cart from a CartId and HMAC
|
107
|
+
cart = client.get_cart('176-9182855-2326919', 'KgeVCA0YJTbuN/7Ibakrk/KnHWA=')
|
108
|
+
cart.empty?
|
109
|
+
=> false
|
110
|
+
|
111
|
+
# clear everything from the cart
|
112
|
+
cart = client.clear_cart(cart)
|
113
|
+
cart.empty?
|
114
|
+
=> true
|
115
|
+
|
116
|
+
# add items to the cart
|
117
|
+
cart = client.add_items(cart, {:asin => '1430216263', :quantity => 2})
|
118
|
+
cart.empty?
|
119
|
+
=> false
|
120
|
+
|
121
|
+
# update items in the cart
|
122
|
+
cart = client.update_items(cart, {:cart_item_id => cart.items.first.CartItemId, :action => :SaveForLater}, {:cart_item_id => cart.items.first.CartItemId, :quantity => 7})
|
123
|
+
cart.saved_items
|
124
|
+
=> [<#Hashie::Mash ASIN="1430218150" CartItemId="U3G241HVLLB8N6" ... >]
|
125
|
+
|
126
|
+
It's also possible to access browse nodes:
|
127
|
+
|
128
|
+
client = ASIN::Client.instance
|
129
|
+
|
130
|
+
# create a cart with an item
|
131
|
+
node = client.browse_node('163357', :ResponseGroup => :TopSellers)
|
132
|
+
node.node_id
|
133
|
+
=> '163357'
|
134
|
+
node.name
|
135
|
+
=> 'Comedy'
|
136
|
+
|
137
|
+
== Response Configuration
|
138
|
+
|
139
|
+
ASIN is customizable in the way it returns Responses from Amazon.
|
140
|
+
By default it will return +SimpleItem+, +SimpleCart+ or +SimpleNode+ instances,
|
141
|
+
but you can override this behavior for using your custom Classes:
|
142
|
+
|
143
|
+
client.configure :item_type => YourItemClass
|
144
|
+
client.configure :cart_type => YourCartClass
|
145
|
+
client.configure :node_type => YourNodeClass
|
146
|
+
|
147
|
+
You can also use built-in +:raw+, +:mash+ or +:rash+ types.
|
148
|
+
Since +rash+ is an additional library, you need to add it to your gemfile if you want to use it:
|
149
|
+
|
150
|
+
gem 'rash'
|
151
|
+
|
152
|
+
== HTTPI
|
153
|
+
|
154
|
+
ASIN uses HTTPI[https://github.com/rubiii/httpi] as a HTTP-Client adapter.
|
155
|
+
See the HTTPI documentation for how to configure different clients or the logger.
|
156
|
+
As a default HTTPI uses _httpclient_ so you should add that dependency to your project:
|
157
|
+
|
158
|
+
gem 'httpclient'
|
159
|
+
|
160
|
+
|
161
|
+
== License
|
162
|
+
|
163
|
+
"THE BEER-WARE LICENSE" (Revision 42):
|
164
|
+
ps@nofail.de[mailto:ps@nofail.de] wrote this file. As long as you retain this notice you
|
165
|
+
can do whatever you want with this stuff. If we meet some day, and you think
|
166
|
+
this stuff is worth it, you can buy me a beer in return Peter Schröder
|
data/asin.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "asin/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rasin"
|
7
|
+
s.version = ASIN::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Peter Schröder']
|
10
|
+
s.email = ['phoetmail@googlemail.com']
|
11
|
+
s.homepage = 'http://github.com/phoet/asin'
|
12
|
+
s.summary = 'Simple interface to AWS Lookup, Search and Cart operations.'
|
13
|
+
s.description = 'Amazon Simple INterface.'
|
14
|
+
|
15
|
+
s.rubyforge_project = "asin"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency('crack', '~> 0.3')
|
23
|
+
s.add_dependency('hashie', '~> 1.1')
|
24
|
+
s.add_dependency('httpi', '~> 0.9')
|
25
|
+
|
26
|
+
s.add_runtime_dependency('jruby-openssl') if RUBY_PLATFORM == 'java'
|
27
|
+
|
28
|
+
s.add_development_dependency('httpclient', '~> 2.2.3')
|
29
|
+
s.add_development_dependency('rash', '~> 0.3.1')
|
30
|
+
|
31
|
+
s.add_development_dependency('rake', '~> 0.9.2.2')
|
32
|
+
s.add_development_dependency('vcr', '~> 1.11.3')
|
33
|
+
s.add_development_dependency('webmock', '~> 1.7.7')
|
34
|
+
s.add_development_dependency('rspec', '~> 2.7.0')
|
35
|
+
s.add_development_dependency('fuubar', '~> 0.0.5')
|
36
|
+
end
|
data/lib/asin.rb
ADDED
data/lib/asin/client.rb
ADDED
@@ -0,0 +1,398 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'httpi'
|
3
|
+
require 'crack/xml'
|
4
|
+
require 'cgi'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
# ASIN (Amazon Simple INterface) is a gem for easy access of the Amazon E-Commerce-API.
|
8
|
+
# It is simple to configure and use. Since it's very small and flexible, it is easy to extend it to your needs.
|
9
|
+
#
|
10
|
+
# Author:: Peter Schröder (mailto:phoetmail@googlemail.com)
|
11
|
+
#
|
12
|
+
# == Usage
|
13
|
+
#
|
14
|
+
# The ASIN module is designed as a mixin.
|
15
|
+
#
|
16
|
+
# require 'asin'
|
17
|
+
# include ASIN::Client
|
18
|
+
#
|
19
|
+
# In order to use the Amazon API properly, you need to be a registered user (http://aws.amazon.com).
|
20
|
+
#
|
21
|
+
# The registration process will give you a +secret-key+ and an +access-key+ (AWSAccessKeyId).
|
22
|
+
#
|
23
|
+
# Both are needed to use ASIN (see Configuration for more details):
|
24
|
+
#
|
25
|
+
# configure :secret => 'your-secret', :key => 'your-key'
|
26
|
+
#
|
27
|
+
# == Search
|
28
|
+
#
|
29
|
+
# After configuring your environment you can call the +lookup+ method to retrieve an +SimpleItem+ via the
|
30
|
+
# Amazon Standard Identification Number (ASIN):
|
31
|
+
#
|
32
|
+
# item = lookup '1430218150'
|
33
|
+
# item.first.title
|
34
|
+
# => "Learn Objective-C on the Mac (Learn Series)"
|
35
|
+
#
|
36
|
+
# OR search with fulltext/ASIN/ISBN
|
37
|
+
#
|
38
|
+
# items = search 'Learn Objective-C'
|
39
|
+
# items.first.title
|
40
|
+
# => "Learn Objective-C on the Mac (Learn Series)"
|
41
|
+
#
|
42
|
+
# The +SimpleItem+ uses a Hashie::Mash as its internal data representation and you can get fetched data from it:
|
43
|
+
#
|
44
|
+
# item.raw.ItemAttributes.ListPrice.FormattedPrice
|
45
|
+
# => "$39.99"
|
46
|
+
#
|
47
|
+
# == Further Configuration
|
48
|
+
#
|
49
|
+
# If you need more controll over the request that is sent to the
|
50
|
+
# Amazon API (http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html),
|
51
|
+
# you can override some defaults or add additional query-parameters to the REST calls:
|
52
|
+
#
|
53
|
+
# configure :host => 'webservices.amazon.de'
|
54
|
+
# lookup(asin, :ResponseGroup => :Medium)
|
55
|
+
#
|
56
|
+
# == Cart
|
57
|
+
#
|
58
|
+
# ASIN helps with AWS cart-operations.
|
59
|
+
# It currently supports the CartCreate, CartGet, CartAdd, CartModify and CartClear operations:
|
60
|
+
#
|
61
|
+
# cart = create_cart({:asin => '1430218150', :quantity => 1})
|
62
|
+
# cart.valid?
|
63
|
+
# cart.items
|
64
|
+
# => true
|
65
|
+
# => [<#Hashie::Mash ASIN="1430218150" CartItemId="U3G241HVLLB8N6" ... >]
|
66
|
+
#
|
67
|
+
# cart = get_cart('176-9182855-2326919', 'KgeVCA0YJTbuN/7Ibakrk/KnHWA=')
|
68
|
+
# cart.empty?
|
69
|
+
# => false
|
70
|
+
#
|
71
|
+
# cart = clear_cart(cart)
|
72
|
+
# cart.empty?
|
73
|
+
# => true
|
74
|
+
#
|
75
|
+
# cart = add_items(cart, {:asin => '1430216263', :quantity => 2})
|
76
|
+
# cart.empty?
|
77
|
+
# => false
|
78
|
+
#
|
79
|
+
# cart = update_items(cart, {:cart_item_id => cart.items.first.CartItemId, :action => :SaveForLater}, {:cart_item_id => cart.items.first.CartItemId, :quantity => 7})
|
80
|
+
# cart.valid?
|
81
|
+
# cart.saved_items
|
82
|
+
# => true
|
83
|
+
# => [<#Hashie::Mash ASIN="1430218150" CartItemId="U3G241HVLLB8N6" ... >]
|
84
|
+
#
|
85
|
+
# == Nodes
|
86
|
+
#
|
87
|
+
# In order to browse Amazon nodes, you can use +browse_node+ method:
|
88
|
+
#
|
89
|
+
# node = browse_node('163357')
|
90
|
+
# node.node_id
|
91
|
+
# => '163357'
|
92
|
+
# node.name
|
93
|
+
# => 'Comedy'
|
94
|
+
# node.children
|
95
|
+
# node.ancestors
|
96
|
+
#
|
97
|
+
# you can configure the +:ResponseGroup+ option to your needs:
|
98
|
+
#
|
99
|
+
# node = browse_node('163357', :ResponseGroup => :TopSellers)
|
100
|
+
#
|
101
|
+
module ASIN
|
102
|
+
module Client
|
103
|
+
|
104
|
+
DIGEST = OpenSSL::Digest::Digest.new('sha256')
|
105
|
+
PATH = '/onca/xml'
|
106
|
+
|
107
|
+
# Convenience method to create an ASIN client.
|
108
|
+
#
|
109
|
+
# An instance is not necessary though, you can simply include the ASIN module otherwise.
|
110
|
+
#
|
111
|
+
def self.instance
|
112
|
+
ins = Object.new
|
113
|
+
ins.extend ASIN::Client
|
114
|
+
ins
|
115
|
+
end
|
116
|
+
|
117
|
+
# Configures the basic request parameters for ASIN.
|
118
|
+
#
|
119
|
+
# Expects at least +secret+ and +key+ for the API call:
|
120
|
+
#
|
121
|
+
# configure :secret => 'your-secret', :key => 'your-key'
|
122
|
+
#
|
123
|
+
# See ASIN::Configuration for more infos.
|
124
|
+
#
|
125
|
+
def configure(options={})
|
126
|
+
Configuration.configure(options)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Performs an +ItemLookup+ REST call against the Amazon API.
|
130
|
+
#
|
131
|
+
# Expects an arbitrary number of ASIN (Amazon Standard Identification Number) and returns an array of +SimpleItem+:
|
132
|
+
#
|
133
|
+
# item = lookup '1430218150'
|
134
|
+
# item.title
|
135
|
+
# => "Learn Objective-C on the Mac (Learn Series)"
|
136
|
+
# items = lookup ['1430218150', '0439023521']
|
137
|
+
# items[0].title
|
138
|
+
# => "Learn Objective-C on the Mac (Learn Series)"
|
139
|
+
# items[1].title
|
140
|
+
# => "The Hunger Games"
|
141
|
+
#
|
142
|
+
# ==== Options:
|
143
|
+
#
|
144
|
+
# Additional parameters for the API call like this:
|
145
|
+
#
|
146
|
+
# lookup(asin, :ResponseGroup => :Medium)
|
147
|
+
#
|
148
|
+
# Or with multiple parameters:
|
149
|
+
#
|
150
|
+
# lookup(asin, :ResponseGroup => [:Small, :AlternateVersions])
|
151
|
+
#
|
152
|
+
def lookup(*asins)
|
153
|
+
params = asins.last.is_a?(Hash) ? asins.pop : {:ResponseGroup => :Medium}
|
154
|
+
response = call(params.merge(:Operation => :ItemLookup, :ItemId => asins.join(',')))
|
155
|
+
[arrayfy(response['ItemLookupResponse']['Items']['Item']).map {|item| handle_item(item)},response]
|
156
|
+
end
|
157
|
+
|
158
|
+
# Performs an +ItemSearch+ REST call against the Amazon API.
|
159
|
+
#
|
160
|
+
# Expects a search-string which can be an arbitrary array of strings (ASINs f.e.) and returns a list of +SimpleItem+s:
|
161
|
+
#
|
162
|
+
# items = search_keywords 'Learn', 'Objective-C'
|
163
|
+
# items.first.title
|
164
|
+
# => "Learn Objective-C on the Mac (Learn Series)"
|
165
|
+
#
|
166
|
+
# ==== Options:
|
167
|
+
#
|
168
|
+
# Additional parameters for the API call like this:
|
169
|
+
#
|
170
|
+
# search_keywords('nirvana', 'never mind', :SearchIndex => :Music)
|
171
|
+
#
|
172
|
+
# Have a look at the different search index values on the Amazon-Documentation[http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html]
|
173
|
+
#
|
174
|
+
def search_keywords(*keywords)
|
175
|
+
params = keywords.last.is_a?(Hash) ? keywords.pop : {:SearchIndex => :Books, :ResponseGroup => :Medium}
|
176
|
+
response = call(params.merge(:Operation => :ItemSearch, :Keywords => keywords.join(' ')))
|
177
|
+
|
178
|
+
[arrayfy(response['ItemSearchResponse']['Items']['Item']).map {|item| handle_item(item)},response]
|
179
|
+
end
|
180
|
+
|
181
|
+
# Performs an +ItemSearch+ REST call against the Amazon API.
|
182
|
+
#
|
183
|
+
# Expects a Hash of search params where and returns a list of +SimpleItem+s:
|
184
|
+
#
|
185
|
+
# items = search :SearchIndex => :Music
|
186
|
+
#
|
187
|
+
# ==== Options:
|
188
|
+
#
|
189
|
+
# Additional parameters for the API call like this:
|
190
|
+
#
|
191
|
+
# search(:Keywords => 'nirvana', :SearchIndex => :Music)
|
192
|
+
#
|
193
|
+
# Have a look at the different search index values on the Amazon-Documentation[http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html]
|
194
|
+
#
|
195
|
+
def search(params={:SearchIndex => :Books, :ResponseGroup => :Medium})
|
196
|
+
response = call(params.merge(:Operation => :ItemSearch))
|
197
|
+
[arrayfy(response['ItemSearchResponse']['Items']['Item']).map {|item| handle_item(item)},response]
|
198
|
+
end
|
199
|
+
|
200
|
+
# Performs an +BrowseNodeLookup+ REST call against the Amazon API.
|
201
|
+
#
|
202
|
+
# Expects a Hash of search params where and returns a +SimpleNode+:
|
203
|
+
#
|
204
|
+
# node = browse_node '163357'
|
205
|
+
#
|
206
|
+
# ==== Options:
|
207
|
+
#
|
208
|
+
# Additional parameters for the API call like this:
|
209
|
+
#
|
210
|
+
# browse_node('163357', :ResponseGroup => :TopSellers)
|
211
|
+
#
|
212
|
+
# Have a look at the different browse node values on the Amazon-Documentation[http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html]
|
213
|
+
#
|
214
|
+
def browse_node(node_id, params={:ResponseGroup => :BrowseNodeInfo})
|
215
|
+
response = call(params.merge(:Operation => :BrowseNodeLookup, :BrowseNodeId => node_id))
|
216
|
+
[handle_type(response['BrowseNodeLookupResponse']['BrowseNodes']['BrowseNode'], Configuration.node_type),response]
|
217
|
+
end
|
218
|
+
|
219
|
+
# Performs an +CartCreate+ REST call against the Amazon API.
|
220
|
+
#
|
221
|
+
# Expects one ore more item-hashes and returns a +SimpleCart+:
|
222
|
+
#
|
223
|
+
# cart = create_cart({:asin => '1430218150', :quantity => 1})
|
224
|
+
#
|
225
|
+
# ==== Options:
|
226
|
+
#
|
227
|
+
# Additional parameters for the API call like this:
|
228
|
+
#
|
229
|
+
# create_cart({:asin => '1430218150', :quantity => 1}, {:asin => '1430216263', :quantity => 1, :action => :SaveForLater})
|
230
|
+
#
|
231
|
+
# Have a look at the different cart item operation values on the Amazon-Documentation[http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html]
|
232
|
+
#
|
233
|
+
def create_cart(*items)
|
234
|
+
cart(:CartCreate, create_item_params(items))
|
235
|
+
end
|
236
|
+
|
237
|
+
# Performs an +CartGet+ REST call against the Amazon API.
|
238
|
+
#
|
239
|
+
# Expects the CartId and the HMAC to identify the returning +SimpleCart+:
|
240
|
+
#
|
241
|
+
# cart = get_cart('176-9182855-2326919', 'KgeVCA0YJTbuN/7Ibakrk/KnHWA=')
|
242
|
+
#
|
243
|
+
def get_cart(cart_id, hmac)
|
244
|
+
cart(:CartGet, {:CartId => cart_id, :HMAC => hmac})
|
245
|
+
end
|
246
|
+
|
247
|
+
# Performs an +CartAdd+ REST call against the Amazon API.
|
248
|
+
#
|
249
|
+
# Expects a +SimpleCart+ created with +create_cart+ and one ore more Item-Hashes and returns an updated +SimpleCart+:
|
250
|
+
#
|
251
|
+
# cart = add_items(cart, {:asin => '1430216263', :quantity => 2})
|
252
|
+
#
|
253
|
+
# ==== Options:
|
254
|
+
#
|
255
|
+
# Additional parameters for the API call like this:
|
256
|
+
#
|
257
|
+
# add_items(cart, {:asin => '1430218150', :quantity => 1}, {:asin => '1430216263', :quantity => 1, :action => :SaveForLater})
|
258
|
+
#
|
259
|
+
# Have a look at the different cart item operation values on the Amazon-Documentation[http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html]
|
260
|
+
#
|
261
|
+
def add_items(cart, *items)
|
262
|
+
cart(:CartAdd, create_item_params(items).merge({:CartId => cart.cart_id, :HMAC => cart.hmac}))
|
263
|
+
end
|
264
|
+
|
265
|
+
# Performs an +CartModify+ REST call against the Amazon API.
|
266
|
+
#
|
267
|
+
# Expects a +SimpleCart+ created with +create_cart+ and one ore more Item-Hashes to modify and returns an updated +SimpleCart+:
|
268
|
+
#
|
269
|
+
# cart = update_items(cart, {:cart_item_id => cart.items.first.CartItemId, :quantity => 7})
|
270
|
+
#
|
271
|
+
# ==== Options:
|
272
|
+
#
|
273
|
+
# Additional parameters for the API call like this:
|
274
|
+
#
|
275
|
+
# update_items(cart, {:cart_item_id => cart.items.first.CartItemId, :action => :SaveForLater}, {:cart_item_id => cart.items.first.CartItemId, :quantity => 7})
|
276
|
+
#
|
277
|
+
# Have a look at the different cart item operation values on the Amazon-Documentation[http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html]
|
278
|
+
#
|
279
|
+
def update_items(cart, *items)
|
280
|
+
cart(:CartModify, create_item_params(items).merge({:CartId => cart.cart_id, :HMAC => cart.hmac}))
|
281
|
+
end
|
282
|
+
|
283
|
+
# Performs an +CartClear+ REST call against the Amazon API.
|
284
|
+
#
|
285
|
+
# Expects a +SimpleCart+ created with +create_cart+ and returns an empty +SimpleCart+:
|
286
|
+
#
|
287
|
+
# cart = clear_cart(cart)
|
288
|
+
#
|
289
|
+
def clear_cart(cart)
|
290
|
+
cart(:CartClear, {:CartId => cart.cart_id, :HMAC => cart.hmac})
|
291
|
+
end
|
292
|
+
|
293
|
+
private()
|
294
|
+
|
295
|
+
def arrayfy(item)
|
296
|
+
return [] unless item
|
297
|
+
item.is_a?(Array) ? item : [item]
|
298
|
+
end
|
299
|
+
|
300
|
+
def handle_item(item)
|
301
|
+
handle_type(item, Configuration.item_type)
|
302
|
+
end
|
303
|
+
|
304
|
+
def handle_type(data, type)
|
305
|
+
if type.is_a?(Class)
|
306
|
+
type.new(data)
|
307
|
+
elsif type == :mash
|
308
|
+
require 'hashie'
|
309
|
+
Hashie::Mash.new(data)
|
310
|
+
elsif type == :rash
|
311
|
+
require 'rash'
|
312
|
+
Hashie::Rash.new(data)
|
313
|
+
else
|
314
|
+
data
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def create_item_params(items)
|
319
|
+
keyword_mappings = {
|
320
|
+
:asin => 'ASIN',
|
321
|
+
:quantity => 'Quantity',
|
322
|
+
:cart_item_id => 'CartItemId',
|
323
|
+
:offer_listing_id => 'OfferListingId',
|
324
|
+
:action => 'Action'
|
325
|
+
}
|
326
|
+
params = {}
|
327
|
+
items.each_with_index do |item, i|
|
328
|
+
item.each do |key, value|
|
329
|
+
next unless keyword = keyword_mappings[key]
|
330
|
+
params["Item.#{i}.#{keyword}"] = value.to_s
|
331
|
+
end
|
332
|
+
end
|
333
|
+
params
|
334
|
+
end
|
335
|
+
|
336
|
+
def cart(operation, params={})
|
337
|
+
response = call(params.merge(:Operation => operation))
|
338
|
+
cart = response["#{operation}Response"]['Cart']
|
339
|
+
handle_type(cart, Configuration.cart_type)
|
340
|
+
end
|
341
|
+
|
342
|
+
def call(params)
|
343
|
+
Configuration.validate_credentials!
|
344
|
+
params[:ResponseGroup] = params[:ResponseGroup].collect{|g| g.to_s.strip}.join(',') if !params[:ResponseGroup].nil? && params[:ResponseGroup].is_a?(Array)
|
345
|
+
log(:debug, "calling with params=#{params}")
|
346
|
+
signed = create_signed_query_string(params)
|
347
|
+
|
348
|
+
url = "http://#{Configuration.host}#{PATH}?#{signed}"
|
349
|
+
log(:info, "performing rest call to url='#{url}'")
|
350
|
+
|
351
|
+
response = HTTPI.get(url)
|
352
|
+
if response.code == 200
|
353
|
+
# force utf-8 chars, works only on 1.9 string
|
354
|
+
resp = response.body
|
355
|
+
resp = resp.force_encoding('UTF-8') if resp.respond_to? :force_encoding
|
356
|
+
log(:debug, "got response='#{resp}'")
|
357
|
+
Crack::XML.parse(resp)
|
358
|
+
else
|
359
|
+
log(:error, "got response='#{response.body}'")
|
360
|
+
raise "request failed with response-code='#{response.code}'"
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def create_signed_query_string(params)
|
365
|
+
# nice tutorial http://cloudcarpenters.com/blog/amazon_products_api_request_signing/
|
366
|
+
params[:Service] = :AWSECommerceService
|
367
|
+
params[:AWSAccessKeyId] = Configuration.key
|
368
|
+
|
369
|
+
params[:Version] = Configuration.version unless Configuration.blank? :version
|
370
|
+
params[:AssociateTag] = Configuration.associate_tag unless Configuration.blank? :associate_tag
|
371
|
+
|
372
|
+
# utc timestamp needed for signing
|
373
|
+
params[:Timestamp] = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
|
374
|
+
|
375
|
+
|
376
|
+
query = create_query(params)
|
377
|
+
|
378
|
+
# yeah, you really need to sign the get-request not the query
|
379
|
+
request_to_sign = "GET\n#{Configuration.host}\n#{PATH}\n#{query}"
|
380
|
+
hmac = OpenSSL::HMAC.digest(DIGEST, Configuration.secret, request_to_sign)
|
381
|
+
|
382
|
+
# don't forget to remove the newline from base64
|
383
|
+
signature = CGI.escape(Base64.encode64(hmac).chomp)
|
384
|
+
"#{query}&Signature=#{signature}"
|
385
|
+
end
|
386
|
+
|
387
|
+
def create_query(params)
|
388
|
+
params.map do |key, value|
|
389
|
+
value = value.collect{|v| v.to_s.strip}.join(',') if value.is_a?(Array)
|
390
|
+
"#{key}=#{CGI.escape(value.to_s)}"
|
391
|
+
end.sort.join('&').gsub('+','%20') # signing needs to order the query alphabetically
|
392
|
+
end
|
393
|
+
|
394
|
+
def log(severity, message)
|
395
|
+
Configuration.logger.send severity, message if Configuration.logger
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|