asin 0.0.8 → 0.1.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.
@@ -0,0 +1,2 @@
1
+ lib/*.rb
2
+ README.rdoc
@@ -0,0 +1,5 @@
1
+ html
2
+ pkg
3
+ *.gem
4
+ .DS_Store
5
+ .bundle
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm use 1.9.2@asin --create
2
+ rvm wrapper 1.9.2@asin textmate
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in asin.gemspec
4
+ gemspec
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ asin (0.0.9)
5
+ crack (~> 0.1.8)
6
+ hashie (~> 0.4.0)
7
+ httpi (~> 0.7.6)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ crack (0.1.8)
13
+ hashie (0.4.0)
14
+ httpclient (2.1.5.2)
15
+ httpi (0.7.6)
16
+ rack
17
+ mocha (0.9.10)
18
+ rake
19
+ rack (1.2.1)
20
+ rake (0.8.7)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ asin!
27
+ crack (~> 0.1.8)
28
+ hashie (~> 0.4.0)
29
+ httpclient (~> 2.1.5.2)
30
+ httpi (~> 0.7.6)
31
+ mocha (~> 0.9.10)
@@ -1,20 +1,7 @@
1
- = Introduction
2
-
3
- There is already a sophisticated amazon gem out there called ruby-aaws[http://raa.ruby-lang.org/project/ruby-aws/], but ASIN in comparison is _reaaaaaaaaaly_ easy to use!
4
-
5
- It currently just supports the _ItemLookup_ via REST but is easy to extend and understand!
6
-
7
- Have a look at the RDOC[http://rdoc.info/projects/phoet/asin] for this project, if you want further information.
8
-
9
- For more information on the REST calls, have a look at the whole Amazon E-Commerce-API[http://docs.amazonwebservices.com/AWSEcommerceService/4-0/].
10
-
11
- The code currently runs best on Ruby-1.9 due to encoding issues with the Amazon REST output (if *YOU* know how to backport this to 1.8.7, you are welcome!).
12
-
13
1
  == Installation
14
2
 
15
- The gem is tested against 1.9.1 (to be compatible with Heroku Bamboo Stack) and somewhat against 1.8.7.
3
+ The gem is tested against 1.9.2, 1.9.1 and 1.8.7 (compatibility with Heroku-Bamboo-Stack[http://docs.heroku.com/stack]) and runs smoothly with Rails 3.
16
4
 
17
- rvm use 1.9.1
18
5
  gem install asin
19
6
 
20
7
  == Usage
@@ -32,6 +19,26 @@ The gem is tested against 1.9.1 (to be compatible with Heroku Bamboo Stack) and
32
19
  item.title
33
20
  => Learn Objective-C on the Mac (Learn Series)
34
21
 
22
+ # search for any kind of stuff on amazon
23
+ items = search 'Learn Objective-C'
24
+ items.first.title
25
+ => "Learn Objective-C on the Mac (Learn Series)"
26
+
35
27
  # access the internal data representation (Hashie::Mash)
36
28
  item.raw.ItemAttributes.ListPrice.FormattedPrice
37
29
  => $39.99
30
+
31
+ == HTTPI
32
+
33
+ ASIN uses HTTPI[https://github.com/rubiii/httpi] as a HTTP-Client adapter.
34
+ You can configure the Client you like via configure:
35
+
36
+ configure :client => :curb
37
+
38
+ == Infos
39
+
40
+ Have a look at the RDOC[http://rdoc.info/projects/phoet/asin] for this project, if you want further information.
41
+
42
+ For more information on the REST calls, have a look at the whole Amazon E-Commerce-API[http://docs.amazonwebservices.com/AWSEcommerceService/4-0/].
43
+
44
+ The code currently runs best on Ruby-1.9 due to encoding issues with the Amazon REST output (if *YOU* know how to backport this to 1.8.7, you are welcome!).
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "asin/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "asin"
7
+ s.version = Asin::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Peter Schröder']
10
+ s.email = ['phoetmail@googlemail.com']
11
+ s.homepage = 'http://github.com/phoet/asin'
12
+ s.summary = 'Simple interface to Amazon Item lookup.'
13
+ s.description = 'Amazon Simple INterface or whatever you want to call this.'
14
+
15
+ s.rubyforge_project = "asin"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency('crack', '~> 0.1.8')
23
+ s.add_dependency('hashie', '~> 0.4.0')
24
+ s.add_dependency('httpi', '~> 0.7.6')
25
+
26
+ s.add_development_dependency('httpclient', '~> 2.1.5.2')
27
+ s.add_development_dependency('mocha', '~> 0.9.10')
28
+ end
@@ -1,10 +1,12 @@
1
- require 'hashie'
2
- require 'httpclient'
1
+ require 'httpi'
3
2
  require 'crack/xml'
4
3
  require 'cgi'
5
4
  require 'base64'
6
5
  require 'logger'
7
6
 
7
+ require 'asin/item'
8
+ require 'asin/version'
9
+
8
10
  # ASIN (Amazon Simple INterface) is a gem for easy access of the Amazon E-Commerce-API.
9
11
  # It is simple to configure and use. Since it's very small and flexible, it is easy to extend it to your needs.
10
12
  #
@@ -25,11 +27,18 @@ require 'logger'
25
27
  #
26
28
  # configure :secret => 'your-secret', :key => 'your-key'
27
29
  #
28
- # After configuring your environment you can call the +lookup+ method to retrieve an +Item+ via the Amazon Standard Identification Number (ASIN):
30
+ # After configuring your environment you can call the +lookup+ method to retrieve an +Item+ via the
31
+ # Amazon Standard Identification Number (ASIN):
29
32
  #
30
33
  # item = lookup '1430218150'
31
34
  # item.title
32
35
  # => "Learn Objective-C on the Mac (Learn Series)"
36
+ #
37
+ # OR search with fulltext/ASIN/ISBN
38
+ #
39
+ # items = search 'Learn Objective-C'
40
+ # items.first.title
41
+ # => "Learn Objective-C on the Mac (Learn Series)"
33
42
  #
34
43
  # The +Item+ uses a Hashie::Mash as its internal data representation and you can get fetched data from it:
35
44
  #
@@ -38,7 +47,8 @@ require 'logger'
38
47
  #
39
48
  # ==Further Configuration
40
49
  #
41
- # If you need more controll over the request that is sent to the Amazon API (http://docs.amazonwebservices.com/AWSEcommerceService/4-0/),
50
+ # If you need more controll over the request that is sent to the
51
+ # Amazon API (http://docs.amazonwebservices.com/AWSEcommerceService/4-0/),
42
52
  # you can override some defaults or add additional query-parameters to the REST calls:
43
53
  #
44
54
  # configure :host => 'webservices.amazon.de'
@@ -46,26 +56,6 @@ require 'logger'
46
56
  #
47
57
  module ASIN
48
58
 
49
- # =Item
50
- #
51
- # The +Item+ class is a wrapper for the Amazon XML-REST-Response.
52
- #
53
- # A Hashie::Mash is used for the internal data representation and can be accessed over the +raw+ attribute.
54
- #
55
- class Item
56
-
57
- attr_reader :raw
58
-
59
- def initialize(hash)
60
- @raw = Hashie::Mash.new(hash).ItemLookupResponse.Items.Item
61
- end
62
-
63
- def title
64
- @raw.ItemAttributes.Title
65
- end
66
-
67
- end
68
-
69
59
  # Configures the basic request parameters for ASIN.
70
60
  #
71
61
  # Expects at least +secret+ and +key+ for the API call:
@@ -77,6 +67,7 @@ module ASIN
77
67
  # [secret] the API secret key
78
68
  # [key] the API access key
79
69
  # [host] the host, which defaults to 'webservices.amazon.com'
70
+ # [client] the client library for http (:httpclient, :curb, :net_http) see HTTPI for more information
80
71
  # [logger] a different logger than logging to STDERR
81
72
  #
82
73
  def configure(options={})
@@ -84,6 +75,7 @@ module ASIN
84
75
  :host => 'webservices.amazon.com',
85
76
  :path => '/onca/xml',
86
77
  :digest => OpenSSL::Digest::Digest.new('sha256'),
78
+ :client => :httpclient,
87
79
  :logger => Logger.new(STDERR),
88
80
  :key => '',
89
81
  :secret => '',
@@ -106,22 +98,57 @@ module ASIN
106
98
  # lookup(asin, :ResponseGroup => :Medium)
107
99
  #
108
100
  def lookup(asin, params={})
109
- Item.new(call(params.merge(:Operation => :ItemLookup, :ItemId => asin)))
101
+ response = call(params.merge(:Operation => :ItemLookup, :ItemId => asin))
102
+ Item.new(response['ItemLookupResponse']['Items']['Item'])
103
+ end
104
+
105
+ # Performs an +ItemSearch+ REST call against the Amazon API.
106
+ #
107
+ # Expects a search-string which can be an ASIN (Amazon Standard Identification Number) and returns a list of +Items+:
108
+ #
109
+ # items = search 'Learn Objective-C'
110
+ # items.first.title
111
+ # => "Learn Objective-C on the Mac (Learn Series)"
112
+ #
113
+ # ==== Options:
114
+ #
115
+ # Additional parameters for the API call like this:
116
+ #
117
+ # search(asin, :SearchIndex => :Music)
118
+ #
119
+ # Have a look at the different search index values on the Amazon-Documentation[http://docs.amazonwebservices.com/AWSEcommerceService/4-0/]
120
+ #
121
+ def search(search_string, params={:SearchIndex => :Books})
122
+ response = call(params.merge(:Operation => :ItemSearch, :Keywords => search_string))
123
+ response['ItemSearchResponse']['Items']['Item'].map {|item| Item.new(item)}
110
124
  end
111
125
 
112
126
  private
113
127
 
114
128
  def call(params)
115
129
  raise "you have to configure ASIN: 'configure :secret => 'your-secret', :key => 'your-key''" if @options.nil?
130
+
116
131
  log(:debug, "calling with params=#{params}")
117
132
  signed = create_signed_query_string(params)
133
+
118
134
  url = "http://#{@options[:host]}#{@options[:path]}?#{signed}"
119
- log(:info, "performing rest call to url='#{url}'")
120
- resp = HTTPClient.new.get_content(url)
121
- # force utf-8 chars, works only on 1.9 string
122
- resp = resp.force_encoding('UTF-8') if resp.respond_to? :force_encoding
123
- log(:debug, "got response='#{resp}'")
124
- Crack::XML.parse(resp)
135
+ log(:info, "performing rest call to url='#{url}' with client='#{@options[:client]}'")
136
+
137
+ HTTPI::Adapter.use = @options[:client]
138
+ HTTPI.logger = @options[:logger] if @options[:logger]
139
+ request = HTTPI::Request.new(url)
140
+ response = HTTPI.get(request)
141
+
142
+ if response.code == 200
143
+ # force utf-8 chars, works only on 1.9 string
144
+ resp = response.body
145
+ resp = resp.force_encoding('UTF-8') if resp.respond_to? :force_encoding
146
+ log(:debug, "got response='#{resp}'")
147
+ Crack::XML.parse(resp)
148
+ else
149
+ log(:error, "got response='#{response.body}'")
150
+ raise "request failed with response-code='#{response.code}'"
151
+ end
125
152
  end
126
153
 
127
154
  def create_signed_query_string(params)
@@ -130,12 +157,15 @@ module ASIN
130
157
  params[:AWSAccessKeyId] = @options[:key]
131
158
  # utc timestamp needed for signing
132
159
  params[:Timestamp] = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
133
-
134
- query = params.map{|key, value| "#{key}=#{CGI.escape(value.to_s)}" }.sort.join('&')
135
-
160
+
161
+ # signing needs to order the query alphabetically
162
+ query = params.map{|key, value| "#{key}=#{CGI.escape(value.to_s)}" }.sort.join('&').gsub('+','%20')
163
+
164
+ # yeah, you really need to sign the get-request not the query
136
165
  request_to_sign = "GET\n#{@options[:host]}\n#{@options[:path]}\n#{query}"
137
166
  hmac = OpenSSL::HMAC.digest(@options[:digest], @options[:secret], request_to_sign)
138
-
167
+
168
+ # don't forget to remove the newline from base64
139
169
  signature = CGI.escape(Base64.encode64(hmac).chomp)
140
170
  "#{query}&Signature=#{signature}"
141
171
  end
@@ -0,0 +1,24 @@
1
+ require 'hashie'
2
+
3
+ module ASIN
4
+
5
+ # =Item
6
+ #
7
+ # The +Item+ class is a wrapper for the Amazon XML-REST-Response.
8
+ #
9
+ # A Hashie::Mash is used for the internal data representation and can be accessed over the +raw+ attribute.
10
+ #
11
+ class Item
12
+
13
+ attr_reader :raw
14
+
15
+ def initialize(hash)
16
+ @raw = Hashie::Mash.new(hash)
17
+ end
18
+
19
+ def title
20
+ @raw.ItemAttributes.Title
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,3 @@
1
+ module Asin
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ require "bundler"
2
+ require "rake/rdoctask"
3
+ require "rake/gempackagetask"
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ spec = eval(File.new("asin.gemspec").readlines.join("\n"))
8
+
9
+ Rake::GemPackageTask.new(spec) do |pkg|
10
+ pkg.need_zip = true
11
+ pkg.need_tar = true
12
+ end
13
+
14
+ Rake::RDocTask.new(:rdoc_dev) do |rd|
15
+ rd.rdoc_files.include("lib/**/*.rb", "README.rdoc")
16
+ rd.options + ['-a', '--inline-source', '--charset=UTF-8']
17
+ end
18
+
19
+ desc "the test task"
20
+ task :test do
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new do |t|
23
+ t.libs << "test"
24
+ t.ruby_opts << "-rubygems"
25
+ t.test_files = FileList['test/test_*.rb']
26
+ t.verbose = true
27
+ end
28
+ end
29
+ task :default=>:test
@@ -1,9 +1,11 @@
1
1
  require 'test_helper'
