amazon_associate 0.7.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,8 @@
1
+ Autotest.add_hook :initialize do |at|
2
+ at.clear_mappings
3
+
4
+ at.add_mapping(%r{^test/.*_test\.rb$}) {|f, _| [f] }
5
+ at.add_mapping(%r{^lib/amazon/(.*)\.rb$}) {|_, m| ["test/#{m[1]}_test.rb"] }
6
+ at.add_mapping(%r{^test/(test_helper)\.rb$}) { at.files_matching %r{^test/.*_test\.rb$} }
7
+ at.add_mapping(%r{^lib/.*\.rb$}) { at.files_matching %r{^test/.*_test\.rb$} }
8
+ end
@@ -0,0 +1,2 @@
1
+ *.swp
2
+ tags
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>amazon-ecs</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>org.rubypeople.rdt.core.rubybuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ <buildCommand>
14
+ <name>com.ibm.etools.validation.validationbuilder</name>
15
+ <arguments>
16
+ </arguments>
17
+ </buildCommand>
18
+ </buildSpec>
19
+ <natures>
20
+ <nature>org.radrails.rails.ui.railsnature</nature>
21
+ <nature>org.rubypeople.rdt.core.rubynature</nature>
22
+ </natures>
23
+ </projectDescription>
@@ -0,0 +1,34 @@
1
+ 0.6.1 2008-11-10
2
+ * renamed to amazon_associate
3
+
4
+ 0.6 2008-11-09
5
+ * separated the classes in ecs.rb
6
+ * filesystem caching available
7
+ * more test coverage
8
+
9
+ 0.5.4 2008-10-21
10
+ * include Chris Martin's patches
11
+ * rename Gem to match Amazon's new name for ECS - Amazon Associates API
12
+ * update to git gemspec format
13
+
14
+ 0.5.3 2007-09-12
15
+ ----------------
16
+ * send_request to use default options.
17
+
18
+ 0.5.2 2007-09-08
19
+ ----------------
20
+ * Fixed AmazonAssociate::Element.get_unescaped error when result returned for given element path is nil
21
+
22
+ 0.5.1 2007-02-08
23
+ ----------------
24
+ * Fixed Amazon Japan and France URL error
25
+ * Removed opts.delete(:search_index) from item_lookup, SearchIndex param is allowed
26
+ when looking for a book with IdType other than the ASIN.
27
+ * Check for defined? RAILS_DEFAULT_LOGGER to avoid exception for non-rails ruby app
28
+ * Added check for LOGGER constant if RAILS_DEFAULT_LOGGER is not defined
29
+ * Added Ecs.configure(&proc) method for easier configuration of default options
30
+ * Added Element#search_and_convert method
31
+
32
+ 0.5.0 2006-09-12
33
+ ----------------
34
+ Initial Release
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2008 Dan Pickett, Enlight Solutions
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,120 @@
1
+ == amazon-associate
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>AmazonAssociate::Request</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
+ NOTE: You must now specify a secret key to support request signing as
16
+ required by Amazon.
17
+
18
+ Version: 0.6.1
19
+
20
+ == WANTS
21
+ * instance based refactoring (singletons are not ideal here)
22
+
23
+ == INSTALLATION
24
+
25
+ $ gem install dpickett-amazon_associate
26
+
27
+ == EXAMPLE
28
+
29
+ require 'amazon_associate'
30
+
31
+ # set the default options; options will be camelized and converted to REST request parameters.
32
+ AmazonAssociate::Request.configure do |options|
33
+ options[:aWS_access_key_id] = [your developer token]
34
+ options[:secrety_key] = [your secret key]
35
+ end
36
+
37
+ # options provided on method call will merge with the default options
38
+ res = AmazonAssociate::Request.item_search('ruby', {:response_group => 'Medium', :sort => 'salesrank'})
39
+
40
+ # some common response object methods
41
+ res.is_valid_request? # return true if request is valid
42
+ res.has_error? # return true if there is an error
43
+ res.error # return error message if there is any
44
+ res.total_pages # return total pages
45
+ res.total_results # return total results
46
+ res.item_page # return current page no if :item_page option is provided
47
+
48
+ # traverse through each item (AmazonAssociate::Element)
49
+ res.items.each do |item|
50
+ # retrieve string value using XML path
51
+ item.get('asin')
52
+ item.get('itemattributes/title')
53
+
54
+ # or return AmazonAssociate::Element instance
55
+ atts = item.search_and_convert('itemattributes')
56
+ atts.get('title')
57
+
58
+ # return first author or a string array of authors
59
+ atts.get('author') # 'Author 1'
60
+ atts.get_array('author') # ['Author 1', 'Author 2', ...]
61
+
62
+ # return an hash of children text values with the element names as the keys
63
+ item.get_hash('smallimage') # {:url => ..., :width => ..., :height => ...}
64
+
65
+ # note that '/' returns Hpricot::Elements array object, nil if not found
66
+ reviews = item/'editorialreview'
67
+
68
+ # traverse through Hpricot elements
69
+ reviews.each do |review|
70
+ # Getting hash value out of Hpricot element
71
+ AmazonAssociate::Element.get_hash(review) # [:source => ..., :content ==> ...]
72
+
73
+ # Or to get unescaped HTML values
74
+ AmazonAssociate::Element.get_unescaped(review, 'source')
75
+ AmazonAssociate::Element.get_unescaped(review, 'content')
76
+
77
+ # Or this way
78
+ el = AmazonAssociate::Element.new(review)
79
+ el.get_unescaped('source')
80
+ el.get_unescaped('content')
81
+ end
82
+
83
+ # returns AmazonAssociate::Element instead of string
84
+ item.search_and_convert('itemattributes').
85
+ end
86
+
87
+ Refer to Amazon Associate's documentation for more information on Amazon REST request parameters and XML output:
88
+ http://docs.amazonwebservices.com/AWSEcommerceService/2006-09-13/
89
+
90
+ To get a sample of Amazon REST response XML output, use AWSZone.com scratch pad:
91
+ http://www.awszone.com/scratchpads/aws/ecs.us/index.aws
92
+
93
+ == CACHING
94
+
95
+ Filesystem caching is now available.
96
+
97
+ AmazonAssociate::Request.configure do |options|
98
+ options[:aWS_access_key_id] = [your developer token]
99
+ options[:scret_key] = [your secret key]
100
+ options[:caching_strategy] = :filesystem
101
+ options[:caching_options] = {
102
+ :disk_quota => 200,
103
+ :cache_path => <path where you want to store requests>,
104
+ :sweep_frequency => 4
105
+ }
106
+ end
107
+
108
+ The above command will cache up to 200MB of requests. It will purge the cache every 4 hours or when the disk quota has been exceeded.
109
+
110
+ Every request will be stored in the cache path. On every request, AmazonAssociate::Request will check for the presence of the cached file before querying Amazon directly.
111
+
112
+ == LINKS
113
+
114
+ * http://amazon-ecs.rubyforge.org
115
+
116
+ == LICENSE
117
+
118
+ (The MIT License)
119
+
120
+ Copyright (c) 2008 Dan Pickett, Enlight Solutions, Inc.
@@ -0,0 +1,43 @@
1
+ require "rubygems"
2
+ require "rake"
3
+ require "rake/testtask"
4
+ require "rake/rdoctask"
5
+ require "rake/gempackagetask"
6
+ require "date"
7
+
8
+ desc "Run unit tests."
9
+ task :default => :test
10
+
11
+ desc "Test the amazon_associate library."
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << "test"
14
+ t.test_files = FileList["test/**/*test.rb"]
15
+ t.verbose = true
16
+ t.warning = true
17
+ end
18
+
19
+ desc "Generate documentation for the amazon_associate plugin."
20
+ Rake::RDocTask.new(:rdoc) do |rdoc|
21
+ rdoc.rdoc_dir = "rdoc"
22
+ rdoc.title = "amazon_associate"
23
+ rdoc.options << "--line-numbers" << "--inline-source" << "--main" << "README.textile"
24
+ rdoc.rdoc_files.include("README.rdoc", "CHANGELOG")
25
+ rdoc.rdoc_files.include("lib/**/*.rb")
26
+ end
27
+
28
+ begin
29
+ require 'jeweler'
30
+ Jeweler::Tasks.new do |gemspec|
31
+ gemspec.name = "amazon_associate"
32
+ gemspec.summary = "Amazon Associates API Interface using Hpricot"
33
+ gemspec.email = "dpickett@enlightsolutions.com"
34
+ gemspec.homepage = "http://github.com/dpickett/amazon_associate"
35
+ gemspec.description = "interfaces with Amazon Associate's API using Hpricot"
36
+ gemspec.authors = ["Dan Pickett"]
37
+ end
38
+ rescue LoadError
39
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
40
+ end
41
+
42
+ desc "Clean files generated by rake tasks"
43
+ task :clobber => [:clobber_rdoc, :clobber_package]
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 7
@@ -0,0 +1,67 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{amazon_associate}
5
+ s.version = "0.7.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Dan Pickett"]
9
+ s.date = %q{2009-08-01}
10
+ s.description = %q{interfaces with Amazon Associate's API using Hpricot}
11
+ s.email = %q{dpickett@enlightsolutions.com}
12
+ s.extra_rdoc_files = [
13
+ "README.rdoc"
14
+ ]
15
+ s.files = [
16
+ ".autotest",
17
+ ".gitignore",
18
+ ".project",
19
+ "CHANGELOG",
20
+ "MIT-LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION.yml",
24
+ "amazon_associate.gemspec",
25
+ "lib/amazon_associate.rb",
26
+ "lib/amazon_associate/cache_factory.rb",
27
+ "lib/amazon_associate/caching_strategy.rb",
28
+ "lib/amazon_associate/caching_strategy/base.rb",
29
+ "lib/amazon_associate/caching_strategy/filesystem.rb",
30
+ "lib/amazon_associate/configuration_error.rb",
31
+ "lib/amazon_associate/element.rb",
32
+ "lib/amazon_associate/request.rb",
33
+ "lib/amazon_associate/request_error.rb",
34
+ "lib/amazon_associate/response.rb",
35
+ "test/amazon_associate/browse_node_lookup_test.rb",
36
+ "test/amazon_associate/cache_test.rb",
37
+ "test/amazon_associate/caching_strategy/filesystem_test.rb",
38
+ "test/amazon_associate/cart_test.rb",
39
+ "test/amazon_associate/request_test.rb",
40
+ "test/test_helper.rb",
41
+ "test/utilities/filesystem_test_helper.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/dpickett/amazon_associate}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.5}
47
+ s.summary = %q{Amazon Associates API Interface using Hpricot}
48
+ s.test_files = [
49
+ "test/amazon_associate/browse_node_lookup_test.rb",
50
+ "test/amazon_associate/cache_test.rb",
51
+ "test/amazon_associate/caching_strategy/filesystem_test.rb",
52
+ "test/amazon_associate/cart_test.rb",
53
+ "test/amazon_associate/request_test.rb",
54
+ "test/test_helper.rb",
55
+ "test/utilities/filesystem_test_helper.rb"
56
+ ]
57
+
58
+ if s.respond_to? :specification_version then
59
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
60
+ s.specification_version = 3
61
+
62
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
63
+ else
64
+ end
65
+ else
66
+ end
67
+ end
@@ -0,0 +1,14 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require "base64"
4
+ require "hmac-sha2"
5
+ require "digest/sha1"
6
+ require "digest/sha2"
7
+
8
+ require "amazon_associate/request"
9
+ require "amazon_associate/element"
10
+ require "amazon_associate/response"
11
+ require "amazon_associate/cache_factory"
12
+ require "amazon_associate/caching_strategy"
13
+ require "amazon_associate/configuration_error"
14
+ require "amazon_associate/request_error"
@@ -0,0 +1,31 @@
1
+ module AmazonAssociate
2
+ class CacheFactory
3
+ def self.cache(request, response, strategy)
4
+ strategy_class_hash[strategy].cache(request, response)
5
+ end
6
+
7
+ def self.initialize_options(options)
8
+ #check for a valid caching strategy
9
+ unless self.strategy_class_hash.keys.include?(options[:caching_strategy])
10
+ raise AmazonAssociate::ConfigurationError, "Invalid caching strategy"
11
+ end
12
+
13
+ strategy_class_hash[options[:caching_strategy]].initialize_options(options)
14
+ end
15
+
16
+ def self.get(request, strategy)
17
+ strategy_class_hash[strategy].get(request)
18
+ end
19
+
20
+ def self.sweep(strategy)
21
+ strategy_class_hash[strategy].sweep
22
+ end
23
+
24
+ private
25
+ def self.strategy_class_hash
26
+ {
27
+ :filesystem => AmazonAssociate::CachingStrategy::Filesystem
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), "caching_strategy/base")
2
+ require File.join(File.dirname(__FILE__), "caching_strategy/filesystem")
@@ -0,0 +1,22 @@
1
+ #abstract class
2
+ module AmazonAssociate
3
+ module CachingStrategy
4
+ class Base
5
+ def self.cache(request, response)
6
+ raise "This method must be overwritten by a caching strategy"
7
+ end
8
+
9
+ def self.initialize_options(options)
10
+ raise "This method must be overwritten by a caching strategy"
11
+ end
12
+
13
+ def self.get(request)
14
+ raise "This method must be overwritten by a caching strategy"
15
+ end
16
+
17
+ def self.sweep
18
+ raise "This method must be overwritten by a caching strategy"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,109 @@
1
+ require "fileutils"
2
+ require "find"
3
+
4
+ module AmazonAssociate
5
+ module CachingStrategy
6
+ class Filesystem < AmazonAssociate::CachingStrategy::Base
7
+ #disk quota in megabytes
8
+ DEFAULT_DISK_QUOTA = 200
9
+
10
+ #frequency of sweeping in hours
11
+ DEFAULT_SWEEP_FREQUENCY = 2
12
+
13
+ class << self
14
+ attr_accessor :cache_path
15
+ attr_accessor :disk_quota
16
+ attr_accessor :sweep_frequency
17
+
18
+ def cache(request, response)
19
+ path = self.cache_path
20
+ cached_filename = Digest::SHA1.hexdigest(request)
21
+ cached_folder = cached_filename[0..2]
22
+
23
+ FileUtils.mkdir_p(File.join(path, cached_folder, cached_folder))
24
+
25
+ cached_file = File.open(File.join(path, cached_folder, cached_filename), "w")
26
+ cached_file.puts response.doc.to_s
27
+ cached_file.close
28
+ end
29
+
30
+ def get(request)
31
+ path = self.cache_path
32
+ cached_filename = Digest::SHA1.hexdigest(request)
33
+ file_path = File.join(path, cached_filename[0..2], cached_filename)
34
+ if FileTest.exists?(file_path)
35
+ File.read(file_path).chomp
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ def initialize_options(options)
42
+ #check for required options
43
+ if options[:caching_options].nil? || options[:caching_options][:cache_path].nil?
44
+ raise AmazonAssociate::ConfigurationError, "You must specify caching options for filesystem caching: :cache_path is required"
45
+ end
46
+
47
+ #default disk quota to 200MB
48
+ Filesystem.disk_quota = options[:caching_options][:disk_quota] || DEFAULT_DISK_QUOTA
49
+
50
+ Filesystem.sweep_frequency = options[:caching_options][:sweep_frequency] || DEFAULT_SWEEP_FREQUENCY
51
+
52
+ Filesystem.cache_path = options[:caching_options][:cache_path]
53
+
54
+ if Filesystem.cache_path.nil? || !File.directory?(Filesystem.cache_path)
55
+ raise AmazonAssociate::ConfigurationError, "You must specify a cache path for filesystem caching"
56
+ end
57
+ return options
58
+ end
59
+
60
+ def sweep
61
+ perform_sweep if must_sweep?
62
+ end
63
+
64
+ private
65
+ def perform_sweep
66
+ FileUtils.rm_rf(Dir.glob("#{Filesystem.cache_path}/*"))
67
+
68
+ timestamp_sweep_performance
69
+ end
70
+
71
+ def timestamp_sweep_performance
72
+ #remove the timestamp
73
+ FileUtils.rm_rf(timestamp_filename)
74
+
75
+ #create a new one its place
76
+ timestamp = File.open(timestamp_filename, "w")
77
+ timestamp.puts(Time.now)
78
+ timestamp.close
79
+ end
80
+
81
+ def must_sweep?
82
+ sweep_time_expired? || disk_quota_exceeded?
83
+ end
84
+
85
+ def sweep_time_expired?
86
+ FileTest.exists?(timestamp_filename) && Time.parse(File.read(timestamp_filename).chomp) < Time.now - (sweep_frequency * 3600)
87
+ end
88
+
89
+ def disk_quota_exceeded?
90
+ cache_size > Filesystem.disk_quota
91
+ end
92
+
93
+ def timestamp_filename
94
+ File.join(Filesystem.cache_path, ".amz_timestamp")
95
+ end
96
+
97
+ def cache_size
98
+ size = 0
99
+ Find.find(Filesystem.cache_path) do|f|
100
+ size += File.size(f) if File.file?(f)
101
+ end
102
+ size / 1000000
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+ end
109
+ end