amazon-ecs 0.5.0

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