2
2
 
3
- ANY_ASIN = '1430218150'
4
-
5
3
  class TestAsin < Test::Unit::TestCase
6
4
 
5
+
6
+ ANY_ASIN = '1430218150'
7
+ ANY_SEARCH = 'Learn Objective-C'
8
+
7
9
  def setup
8
10
  @helper = Object.new
9
11
  @helper.extend ASIN
@@ -13,12 +15,24 @@ class TestAsin < Test::Unit::TestCase
13
15
  secret = ENV['ASIN_SECRET']
14
16
  key = ENV['ASIN_KEY']
15
17
  puts "configure #{secret} and #{key} for this test"
16
-
18
+
19
+ @helper.configure :secret => secret, :key => key
20
+ item = @helper.lookup(ANY_ASIN)
21
+ assert_match(/Learn Objective/, item.title)
22
+ end
23
+
24
+ def test_search_with_configured_string
25
+ secret = ENV['ASIN_SECRET']
26
+ key = ENV['ASIN_KEY']
27
+ puts "configure #{secret} and #{key} for this test"
28
+
17
29
  @helper.configure :secret => secret, :key => key
18
- p item = @helper.lookup(ANY_ASIN)
30
+ items = @helper.search(ANY_SEARCH)
31
+ assert_equal(10, items.size)
32
+ item = items.first
19
33
  assert_match(/Learn Objective/, item.title)
