a2z 0.0.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/.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