kineticac-amazon-ecs 0.5.5

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/README ADDED
@@ -0,0 +1,97 @@
1
+ == amazon-ecs
2
+
3
+ Generic Amazon E-commerce REST API using Hpricot with configurable
4
+ default options and method call options. Uses Response and
5
+ Element wrapper classes for easy access to REST XML output. It supports ECS 4.0.
6
+
7
+ It is generic, so you can easily extend <tt>Amazon::Ecs</tt> to support
8
+ other not implemented REST operations; and it is also generic because it just wraps around
9
+ Hpricot element object, instead of providing one-to-one object/attributes to XML elements map.
10
+
11
+ If in the future, there is a change in REST XML output structure,
12
+ no changes will be required on <tt>amazon-ecs</tt> library,
13
+ instead you just need to change the element path.
14
+
15
+ Version: 0.5.4
16
+
17
+ == INSTALLATION
18
+
19
+ $ gem install amazon-ecs
20
+
21
+ == EXAMPLE
22
+
23
+ require 'amazon/ecs'
24
+
25
+ # set the default options; options will be camelized and converted to REST request parameters.
26
+ Amazon::Ecs.options = {:aWS_access_key_id => [your developer token]}
27
+
28
+ # options provided on method call will merge with the default options
29
+ res = Amazon::Ecs.item_search('ruby', {:response_group => 'Medium', :sort => 'salesrank'})
30
+
31
+ # some common response object methods
32
+ res.is_valid_request? # return true if request is valid
33
+ res.has_error? # return true if there is an error
34
+ res.error # return error message if there is any
35
+ res.total_pages # return total pages
36
+ res.total_results # return total results
37
+ res.item_page # return current page no if :item_page option is provided
38
+
39
+ # traverse through each item (Amazon::Element)
40
+ res.items.each do |item|
41
+ # retrieve string value using XML path
42
+ item.get('asin')
43
+ item.get('itemattributes/title')
44
+
45
+ # or return Amazon::Element instance
46
+ atts = item.search_and_convert('itemattributes')
47
+ atts.get('title')
48
+
49
+ # return first author or a string array of authors
50
+ atts.get('author') # 'Author 1'
51
+ atts.get_array('author') # ['Author 1', 'Author 2', ...]
52
+
53
+ # return an hash of children text values with the element names as the keys
54
+ item.get_hash('smallimage') # {:url => ..., :width => ..., :height => ...}
55
+
56
+ # note that '/' returns Hpricot::Elements array object, nil if not found
57
+ reviews = item/'editorialreview'
58
+
59
+ # traverse through Hpricot elements
60
+ reviews.each do |review|
61
+ # Getting hash value out of Hpricot element
62
+ Amazon::Element.get_hash(review) # [:source => ..., :content ==> ...]
63
+
64
+ # Or to get unescaped HTML values
65
+ Amazon::Element.get_unescaped(review, 'source')
66
+ Amazon::Element.get_unescaped(review, 'content')
67
+
68
+ # Or this way
69
+ el = Amazon::Element.new(review)
70
+ el.get_unescaped('source')
71
+ el.get_unescaped('content')
72
+ end
73
+
74
+ # returns Amazon::Element instead of string
75
+ item.search_and_convert('itemattributes').
76
+ end
77
+
78
+ Refer to Amazon ECS documentation for more information on Amazon REST request parameters and XML output:
79
+ http://docs.amazonwebservices.com/AWSEcommerceService/2006-09-13/
80
+
81
+ To get a sample of Amazon REST response XML output, use AWSZone.com scratch pad:
82
+ http://www.awszone.com/scratchpads/aws/ecs.us/index.aws
83
+
84
+ == SOURCE CODES
85
+
86
+ * http://github.com/jugend/amazon-ecs/tree/master
87
+
88
+ == LINKS
89
+
90
+ * http://amazon-ecs.rubyforge.org
91
+ * http://www.pluitsolutions.com/amazon-ecs
92
+
93
+ == LICENSE
94
+
95
+ (The MIT License)
96
+
97
+ Copyright (c) 2006 Herryanto Siatono, Pluit Solutions
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 1
4
+ :minor: 0
@@ -0,0 +1,314 @@
1
+ #--
2
+ # Copyright (c) 2006 Herryanto Siatono, Pluit Solutions
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'net/http'
25
+ require 'hpricot'
26
+ require 'cgi'
27
+
28
+ module Amazon
29
+ class RequestError < StandardError; end
30
+
31
+ class Ecs
32
+ SERVICE_URLS = {:us => 'http://webservices.amazon.com/onca/xml?Service=AWSECommerceService',
33
+ :uk => 'http://webservices.amazon.co.uk/onca/xml?Service=AWSECommerceService',
34
+ :ca => 'http://webservices.amazon.ca/onca/xml?Service=AWSECommerceService',
35
+ :de => 'http://webservices.amazon.de/onca/xml?Service=AWSECommerceService',
36
+ :jp => 'http://webservices.amazon.co.jp/onca/xml?Service=AWSECommerceService',
37
+ :fr => 'http://webservices.amazon.fr/onca/xml?Service=AWSECommerceService'
38
+ }
39
+
40
+ @@options = {}
41
+ @@debug = false
42
+
43
+ # Default search options
44
+ def self.options
45
+ @@options
46
+ end
47
+
48
+ # Set default search options
49
+ def self.options=(opts)
50
+ @@options = opts
51
+ end
52
+
53
+ # Get debug flag.
54
+ def self.debug
55
+ @@debug
56
+ end
57
+
58
+ # Set debug flag to true or false.
59
+ def self.debug=(dbg)
60
+ @@debug = dbg
61
+ end
62
+
63
+ def self.configure(&proc)
64
+ raise ArgumentError, "Block is required." unless block_given?
65
+ yield @@options
66
+ end
67
+
68
+ # Search amazon items with search terms. Default search index option is 'Books'.
69
+ # For other search type other than keywords, please specify :type => [search type param name].
70
+ def self.item_search(terms, opts = {})
71
+ opts[:operation] = 'ItemSearch'
72
+ opts[:search_index] = opts[:search_index] || 'Books'
73
+
74
+ type = opts.delete(:type)
75
+ if type
76
+ opts[type.to_sym] = terms
77
+ else
78
+ opts[:keywords] = terms
79
+ end
80
+
81
+ self.send_request(opts)
82
+ end
83
+
84
+ # Search an item by ASIN no.
85
+ def self.item_lookup(item_id, opts = {})
86
+ opts[:operation] = 'ItemLookup'
87
+ opts[:item_id] = item_id
88
+
89
+ self.send_request(opts)
90
+ end
91
+
92
+ # BrowseNodeLookup
93
+ def self.browse_node_lookup(node_id)
94
+ opts[:operation] = 'BrowseNodeLookup'
95
+ opts[:item_id] = node_id
96
+
97
+ self.send_request(opts)
98
+ end
99
+
100
+ # Generic send request to ECS REST service. You have to specify the :operation parameter.
101
+ def self.send_request(opts)
102
+ opts = self.options.merge(opts) if self.options
103
+ request_url = prepare_url(opts)
104
+ log "Request URL: #{request_url}"
105
+
106
+ res = Net::HTTP.get_response(URI::parse(request_url))
107
+ unless res.kind_of? Net::HTTPSuccess
108
+ raise Amazon::RequestError, "HTTP Response: #{res.code} #{res.message}"
109
+ end
110
+ Response.new(res.body)
111
+ end
112
+
113
+ # Response object returned after a REST call to Amazon service.
114
+ class Response
115
+ # XML input is in string format
116
+ def initialize(xml)
117
+ @doc = Hpricot(xml)
118
+ end
119
+
120
+ # Return Hpricot object.
121
+ def doc
122
+ @doc
123
+ end
124
+
125
+ # Return true if request is valid.
126
+ def is_valid_request?
127
+ (@doc/"isvalid").inner_html == "True"
128
+ end
129
+
130
+ # Return true if response has an error.
131
+ def has_error?
132
+ !(error.nil? || error.empty?)
133
+ end
134
+
135
+ # Return error message.
136
+ def error
137
+ Element.get(@doc, "error/message")
138
+ end
139
+
140
+ # Return error code
141
+ def error_code
142
+ Element.get(@doc, "error/code")
143
+ end
144
+
145
+ # Return an array of Amazon::Element item objects.
146
+ def items
147
+ unless @items
148
+ @items = (@doc/"item").collect {|item| Element.new(item)}
149
+ end
150
+ @items
151
+ end
152
+
153
+ # Return the first item (Amazon::Element)
154
+ def first_item
155
+ items.first
156
+ end
157
+
158
+ # Return current page no if :item_page option is when initiating the request.
159
+ def item_page
160
+ unless @item_page
161
+ @item_page = (@doc/"itemsearchrequest/itempage").inner_html.to_i
162
+ end
163
+ @item_page
164
+ end
165
+
166
+ # Return total results.
167
+ def total_results
168
+ unless @total_results
169
+ @total_results = (@doc/"totalresults").inner_html.to_i
170
+ end
171
+ @total_results
172
+ end
173
+
174
+ # Return total pages.
175
+ def total_pages
176
+ unless @total_pages
177
+ @total_pages = (@doc/"totalpages").inner_html.to_i
178
+ end
179
+ @total_pages
180
+ end
181
+ end
182
+
183
+ protected
184
+ def self.log(s)
185
+ return unless self.debug
186
+ if defined? RAILS_DEFAULT_LOGGER
187
+ RAILS_DEFAULT_LOGGER.error(s)
188
+ elsif defined? LOGGER
189
+ LOGGER.error(s)
190
+ else
191
+ puts s
192
+ end
193
+ end
194
+
195
+ private
196
+ def self.prepare_url(opts)
197
+ country = opts.delete(:country)
198
+ country = (country.nil?) ? 'us' : country
199
+ request_url = SERVICE_URLS[country.to_sym]
200
+ raise Amazon::RequestError, "Invalid country '#{country}'" unless request_url
201
+
202
+ qs = ''
203
+ opts.each {|k,v|
204
+ next unless v
205
+ v = v.join(',') if v.is_a? Array
206
+ qs << "&#{camelize(k.to_s)}=#{URI.encode(v.to_s)}"
207
+ }
208
+ "#{request_url}#{qs}"
209
+ end
210
+
211
+ def self.camelize(s)
212
+ s.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
213
+ end
214
+ end
215
+
216
+ # Internal wrapper class to provide convenient method to access Hpricot element value.
217
+ class Element
218
+ # Pass Hpricot::Elements object
219
+ def initialize(element)
220
+ @element = element
221
+ end
222
+
223
+ # Returns Hpricot::Elments object
224
+ def elem
225
+ @element
226
+ end
227
+
228
+ # Find Hpricot::Elements matching the given path. Example: element/"author".
229
+ def /(path)
230
+ elements = @element/path
231
+ return nil if elements.size == 0
232
+ elements
233
+ end
234
+
235
+ # Find Hpricot::Elements matching the given path, and convert to Amazon::Element.
236
+ # Returns an array Amazon::Elements if more than Hpricot::Elements size is greater than 1.
237
+ def search_and_convert(path)
238
+ elements = self./(path)
239
+ return unless elements
240
+ elements = elements.map{|element| Element.new(element)}
241
+ return elements.first if elements.size == 1
242
+ elements
243
+ end
244
+
245
+ # Get the text value of the given path, leave empty to retrieve current element value.
246
+ def get(path='')
247
+ Element.get(@element, path)
248
+ end
249
+
250
+ # Get the unescaped HTML text of the given path.
251
+ def get_unescaped(path='')
252
+ Element.get_unescaped(@element, path)
253
+ end
254
+
255
+ # Get the array values of the given path.
256
+ def get_array(path='')
257
+ Element.get_array(@element, path)
258
+ end
259
+
260
+ # Get the children element text values in hash format with the element names as the hash keys.
261
+ def get_hash(path='')
262
+ Element.get_hash(@element, path)
263
+ end
264
+
265
+ # Similar to #get, except an element object must be passed-in.
266
+ def self.get(element, path='')
267
+ return unless element
268
+ result = element.at(path)
269
+ result = result.inner_html if result
270
+ result
271
+ end
272
+
273
+ # Similar to #get_unescaped, except an element object must be passed-in.
274
+ def self.get_unescaped(element, path='')
275
+ result = get(element, path)
276
+ CGI::unescapeHTML(result) if result
277
+ end
278
+
279
+ # Similar to #get_array, except an element object must be passed-in.
280
+ def self.get_array(element, path='')
281
+ return unless element
282
+
283
+ result = element/path
284
+ if (result.is_a? Hpricot::Elements) || (result.is_a? Array)
285
+ parsed_result = []
286
+ result.each {|item|
287
+ parsed_result << Element.get(item)
288
+ }
289
+ parsed_result
290
+ else
291
+ [Element.get(result)]
292
+ end
293
+ end
294
+
295
+ # Similar to #get_hash, except an element object must be passed-in.
296
+ def self.get_hash(element, path='')
297
+ return unless element
298
+
299
+ result = element.at(path)
300
+ if result
301
+ hash = {}
302
+ result = result.children
303
+ result.each do |item|
304
+ hash[item.name.to_sym] = item.inner_html
305
+ end
306
+ hash
307
+ end
308
+ end
309
+
310
+ def to_s
311
+ elem.to_s if elem
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,110 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class Amazon::EcsTest < Test::Unit::TestCase
4
+
5
+ AWS_ACCESS_KEY_ID = '1ZD53WRGR730ZCVWBSG2'
6
+ raise "Please specify set your AWS_ACCESS_KEY_ID" if AWS_ACCESS_KEY_ID.empty?
7
+
8
+ Amazon::Ecs.configure do |options|
9
+ options[:response_group] = 'Large'
10
+ options[:aWS_access_key_id] = AWS_ACCESS_KEY_ID
11
+ end
12
+
13
+ ## Test item_search
14
+
15
+ def test_item_search
16
+ resp = Amazon::Ecs.item_search('ruby')
17
+ assert(resp.is_valid_request?)
18
+ assert(resp.total_results >= 3600)
19
+ assert(resp.total_pages >= 360)
20
+ end
21
+
22
+ def test_item_search_with_paging
23
+ resp = Amazon::Ecs.item_search('ruby', :item_page => 2)
24
+ assert resp.is_valid_request?
25
+ assert 2, resp.item_page
26
+ end
27
+
28
+ def test_item_search_with_invalid_request
29
+ resp = Amazon::Ecs.item_search(nil)
30
+ assert !resp.is_valid_request?
31
+ end
32
+
33
+ def test_item_search_with_no_result
34
+ resp = Amazon::Ecs.item_search('afdsafds')
35
+
36
+ assert resp.is_valid_request?
37
+ assert_equal "We did not find any matches for your request.",
38
+ resp.error
39
+ end
40
+
41
+ def test_item_search_uk
42
+ resp = Amazon::Ecs.item_search('ruby', :country => :uk)
43
+ assert resp.is_valid_request?
44
+ end
45
+
46
+ def test_item_search_by_author
47
+ resp = Amazon::Ecs.item_search('dave', :type => :author)
48
+ assert resp.is_valid_request?
49
+ end
50
+
51
+ def test_item_get
52
+ resp = Amazon::Ecs.item_search("0974514055")
53
+ item = resp.first_item
54
+
55
+ # test get
56
+ assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition",
57
+ item.get("itemattributes/title")
58
+
59
+ # test get_array
60
+ assert_equal ['Dave Thomas', 'Chad Fowler', 'Andy Hunt'],
61
+ item.get_array("author")
62
+
63
+ # test get_hash
64
+ small_image = item.get_hash("smallimage")
65
+
66
+ assert_equal 3, small_image.keys.size
67
+ assert_match ".jpg", small_image[:url]
68
+ assert_equal "75", small_image[:height]
69
+ assert_equal "59", small_image[:width]
70
+
71
+ # test /
72
+ reviews = item/"editorialreview"
73
+ reviews.each do |review|
74
+ # returns unescaped HTML content, Hpricot escapes all text values
75
+ assert Amazon::Element.get_unescaped(review, 'source')
76
+ assert Amazon::Element.get_unescaped(review, 'content')
77
+ end
78
+ end
79
+
80
+ ## Test item_lookup
81
+ def test_item_lookup
82
+ resp = Amazon::Ecs.item_lookup('0974514055')
83
+ assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition",
84
+ resp.first_item.get("itemattributes/title")
85
+ end
86
+
87
+ def test_item_lookup_with_invalid_request
88
+ resp = Amazon::Ecs.item_lookup(nil)
89
+ assert resp.has_error?
90
+ assert resp.error
91
+ end
92
+
93
+ def test_item_lookup_with_no_result
94
+ resp = Amazon::Ecs.item_lookup('abc')
95
+
96
+ assert resp.is_valid_request?
97
+ assert_match(/ABC is not a valid value for ItemId/, resp.error)
98
+ end
99
+
100
+ def test_search_and_convert
101
+ resp = Amazon::Ecs.item_lookup('0974514055')
102
+ title = resp.first_item.get("itemattributes/title")
103
+ authors = resp.first_item.search_and_convert("author")
104
+
105
+ assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition", title
106
+ assert authors.is_a?(Array)
107
+ assert 3, authors.size
108
+ assert_equal "Dave Thomas", authors.first.get
109
+ end
110
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/amazon/ecs'
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kineticac-amazon-ecs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.5
5
+ platform: ruby
6
+ authors:
7
+ - Herryanto Siatono
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-17 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Generic Amazon E-commerce Service (ECS) REST API. Supports ECS 4.0.
17
+ email: herryanto@pluitsolutions.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - VERSION.yml
26
+ - lib/amazon
27
+ - lib/amazon/ecs.rb
28
+ - test/amazon
29
+ - test/amazon/ecs_test.rb
30
+ - test/test_helper.rb
31
+ - README
32
+ has_rdoc: true
33
+ homepage: http://amazon-ecs.rubyforge.net/
34
+ post_install_message:
35
+ rdoc_options:
36
+ - --inline-source
37
+ - --charset=UTF-8
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ requirements: []
53
+
54
+ rubyforge_project:
55
+ rubygems_version: 1.2.0
56
+ signing_key:
57
+ specification_version: 2
58
+ summary: Generic Amazon E-commerce Service (ECS) REST API. Supports ECS 4.0.
59
+ test_files: []
60
+