20
34
  end
21
-
35
+
22
36
  def test_configure_second_time_wont_get_overridden
23
37
  config = @helper.configure :something => 'wont get overridden'
24
38
  assert_not_nil(config[:something])
@@ -27,9 +41,9 @@ class TestAsin < Test::Unit::TestCase
27
41
  assert_not_nil(config[:something])
28
42
  assert_not_nil(config[:different])
29
43
  end
30
-
44
+
31
45
  def test_error_with_not_called_configure
32
46
  assert_raise(RuntimeError) { @helper.lookup ANY_ASIN }
33
47
  end
34
48
 
35
- end
49
+ end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asin
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
7
+ - 1
8
8
  - 0
9
- - 8
10
- version: 0.0.8
9
+ version: 0.1.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - "Peter Schr\xC3\xB6der"
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-08-06 00:00:00 +02:00
17
+ date: 2011-01-04 00:00:00 +01:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -24,14 +23,13 @@ dependencies:
24
23
  requirement: &id001 !ruby/object:Gem::Requirement
25
24
  none: false
26
25
  requirements:
27
- - - ">="
26
+ - - ~>
28
27
  - !ruby/object:Gem::Version
29
- hash: 27
30
28
  segments:
31
29
  - 0
32
30
  - 1
33
- - 0
34
- version: 0.1.0
31
+ - 8
32
+ version: 0.1.8
35
33
  type: :runtime
