a2z 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in a2z.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Matt Huggins
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,270 @@
1
+ # A2z
2
+
3
+ A2z provides a simple Ruby DSL to retrieve product information from the
4
+ [Amazon Product Advertising API](https://affiliate-program.amazon.com/gp/advertising/api/detail/main.html).
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'a2z'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install a2z
19
+
20
+ ## Usage
21
+
22
+ The basis of the A2z gem is the `A2z::Client`. A client can be used to search
23
+ for items or lookup a specific item available on [Amazon](http://www.amazon.com).
24
+ Instantiating a client requires an API `key`, `secret`, and `tag`. To obtain
25
+ these, you'll need to [create an Amazon Product Advertising API account](https://affiliate-program.amazon.com/gp/advertising/api/detail/main.html).
26
+
27
+ Once you have these, create the client as follows:
28
+
29
+ client = A2z::Client.new(key: 'YOUR_KEY', secret: 'YOUR_SECRET', tag: 'YOUR_TAG')
30
+
31
+ By default, a client will search within Amazon US (i.e.: amazon.com). This can
32
+ be customized by passing a `country` option when initializing your client.
33
+ Valid country options are `:ca`, `:cn`, `:de`, `:es`, `:fr`, `:it`, `:jp`,
34
+ `:uk`, and `:us`. For example:
35
+
36
+ client = A2z::Client.new(country: :us, ...)
37
+
38
+ Alternative, the country can be changed after the client has been initialized.
39
+
40
+ client.country = :us
41
+
42
+ Within a Rails application, you'll probably want to keep your key, secret, and
43
+ tag information out of source control. One way to do this is to store this
44
+ information in a YAML file such as `config/amazon.yml`. For example:
45
+
46
+ key: YOUR_KEY
47
+ secret: YOUR_SECRET
48
+ tag: YOUR_TAG
49
+
50
+ This file can then be used in the following manner:
51
+
52
+ config = YAML.load_file(Rails.root.join('config', 'amazon.yml'))
53
+ client = A2z::Client.new(config)
54
+
55
+ ### Item Search
56
+
57
+ Once you have instantiated an `A2z::Client` instance, searching for products
58
+ can be done through the `item_search` method.
59
+
60
+ response = client.item_search do
61
+ category 'Music'
62
+ keywords 'Dave Matthews Band'
63
+ end
64
+
65
+ The return value is an `A2z::Responses::ItemSearch` object. The example above
66
+ would make the following calls possible.
67
+
68
+ response.valid?
69
+ => true
70
+
71
+ response.total_results
72
+ => 435
73
+
74
+ response.total_pages
75
+ => 44
76
+
77
+ response.items.size
78
+ => 10
79
+
80
+ response.items.first
81
+ # => #<A2z::Responses::Item ...>
82
+
83
+ response.more_search_results_url
84
+ => "http://www.amazon.com/gp/redirect.html?camp=2025&creative=386001&location=..."
85
+
86
+ response.operation_request
87
+ => #<A2z::Responses::OperationRequest ...>
88
+
89
+ For more information on interacting with `A2z::Responses::Item` and
90
+ `A2z::Responses::OperationRequest` objects, refer to their respective sections
91
+ below.
92
+
93
+ The following code is likely not a real-world example of how you would use
94
+ the client to perform a search; it is only intended to demonstrate the full
95
+ collection of methods provided when performing an item search.
96
+
97
+ client.item_search do
98
+ category 'Books'
99
+ keywords 'Harry Potter'
100
+ actor 'Adam Sandler'
101
+ artist 'Muse'
102
+ audience_rating 'PG-13'
103
+ author 'J.K. Rowling'
104
+ brand 'Timex'
105
+ browse_node 17
106
+ composer 'Wolfgang Amadeus Mozart'
107
+ condition 'Refurbished'
108
+ conductor 'Leopold Stokowski'
109
+ director 'Judd Apatow'
110
+ include_reviews_summary true
111
+ item_page 1
112
+ manufacturer 'Sony'
113
+ merchant_id 'SOME_MERCHANT_ID'
114
+ maximum_price 49.99
115
+ minimum_price 10.50
116
+ min_percentage_off 20
117
+ music_label 'Universal'
118
+ orchestra 'Antonio Vivaldi'
119
+ power 'subject:history and (spain or mexico) and not military and language:spanish'
120
+ publisher 'EMI'
121
+ sort 'inverse-pricerank'
122
+ title 'Harry Potter and the Chamber of Secrets'
123
+ truncate_reviews_at 0
124
+ variation_page 2
125
+ response_group('RelatedItems') do
126
+ related_item_page 2
127
+ relationship_type 'Episode'
128
+ end
129
+ end
130
+
131
+ Required arguments and argument values vary depending upon how you use the API.
132
+ For a full list of arguments and when they are required vs. optional, refer to
133
+ Amazon's [ItemSearch documentation](http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/ItemSearch.html).
134
+
135
+ ### Item Lookup
136
+
137
+ Once you have instantiated an `A2z::Client` instance, looking up a product can
138
+ be done through the `item_lookup` method.
139
+
140
+ response = client.item_lookup do
141
+ id '059035342X'
142
+ end
143
+
144
+ The return value is an `A2z::Responses::ItemLookup` object. The example above
145
+ would make the following calls possible.
146
+
147
+ response.valid?
148
+ => true
149
+
150
+ response.item
151
+ # => #<A2z::Responses::Item ...>
152
+
153
+ response.operation_request
154
+ => #<A2z::Responses::OperationRequest ...>
155
+
156
+ For more information on interacting with `A2z::Responses::Item` and
157
+ `A2z::Responses::OperationRequest` objects, refer to their respective sections
158
+ below.
159
+
160
+ The following code is likely not a real-world example of how you would use
161
+ the client to perform a lookup; it is only intended to demonstrate the full
162
+ collection of methods provided when performing an item lookup.
163
+
164
+ client.item_lookup do
165
+ category 'Books'
166
+ id '059035342X'
167
+ id_type 'ASIN'
168
+ merchant_id 'SOME_MERCHANT_ID'
169
+ truncate_reviews_at 0
170
+ variation_page 2
171
+ response_group('RelatedItems') do
172
+ related_item_page 2
173
+ relationship_type 'Episode'
174
+ end
175
+ end
176
+
177
+ Required arguments and argument values vary depending upon how you use the API.
178
+ For a full list of arguments and when they are required vs. optional, refer to
179
+ Amazon's [ItemLookup documentation](http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/ItemLookup.html).
180
+
181
+ ### Items
182
+
183
+ Item lookup and item search response objects generally include one or more
184
+ `A2z::Responses::Item` objects accessible via the `#item` and `#items`
185
+ instance methods, respectively.
186
+
187
+ For example:
188
+
189
+ response = client.item_search { ... }
190
+ => #<A2z::Responses::ItemSearch ...>
191
+
192
+ item = response.items.first
193
+ => #<A2z::Responses::Item ...>
194
+
195
+ item.asin
196
+ => "B008FERRFO"
197
+
198
+ item.title
199
+ => "Away From The World (Deluxe Version)"
200
+
201
+ item.artist
202
+ => "Dave Matthews Band"
203
+
204
+ item.detail_page_url
205
+ => ""http://www.amazon.com/Away-From-World-Deluxe-Version/dp/B008FERRFO..."
206
+
207
+ item.manufacturer
208
+ => "RCA"
209
+
210
+ item.product_group
211
+ => "Music"
212
+
213
+ Note that some attributes like `artist` and `manufacturer` are only set
214
+ depending upon the type of item. There is currently no way to determine which
215
+ attributes have been set on an item object instance. This issue will be
216
+ addressed in the next gem release.
217
+
218
+ ### Operation Requests
219
+
220
+ Item lookup and item search response objects include an
221
+ `A2z::Responses::OperationRequest` accessible via the `#operation_request`
222
+ instance method.
223
+
224
+ For example:
225
+
226
+ response = client.item_lookup { ... }
227
+ => #<A2z::Responses::ItemLookup ...>
228
+
229
+ response.operation_request.request_id
230
+ => "fc5d5321-a347-4e65-9483-9655e8d9cf16"
231
+
232
+ response.operation_request.request_processing_time
233
+ => 0.087729
234
+
235
+ response.operation_request.headers.class
236
+ => Hash
237
+
238
+ response.operation_request.headers['UserAgent']
239
+ => "Jeff/0.4.3 (Language=Ruby; localhost)"
240
+
241
+ response.operation_request.arguments.class
242
+ => Hash
243
+
244
+ response.operation_request.arguments['Operation']
245
+ => "ItemSearch"
246
+
247
+ ## Contributing
248
+
249
+ This gem is new, and as such is lacking a full feature-set for interacting with
250
+ the Product Advertising API. For example, many of the available
251
+ [operations](http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/CHAP_OperationListAlphabetical.html)
252
+ have not yet been implemented.
253
+
254
+ Additionally, not much testing has been done yet, so there are many
255
+ combinations of arguments that may result in errors or exceptions being
256
+ thrown by the gem.
257
+
258
+ As such, any and all external help is encouraged and much appreciated!
259
+
260
+ To contribute:
261
+
262
+ 1. Fork it
263
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
264
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
265
+ 4. Push to the branch (`git push origin my-new-feature`)
266
+ 5. Create new Pull Request
267
+
268
+ ## License
269
+
270
+ A2z is released under the [MIT License](http://www.opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+
4
+ Dir.glob('tasks/**/*.rake').each(&method(:import))
5
+
6
+ task default: :spec
data/a2z.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/a2z/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.author = 'Matt Huggins'
6
+ gem.email = 'matt.huggins@gmail.com'
7
+ gem.description = 'Ruby DSL for Amazon Product Advertising API'
8
+ gem.summary = 'Ruby DSL for Amazon Product Advertising API'
9
+ gem.homepage = 'https://github.com/mhuggins/a2z'
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = 'a2z'
15
+ gem.require_path = 'lib'
16
+ gem.version = A2z::VERSION
17
+
18
+ gem.add_dependency 'crack'
19
+ gem.add_dependency 'jeff'
20
+
21
+ gem.add_development_dependency 'rake'
22
+ gem.add_development_dependency 'rspec'
23
+ end
data/lib/a2z.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'a2z/blank_slate'
2
+ require 'a2z/client'
3
+ require 'a2z/helpers'
4
+ require 'a2z/requests'
5
+ require 'a2z/responses'
6
+ require 'a2z/version'
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
4
+ # All rights reserved.
5
+
6
+ # Permission is granted for use, copying, modification, distribution,
7
+ # and distribution of modified versions of this work as long as the
8
+ # above copyright notice is included.
9
+ #++
10
+
11
+ ######################################################################
12
+ # BlankSlate provides an abstract base class with no predefined
13
+ # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
14
+ # BlankSlate is useful as a base class when writing classes that
15
+ # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
16
+ #
17
+ class BlankSlate
18
+ class << self
19
+
20
+ # Hide the method named +name+ in the BlankSlate class. Don't
21
+ # hide +instance_eval+ or any method beginning with "__".
22
+ def hide(name)
23
+ methods = instance_methods.map(&:to_sym)
24
+ if methods.include?(name.to_sym) and
25
+ name !~ /^(__|instance_eval|object_id)/
26
+ @hidden_methods ||= {}
27
+ @hidden_methods[name.to_sym] = instance_method(name)
28
+ undef_method name
29
+ end
30
+ end
31
+
32
+ def find_hidden_method(name)
33
+ @hidden_methods ||= {}
34
+ @hidden_methods[name] || superclass.find_hidden_method(name)
35
+ end
36
+
37
+ # Redefine a previously hidden method so that it may be called on a blank
38
+ # slate object.
39
+ def reveal(name)
40
+ hidden_method = find_hidden_method(name)
41
+ fail "Don't know how to reveal method '#{name}'" unless hidden_method
42
+ define_method(name, hidden_method)
43
+ end
44
+ end
45
+
46
+ instance_methods.each { |m| hide(m) }
47
+ end
48
+
49
+ ######################################################################
50
+ # Since Ruby is very dynamic, methods added to the ancestors of
51
+ # BlankSlate <em>after BlankSlate is defined</em> will show up in the
52
+ # list of available BlankSlate methods. We handle this by defining a
53
+ # hook in the Object and Kernel classes that will hide any method
54
+ # defined after BlankSlate has been loaded.
55
+ #
56
+ module Kernel
57
+ class << self
58
+ alias_method :blank_slate_method_added, :method_added
59
+
60
+ # Detect method additions to Kernel and remove them in the
61
+ # BlankSlate class.
62
+ def method_added(name)
63
+ result = blank_slate_method_added(name)
64
+ return result if self != Kernel
65
+ BlankSlate.hide(name)
66
+ result
67
+ end
68
+ end
69
+ end
70
+
71
+ ######################################################################
72
+ # Same as above, except in Object.
73
+ #
74
+ class Object
75
+ class << self
76
+ alias_method :blank_slate_method_added, :method_added
77
+
78
+ # Detect method additions to Object and remove them in the
79
+ # BlankSlate class.
80
+ def method_added(name)
81
+ result = blank_slate_method_added(name)
82
+ return result if self != Object
83
+ BlankSlate.hide(name)
84
+ result
85
+ end
86
+
87
+ def find_hidden_method(name)
88
+ nil
89
+ end
90
+ end
91
+ end
92
+
93
+ ######################################################################
94
+ # Also, modules included into Object need to be scanned and have their
95
+ # instance methods removed from blank slate. In theory, modules
96
+ # included into Kernel would have to be removed as well, but a
97
+ # "feature" of Ruby prevents late includes into modules from being
98
+ # exposed in the first place.
99
+ #
100
+ class Module
101
+ alias blankslate_original_append_features append_features
102
+ def append_features(mod)
103
+ result = blankslate_original_append_features(mod)
104
+ return result if mod != Object
105
+ instance_methods.each do |name|
106
+ BlankSlate.hide(name)
107
+ end
108
+ result
109
+ end
110
+ end
data/lib/a2z/client.rb ADDED
@@ -0,0 +1,65 @@
1
+ require 'jeff'
2
+ require 'crack/xml'
3
+
4
+ module A2z
5
+ class Client
6
+ ErrorResponse = Class.new(ArgumentError)
7
+
8
+ include Jeff
9
+
10
+ attr_reader :country, :tag
11
+
12
+ HOSTS = {
13
+ ca: 'ecs.amazonaws.ca',
14
+ cn: 'webservices.amazon.cn',
15
+ de: 'ecs.amazonaws.de',
16
+ es: 'webservices.amazon.es',
17
+ fr: 'ecs.amazonaws.fr',
18
+ it: 'webservices.amazon.it',
19
+ jp: 'ecs.amazonaws.jp',
20
+ uk: 'ecs.amazonaws.co.uk',
21
+ us: 'ecs.amazonaws.com',
22
+ }.freeze
23
+
24
+ params 'AssociateTag' => -> { tag },
25
+ 'Service' => 'AWSECommerceService',
26
+ 'Version' => '2011-08-01'
27
+
28
+ def initialize(opts = {})
29
+ self.country = opts.fetch(:country, :us)
30
+ self.key = opts[:key]
31
+ self.secret = opts[:secret]
32
+ self.tag = opts[:tag]
33
+ end
34
+
35
+ def country=(code)
36
+ raise ArgumentError.new("Country code must be one of #{HOSTS.keys.join(', ')}.") if code.nil? || HOSTS[code.to_sym].nil?
37
+ @country = code.to_sym
38
+ @endpoint = "http://#{HOSTS[@country]}/onca/xml"
39
+ end
40
+
41
+ def item_search(&block)
42
+ response = request(Requests::ItemSearch.new(&block))
43
+ Responses::ItemSearch.from_response(response)
44
+ end
45
+
46
+ def item_lookup(&block)
47
+ response = request(Requests::ItemLookup.new(&block))
48
+ Responses::ItemLookup.from_response(response)
49
+ end
50
+
51
+ private
52
+
53
+ def tag=(tag)
54
+ @tag = tag
55
+ end
56
+
57
+ def request(req)
58
+ response = get(query: req.params)
59
+ response = Crack::XML.parse(response.body)
60
+
61
+ # strip the root xml node
62
+ response.values.first
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ module A2z
2
+ module Helpers
3
+ def self.included(base)
4
+ base.extend(self)
5
+ end
6
+
7
+ protected
8
+
9
+ def underscore(camel_cased_word)
10
+ camel_cased_word.dup.tap do |word|
11
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
12
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
13
+ word.tr!('-', '_')
14
+ word.downcase!
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ require 'a2z/requests/item_lookup'
2
+ require 'a2z/requests/item_search'
3
+ require 'a2z/requests/response_group'
@@ -0,0 +1,42 @@
1
+ module A2z
2
+ module Requests
3
+ class ItemLookup < BlankSlate
4
+ include Helpers
5
+
6
+ attr_reader :params
7
+
8
+ def initialize(&block)
9
+ @params = { 'Operation' => 'ItemLookup' }
10
+ instance_eval(&block) if block_given?
11
+ end
12
+
13
+ %w(Condition IdType MerchantId TruncateReviewsAt VariationPage).each do |param|
14
+ method = underscore(param)
15
+
16
+ class_eval <<-END, __FILE__, __LINE__
17
+ def #{method}(value)
18
+ @params['#{param}'] = value
19
+ end
20
+ END
21
+ end
22
+
23
+ def id(value)
24
+ value = value.join(',') if value.kind_of?(Array)
25
+ @params['ItemId'] = value
26
+ end
27
+
28
+ def category(value)
29
+ @params['SearchIndex'] = value
30
+ end
31
+
32
+ def response_group(value, &block)
33
+ response_group = ResponseGroup.new(value, &block)
34
+ @params.merge!(response_group.params)
35
+ end
36
+
37
+ def include_reviews_summary(value)
38
+ @params['IncludeReviewsSummary'] = value ? 'True' : 'False'
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ module A2z
2
+ module Requests
3
+ class ItemSearch < BlankSlate
4
+ include Helpers
5
+
6
+ attr_reader :params
7
+
8
+ def initialize(&block)
9
+ @params = { 'Operation' => 'ItemSearch' }
10
+ instance_eval(&block) if block_given?
11
+ end
12
+
13
+ %w(Actor Artist AudienceRating Author Brand BrowseNode Composer Conductor
14
+ Condition Director ItemPage Manufacturer MaximumPrice MerchantId
15
+ MinimumPrice MinPercentageOff MusicLabel Orchestra Power Publisher
16
+ Sort Title TruncateReviewsAt VariationPage).each do |param|
17
+ method = underscore(param)
18
+
19
+ class_eval <<-END, __FILE__, __LINE__
20
+ def #{method}(value)
21
+ @params['#{param}'] = value
22
+ end
23
+ END
24
+ end
25
+
26
+ def keywords(value)
27
+ value = value.join(' ') if value.kind_of?(Array)
28
+ @params['Keywords'] = value
29
+ end
30
+
31
+ def category(value)
32
+ @params['SearchIndex'] = value
33
+ end
34
+
35
+ def response_group(value, &block)
36
+ response_group = ResponseGroup.new(value, &block)
37
+ @params.merge!(response_group.params)
38
+ end
39
+
40
+ def include_reviews_summary(value)
41
+ @params['IncludeReviewsSummary'] = value ? 'True' : 'False'
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,20 @@
1
+ module A2z
2
+ module Requests
3
+ class ResponseGroup < BlankSlate
4
+ attr_reader :params
5
+
6
+ def initialize(value, &block)
7
+ @params = { 'ResponseGroup' => value }
8
+ instance_eval(&block) if block_given?
9
+ end
10
+
11
+ def related_item_page(value)
12
+ @params['RelatedItemPage'] = value
13
+ end
14
+
15
+ def relationship_type(value)
16
+ @params['RelationshipType'] = value
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ require 'a2z/responses/item'
2
+ require 'a2z/responses/item_link'
3
+ require 'a2z/responses/item_lookup'
4
+ require 'a2z/responses/item_search'
5
+ require 'a2z/responses/operation_request'
@@ -0,0 +1,35 @@
1
+ module A2z
2
+ module Responses
3
+ class Item
4
+ include Helpers
5
+
6
+ attr_accessor :asin, :detail_page_url, :links
7
+
8
+ def initialize
9
+ @links = []
10
+ end
11
+
12
+ def []=(key, value)
13
+ instance_variable_set("@#{key}".to_sym, value)
14
+ self.class.class_eval { attr_reader key.to_sym }
15
+ end
16
+
17
+ def self.from_response(data)
18
+ new.tap do |item|
19
+ item.asin = data['ASIN']
20
+ item.detail_page_url = data['DetailPageURL']
21
+
22
+ if data['ItemLinks']
23
+ item.links = data['ItemLinks']['ItemLink'].collect { |link| ItemLink.from_response(link) }
24
+ end
25
+
26
+ if data['ItemAttributes']
27
+ data['ItemAttributes'].each { |key, value| item[underscore(key)] = value }
28
+ end
29
+
30
+ item.freeze
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ module A2z
2
+ module Responses
3
+ class ItemLink
4
+ attr_accessor :description, :url
5
+
6
+ def self.from_response(data)
7
+ new.tap do |item_link|
8
+ item_link.description = data['Description']
9
+ item_link.url = data['URL']
10
+ item_link.freeze
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ module A2z
2
+ module Responses
3
+ class ItemLookup
4
+ attr_accessor :operation_request, :item, :valid
5
+
6
+ def initialize
7
+ @valid = true
8
+ end
9
+
10
+ def valid?
11
+ @valid
12
+ end
13
+
14
+ def valid=(value)
15
+ @valid = !!value
16
+ end
17
+
18
+ # TODO capture item_search_response['Items']['Request']['Errors'] into an attr_accessor value
19
+ # TODO consider capturing item_search_response['Items']['Request'] into an attr_accessor value
20
+ def self.from_response(data)
21
+ new.tap do |item_lookup|
22
+ item_lookup.operation_request = OperationRequest.from_response(data['OperationRequest']) if data['OperationRequest']
23
+ item_lookup.item = Item.from_response(data['Items']['Item']) if data['Items'] && data['Items']['Item']
24
+ item_lookup.valid = data['Items']['Request']['IsValid'] == 'True' rescue false
25
+ item_lookup.freeze
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,44 @@
1
+ module A2z
2
+ module Responses
3
+ class ItemSearch
4
+ attr_accessor :operation_request, :items, :total_results, :total_pages, :more_search_results_url, :valid
5
+
6
+ def initialize
7
+ @items = []
8
+ @total_results = 0
9
+ @total_pages = 0
10
+ @valid = true
11
+ end
12
+
13
+ def valid?
14
+ @valid
15
+ end
16
+
17
+ def valid=(value)
18
+ @valid = !!value
19
+ end
20
+
21
+ # TODO capture item_search_response['Items']['Request']['Errors'] into an attr_accessor value
22
+ # TODO consider capturing item_search_response['Items']['Request'] into an attr_accessor value
23
+ def self.from_response(data)
24
+ new.tap do |item_search|
25
+ item_search.operation_request = OperationRequest.from_response(data['OperationRequest']) if data['OperationRequest']
26
+ item_search.items = items_from_response(data)
27
+ item_search.total_results = data['Items']['TotalResults'].to_i rescue 0
28
+ item_search.total_pages = data['Items']['TotalPages'].to_i rescue 0
29
+ item_search.more_search_results_url = data['Items']['MoreSearchResultsUrl'] rescue nil
30
+ item_search.valid = data['Items']['Request']['IsValid'] == 'True' rescue false
31
+ item_search.freeze
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def self.items_from_response(data)
38
+ items = data['Items']['Item'] rescue []
39
+ items = [items].compact unless items.kind_of?(Array)
40
+ items.collect { |item| Item.from_response(item) }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ module A2z
2
+ module Responses
3
+ class OperationRequest
4
+ attr_accessor :request_id, :request_processing_time, :headers, :arguments
5
+
6
+ def initialize
7
+ @headers = []
8
+ @arguments = []
9
+ end
10
+
11
+ def self.from_response(data)
12
+ new.tap do |operation_request|
13
+ operation_request.request_id = data['RequestId']
14
+ operation_request.request_processing_time = data['RequestProcessingTime'].to_f
15
+
16
+ if data['HTTPHeaders']
17
+ headers = data['HTTPHeaders']['Header']
18
+ headers = [headers] unless headers.kind_of?(Array)
19
+ headers = headers.collect { |h| [ h['Name'], h['Value'] ] }
20
+ operation_request.headers = Hash[headers]
21
+ end
22
+
23
+ if data['Arguments']
24
+ arguments = data['Arguments']['Argument']
25
+ arguments = [arguments] unless arguments.kind_of?(Array)
26
+ arguments = arguments.collect { |a| [ a['Name'], a['Value'] ] }
27
+ operation_request.arguments = Hash[arguments]
28
+ end
29
+
30
+ operation_request.freeze
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module A2z
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z::Client do
4
+ describe '#initialize' do
5
+ subject do
6
+ A2z::Client
7
+ end
8
+
9
+ context 'when country is not specified' do
10
+ specify { expect { subject.new }.to_not raise_error }
11
+ end
12
+
13
+ context 'when country is valid' do
14
+ specify { expect { subject.new(country: :us) }.to_not raise_error }
15
+ end
16
+
17
+ context 'when country is invalid' do
18
+ specify { expect { subject.new(country: :fake) }.to raise_error }
19
+ end
20
+ end
21
+
22
+ describe '#country=' do
23
+ it 'should succeed'
24
+ end
25
+
26
+ describe '#country' do
27
+ it 'should succeed'
28
+ end
29
+
30
+ describe '#tag' do
31
+ it 'should succeed'
32
+ end
33
+
34
+ describe '#item_search' do
35
+ subject do
36
+ A2z::Client.new
37
+ end
38
+
39
+ it 'should succeed'
40
+ end
41
+
42
+ describe '#item_lookup' do
43
+ subject do
44
+ A2z::Client.new
45
+ end
46
+
47
+ it 'should succeed'
48
+ end
49
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z::Requests::ItemLookup do
4
+ subject do
5
+ A2z::Requests::ItemLookup.new(&block)
6
+ end
7
+
8
+ let(:block) { Proc.new { } }
9
+
10
+ describe '#params' do
11
+ it 'should return a hash'
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z::Requests::ItemSearch do
4
+ subject do
5
+ A2z::Requests::ItemSearch.new(&block)
6
+ end
7
+
8
+ let(:block) { Proc.new { } }
9
+
10
+ describe '#params' do
11
+ it 'should return a hash'
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z::Requests::ResponseGroup do
4
+ subject do
5
+ A2z::Requests::ResponseGroup.new(value, &block)
6
+ end
7
+
8
+ let(:value) { nil }
9
+ let(:block) { Proc.new { } }
10
+
11
+ describe '#params' do
12
+ it 'should return a hash'
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z::Responses::ItemLink do
4
+ subject do
5
+ A2z::Responses::ItemLink.from_response(item_hash)
6
+ end
7
+
8
+ let(:item_hash) { Hash.new }
9
+
10
+ describe '.from_response' do
11
+ it 'should return an item link object'
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z::Responses::ItemLookup do
4
+ subject do
5
+ A2z::Responses::ItemLookup.from_response(item_hash)
6
+ end
7
+
8
+ let(:item_hash) { Hash.new }
9
+
10
+ describe '.from_response' do
11
+ it 'should return an item lookup object'
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z::Responses::ItemSearch do
4
+ subject do
5
+ A2z::Responses::ItemSearch.from_response(item_hash)
6
+ end
7
+
8
+ let(:item_hash) { Hash.new }
9
+
10
+ describe '.from_response' do
11
+ it 'should return an item search object'
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z::Responses::Item do
4
+ subject do
5
+ A2z::Responses::Item.from_response(item_hash)
6
+ end
7
+
8
+ let(:item_hash) { Hash.new }
9
+
10
+ describe '.from_response' do
11
+ it 'should return an item object'
12
+ end
13
+
14
+ describe '#asin' do
15
+ it 'should return string'
16
+ end
17
+
18
+ describe '#detail_page_url' do
19
+ it 'should return string'
20
+ end
21
+
22
+ describe '#links' do
23
+ it 'should return array of item links'
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe A2z do
4
+ subject { A2z }
5
+
6
+ specify { subject::VERSION.should == '0.0.1' }
7
+ end
@@ -0,0 +1 @@
1
+ require 'a2z'
data/tasks/debug.rake ADDED
@@ -0,0 +1,4 @@
1
+ desc 'Open an irb session preloaded with this library'
2
+ task :console do
3
+ sh 'irb -rubygems -I lib -r a2z.rb'
4
+ end
data/tasks/rspec.rake ADDED
@@ -0,0 +1,3 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: a2z
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Matt Huggins
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-12-27 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: crack
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: jeff
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: rake
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :development
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: rspec
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ type: :development
76
+ version_requirements: *id004
77
+ description: Ruby DSL for Amazon Product Advertising API
78
+ email: matt.huggins@gmail.com
79
+ executables: []
80
+
81
+ extensions: []
82
+
83
+ extra_rdoc_files: []
84
+
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE
89
+ - README.md
90
+ - Rakefile
91
+ - a2z.gemspec
92
+ - lib/a2z.rb
93
+ - lib/a2z/blank_slate.rb
94
+ - lib/a2z/client.rb
95
+ - lib/a2z/helpers.rb
96
+ - lib/a2z/requests.rb
97
+ - lib/a2z/requests/item_lookup.rb
98
+ - lib/a2z/requests/item_search.rb
99
+ - lib/a2z/requests/response_group.rb
100
+ - lib/a2z/responses.rb
101
+ - lib/a2z/responses/item.rb
102
+ - lib/a2z/responses/item_link.rb
103
+ - lib/a2z/responses/item_lookup.rb
104
+ - lib/a2z/responses/item_search.rb
105
+ - lib/a2z/responses/operation_request.rb
106
+ - lib/a2z/version.rb
107
+ - spec/a2z/client_spec.rb
108
+ - spec/a2z/requests/item_lookup_spec.rb
109
+ - spec/a2z/requests/item_search_spec.rb
110
+ - spec/a2z/requests/response_group_spec.rb
111
+ - spec/a2z/responses/item_link_spec.rb
112
+ - spec/a2z/responses/item_lookup_spec.rb
113
+ - spec/a2z/responses/item_search_spec.rb
114
+ - spec/a2z/responses/item_spec.rb
115
+ - spec/a2z/version_spec.rb
116
+ - spec/spec_helper.rb
117
+ - tasks/debug.rake
118
+ - tasks/rspec.rake
119
+ has_rdoc: true
120
+ homepage: https://github.com/mhuggins/a2z
121
+ licenses: []
122
+
123
+ post_install_message:
124
+ rdoc_options: []
125
+
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ hash: 3
134
+ segments:
135
+ - 0
136
+ version: "0"
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ hash: 3
143
+ segments:
144
+ - 0
145
+ version: "0"
146
+ requirements: []
147
+
148
+ rubyforge_project:
149
+ rubygems_version: 1.3.7
150
+ signing_key:
151
+ specification_version: 3
152
+ summary: Ruby DSL for Amazon Product Advertising API
153
+ test_files:
154
+ - spec/a2z/client_spec.rb
155
+ - spec/a2z/requests/item_lookup_spec.rb
156
+ - spec/a2z/requests/item_search_spec.rb
157
+ - spec/a2z/requests/response_group_spec.rb
158
+ - spec/a2z/responses/item_link_spec.rb
159
+ - spec/a2z/responses/item_lookup_spec.rb
160
+ - spec/a2z/responses/item_search_spec.rb
161
+ - spec/a2z/responses/item_spec.rb
162
+ - spec/a2z/version_spec.rb
163
+ - spec/spec_helper.rb