kineticac-amazon-ecs 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
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
+