36
34
  version_requirements: *id001
37
35
  - !ruby/object:Gem::Dependency
@@ -40,34 +38,64 @@ dependencies:
40
38
  requirement: &id002 !ruby/object:Gem::Requirement
41
39
  none: false
42
40
  requirements:
43
- - - ">="
41
+ - - ~>
44
42
  - !ruby/object:Gem::Version
45
- hash: 23
46
43
  segments:
47
44
  - 0
48
- - 2
45
+ - 4
49
46
  - 0
50
- version: 0.2.0
47
+ version: 0.4.0
51
48
  type: :runtime
52
49
  version_requirements: *id002
53
50
  - !ruby/object:Gem::Dependency
54
- name: httpclient
51
+ name: httpi
55
52
  prerelease: false
56
53
  requirement: &id003 !ruby/object:Gem::Requirement
57
54
  none: false
58
55
  requirements:
59
- - - ">="
56
+ - - ~>
60
57
  - !ruby/object:Gem::Version
61
- hash: 11
62
58
  segments:
63
- - 2
64
- - 1
65
59
  - 0
66
- version: 2.1.0
60
+ - 7
61
+ - 6
62
+ version: 0.7.6
67
63
  type: :runtime
68
64
  version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: httpclient
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 2
75
+ - 1
76
+ - 5
77
+ - 2
78
+ version: 2.1.5.2
79
+ type: :development
80
+ version_requirements: *id004
81
+ - !ruby/object:Gem::Dependency
82
+ name: mocha
83
+ prerelease: false
84
+ requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ segments:
90
+ - 0
91
+ - 9
92
+ - 10
93
+ version: 0.9.10
94
+ type: :development
95
+ version_requirements: *id005
69
96
  description: Amazon Simple INterface or whatever you want to call this.
70
- email: phoetmail@googlemail.com
97
+ email:
98
+ - phoetmail@googlemail.com
71
99
  executables: []
72
100
 
73
101
  extensions: []
@@ -75,8 +103,17 @@ extensions: []
75
103
  extra_rdoc_files: []
76
104
 
77
105
  files:
78
- - lib/asin.rb
106
+ - .document
107
+ - .gitignore
108
+ - .rvmrc
109
+ - Gemfile
110
+ - Gemfile.lock
79
111
  - README.rdoc
112
+ - asin.gemspec
113
+ - lib/asin.rb
114
+ - lib/asin/item.rb
115
+ - lib/asin/version.rb
116
+ - rakefile.rb
80
117
  - test/test_asin.rb
81
118
  - test/test_helper.rb
82
119
  has_rdoc: true
@@ -84,10 +121,8 @@ homepage: http://github.com/phoet/asin
84
121
  licenses: []
85
122
 
86
123
  post_install_message:
87
- rdoc_options:
88
- - -a
89
- - --inline-source
90
- - --charset=UTF-8
124
+ rdoc_options: []
125
+
91
126
  require_paths:
92
127
  - lib
93
128
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -95,7 +130,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
130
  requirements:
96
131
  - - ">="
97
132
  - !ruby/object:Gem::Version
98
- hash: 3
99
133
  segments:
100
134
  - 0
101
135
  version: "0"
@@ -104,13 +138,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
138
  requirements:
105
139
  - - ">="
106
140
  - !ruby/object:Gem::Version
107
- hash: 3
108
141
  segments:
109
142
  - 0
110
143
  version: "0"
111
144
  requirements: []
112
145
 
113
- rubyforge_project:
146
+ rubyforge_project: asin
114
147
  rubygems_version: 1.3.7
115
148
  signing_key:
116
149
  specification_version: 3