amazon-associates 0.6.3
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/.gitignore +3 -0
- data/CHANGELOG +29 -0
- data/LICENSE +21 -0
- data/README.rdoc +84 -0
- data/Rakefile +62 -0
- data/VERSION.yml +5 -0
- data/amazon-associates.gemspec +118 -0
- data/lib/amazon-associates.rb +90 -0
- data/lib/amazon-associates/caching/filesystem_cache.rb +121 -0
- data/lib/amazon-associates/errors.rb +23 -0
- data/lib/amazon-associates/extensions/core.rb +31 -0
- data/lib/amazon-associates/extensions/hpricot.rb +115 -0
- data/lib/amazon-associates/request.rb +143 -0
- data/lib/amazon-associates/requests/browse_node.rb +10 -0
- data/lib/amazon-associates/requests/cart.rb +81 -0
- data/lib/amazon-associates/requests/item.rb +13 -0
- data/lib/amazon-associates/responses/browse_node_lookup_response.rb +10 -0
- data/lib/amazon-associates/responses/cart_responses.rb +26 -0
- data/lib/amazon-associates/responses/item_lookup_response.rb +16 -0
- data/lib/amazon-associates/responses/item_search_response.rb +20 -0
- data/lib/amazon-associates/responses/response.rb +27 -0
- data/lib/amazon-associates/responses/similarity_lookup_response.rb +9 -0
- data/lib/amazon-associates/types/api_result.rb +8 -0
- data/lib/amazon-associates/types/browse_node.rb +48 -0
- data/lib/amazon-associates/types/cart.rb +87 -0
- data/lib/amazon-associates/types/customer_review.rb +15 -0
- data/lib/amazon-associates/types/editorial_review.rb +8 -0
- data/lib/amazon-associates/types/error.rb +8 -0
- data/lib/amazon-associates/types/image.rb +37 -0
- data/lib/amazon-associates/types/image_set.rb +11 -0
- data/lib/amazon-associates/types/item.rb +156 -0
- data/lib/amazon-associates/types/listmania_list.rb +9 -0
- data/lib/amazon-associates/types/measurement.rb +47 -0
- data/lib/amazon-associates/types/offer.rb +10 -0
- data/lib/amazon-associates/types/ordinal.rb +24 -0
- data/lib/amazon-associates/types/price.rb +29 -0
- data/lib/amazon-associates/types/requests.rb +50 -0
- data/spec/requests/browse_node_lookup_spec.rb +41 -0
- data/spec/requests/item_search_spec.rb +27 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/types/cart_spec.rb +294 -0
- data/spec/types/item_spec.rb +55 -0
- data/spec/types/measurement_spec.rb +43 -0
- data/test/amazon/browse_node_test.rb +34 -0
- data/test/amazon/cache_test.rb +33 -0
- data/test/amazon/caching/filesystem_cache_test.rb +198 -0
- data/test/amazon/item_test.rb +397 -0
- data/test/test_helper.rb +9 -0
- data/test/utilities/filesystem_test_helper.rb +35 -0
- metadata +216 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
0.6.3 2010-08-16
|
2
|
+
----------------
|
3
|
+
* Update gem dependencies
|
4
|
+
|
5
|
+
0.6.0 (?)
|
6
|
+
----------------
|
7
|
+
* Add signing as per the new amazon security requirements
|
8
|
+
|
9
|
+
0.5.3 2007-09-12
|
10
|
+
----------------
|
11
|
+
* send_request to use default options.
|
12
|
+
|
13
|
+
0.5.2 2007-09-08
|
14
|
+
----------------
|
15
|
+
* Fixed Amazon::Element.get_unescaped error when result returned for given element path is nil
|
16
|
+
|
17
|
+
0.5.1 2007-02-08
|
18
|
+
----------------
|
19
|
+
* Fixed Amazon Japan and France URL error
|
20
|
+
* Removed opts.delete(:search_index) from item_lookup, SearchIndex param is allowed
|
21
|
+
when looking for a book with IdType other than the ASIN.
|
22
|
+
* Check for defined? RAILS_DEFAULT_LOGGER to avoid exception for non-rails ruby app
|
23
|
+
* Added check for LOGGER constant if RAILS_DEFAULT_LOGGER is not defined
|
24
|
+
* Added Ecs.configure(&proc) method for easier configuration of default options
|
25
|
+
* Added Element#search_and_convert method
|
26
|
+
|
27
|
+
0.5.0 2006-09-12
|
28
|
+
----------------
|
29
|
+
Initial Release
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2008 Dan Pickett, Enlight Solutions; Herryanto Siatono,
|
2
|
+
Pluit Solutions; and Ben Woosley
|
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.
|
data/README.rdoc
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
== amazon-associates
|
2
|
+
|
3
|
+
Generic Amazon E-commerce REST API using Hpricot with configurable
|
4
|
+
default options and method call options. Uses Response wrapper classes
|
5
|
+
for easy access to REST XML output. It supports ECS 4.0.
|
6
|
+
|
7
|
+
== INSTALLATION
|
8
|
+
|
9
|
+
$ gem install amazon-associates
|
10
|
+
|
11
|
+
== EXAMPLE
|
12
|
+
|
13
|
+
require 'amazon-associates'
|
14
|
+
|
15
|
+
# set the default options; options will be camelized and converted to REST request parameters.
|
16
|
+
Amazon::Ecs.options = {:aws_access_key_id => [your developer token]}
|
17
|
+
|
18
|
+
# options provided on method call will merge with the default options
|
19
|
+
res = Amazon::Ecs.item_search('ruby', {:response_group => 'Medium', :sort => 'salesrank'})
|
20
|
+
|
21
|
+
# some common response object methods
|
22
|
+
res.request.valid? # return true if request is valid
|
23
|
+
res.has_error? # return true if there is an error
|
24
|
+
res.error # return error message if there is any
|
25
|
+
res.total_pages # return total pages
|
26
|
+
res.total_results # return total results
|
27
|
+
res.item_page # return current page no if :item_page option is provided
|
28
|
+
|
29
|
+
# traverse through each item (Amazon::Element)
|
30
|
+
res.items.each do |item|
|
31
|
+
# retrieve string value using XML path
|
32
|
+
item.asin
|
33
|
+
item.attributes['Title']
|
34
|
+
|
35
|
+
# or return Amazon::Element instance
|
36
|
+
atts = item.attributes
|
37
|
+
atts['Title']
|
38
|
+
|
39
|
+
# return first author or a string array of authors
|
40
|
+
atts.get('author') # 'Author 1'
|
41
|
+
atts.get_array('author') # ['Author 1', 'Author 2', ...]
|
42
|
+
|
43
|
+
# return an hash of children text values with the element names as the keys
|
44
|
+
item.get_hash('smallimage') # {:url => ..., :width => ..., :height => ...}
|
45
|
+
|
46
|
+
# note that '/' returns Hpricot::Elements array object, nil if not found
|
47
|
+
reviews = item/'editorialreview'
|
48
|
+
|
49
|
+
# traverse through Hpricot elements
|
50
|
+
reviews.each do |review|
|
51
|
+
# Getting hash value out of Hpricot element
|
52
|
+
Amazon::Element.get_hash(review) # [:source => ..., :content ==> ...]
|
53
|
+
|
54
|
+
# Or to get unescaped HTML values
|
55
|
+
Amazon::Element.get_unescaped(review, 'source')
|
56
|
+
Amazon::Element.get_unescaped(review, 'content')
|
57
|
+
|
58
|
+
# Or this way
|
59
|
+
el = Amazon::Element.new(review)
|
60
|
+
el.get_unescaped('source')
|
61
|
+
el.get_unescaped('content')
|
62
|
+
end
|
63
|
+
|
64
|
+
# returns Amazon::Element instead of string
|
65
|
+
item.search_and_convert('itemattributes').
|
66
|
+
end
|
67
|
+
|
68
|
+
Refer to Amazon ECS documentation for more information on Amazon REST request parameters and XML output:
|
69
|
+
http://docs.amazonwebservices.com/AWSEcommerceService/2006-09-13/
|
70
|
+
|
71
|
+
To get a sample of Amazon REST response XML output, use AWSZone.com scratch pad:
|
72
|
+
http://www.awszone.com/scratchpads/aws/ecs.us/index.aws
|
73
|
+
|
74
|
+
== LINKS
|
75
|
+
|
76
|
+
* http://amazon-ecs.rubyforge.org
|
77
|
+
* http://www.pluitsolutions.com/amazon-ecs
|
78
|
+
|
79
|
+
== LICENSE
|
80
|
+
|
81
|
+
(The MIT License)
|
82
|
+
|
83
|
+
Copyright (c) 2008 Dan Pickett, Enlight Solutions; Herryanto Siatono,
|
84
|
+
Pluit Solutions; and Ben Woosley
|
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
require 'jeweler'
|
4
|
+
Jeweler::Tasks.new do |s|
|
5
|
+
s.name = "amazon-associates"
|
6
|
+
s.summary = "Generic Amazon Associates Web Service (Formerly ECS) REST API. Supports ECS 4.0."
|
7
|
+
s.email = "ben.woosley@gmail.com"
|
8
|
+
s.homepage = "http://github.com/Empact/amazon-associates"
|
9
|
+
s.description = "amazon-associates offers object-oriented access to the Amazon Associates API, built on ROXML"
|
10
|
+
s.authors = ["Ben Woosley", "Dan Pickett", "Herryanto Siatono"]
|
11
|
+
s.add_runtime_dependency("roxml", ">= 3.1.3")
|
12
|
+
s.add_runtime_dependency("activesupport", ">= 2.3.4")
|
13
|
+
s.add_runtime_dependency("ruby-hmac")
|
14
|
+
s.add_runtime_dependency("will_paginate")
|
15
|
+
s.add_development_dependency("thoughtbot-shoulda")
|
16
|
+
s.add_development_dependency("mocha")
|
17
|
+
|
18
|
+
# s.require_path = "lib"
|
19
|
+
# s.autorequire = "amazon-associates"
|
20
|
+
# s.test_files = FileList["test/**/*test.rb"].to_a
|
21
|
+
# s.files = FileList["lib/**/*"].to_a
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
require 'rake/rdoctask'
|
26
|
+
Rake::RDocTask.new do |rdoc|
|
27
|
+
rdoc.rdoc_dir = 'rdoc'
|
28
|
+
rdoc.title = 'amazon-associates'
|
29
|
+
rdoc.options << '--line-numbers' << '--inline-source' << "--main README.rdoc"
|
30
|
+
rdoc.rdoc_files.include('README.rdoc')
|
31
|
+
rdoc.rdoc_files.include('CHANGELOG')
|
32
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rake/testtask'
|
36
|
+
Rake::TestTask.new(:test) do |t|
|
37
|
+
t.libs << "test"
|
38
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
39
|
+
t.verbose = true
|
40
|
+
end
|
41
|
+
|
42
|
+
require 'spec/rake/spectask'
|
43
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
44
|
+
t.libs << 'lib' << 'spec'
|
45
|
+
t.spec_opts << '--format profile --backtrace'
|
46
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
47
|
+
end
|
48
|
+
|
49
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
50
|
+
t.libs << 'lib' << 'spec'
|
51
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
52
|
+
t.rcov = true
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
require 'cucumber/rake/task'
|
57
|
+
Cucumber::Rake::Task.new(:features)
|
58
|
+
rescue LoadError
|
59
|
+
puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
60
|
+
end
|
61
|
+
|
62
|
+
task :default => :spec
|
data/VERSION.yml
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{amazon-associates}
|
8
|
+
s.version = "0.6.3"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ben Woosley", "Dan Pickett", "Herryanto Siatono"]
|
12
|
+
s.date = %q{2010-08-16}
|
13
|
+
s.description = %q{amazon-associates offers object-oriented access to the Amazon Associates API, built on ROXML}
|
14
|
+
s.email = %q{ben.woosley@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"CHANGELOG",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION.yml",
|
26
|
+
"amazon-associates.gemspec",
|
27
|
+
"lib/amazon-associates.rb",
|
28
|
+
"lib/amazon-associates/caching/filesystem_cache.rb",
|
29
|
+
"lib/amazon-associates/errors.rb",
|
30
|
+
"lib/amazon-associates/extensions/core.rb",
|
31
|
+
"lib/amazon-associates/extensions/hpricot.rb",
|
32
|
+
"lib/amazon-associates/request.rb",
|
33
|
+
"lib/amazon-associates/requests/browse_node.rb",
|
34
|
+
"lib/amazon-associates/requests/cart.rb",
|
35
|
+
"lib/amazon-associates/requests/item.rb",
|
36
|
+
"lib/amazon-associates/responses/browse_node_lookup_response.rb",
|
37
|
+
"lib/amazon-associates/responses/cart_responses.rb",
|
38
|
+
"lib/amazon-associates/responses/item_lookup_response.rb",
|
39
|
+
"lib/amazon-associates/responses/item_search_response.rb",
|
40
|
+
"lib/amazon-associates/responses/response.rb",
|
41
|
+
"lib/amazon-associates/responses/similarity_lookup_response.rb",
|
42
|
+
"lib/amazon-associates/types/api_result.rb",
|
43
|
+
"lib/amazon-associates/types/browse_node.rb",
|
44
|
+
"lib/amazon-associates/types/cart.rb",
|
45
|
+
"lib/amazon-associates/types/customer_review.rb",
|
46
|
+
"lib/amazon-associates/types/editorial_review.rb",
|
47
|
+
"lib/amazon-associates/types/error.rb",
|
48
|
+
"lib/amazon-associates/types/image.rb",
|
49
|
+
"lib/amazon-associates/types/image_set.rb",
|
50
|
+
"lib/amazon-associates/types/item.rb",
|
51
|
+
"lib/amazon-associates/types/listmania_list.rb",
|
52
|
+
"lib/amazon-associates/types/measurement.rb",
|
53
|
+
"lib/amazon-associates/types/offer.rb",
|
54
|
+
"lib/amazon-associates/types/ordinal.rb",
|
55
|
+
"lib/amazon-associates/types/price.rb",
|
56
|
+
"lib/amazon-associates/types/requests.rb",
|
57
|
+
"spec/requests/browse_node_lookup_spec.rb",
|
58
|
+
"spec/requests/item_search_spec.rb",
|
59
|
+
"spec/spec_helper.rb",
|
60
|
+
"spec/types/cart_spec.rb",
|
61
|
+
"spec/types/item_spec.rb",
|
62
|
+
"spec/types/measurement_spec.rb",
|
63
|
+
"test/amazon/browse_node_test.rb",
|
64
|
+
"test/amazon/cache_test.rb",
|
65
|
+
"test/amazon/caching/filesystem_cache_test.rb",
|
66
|
+
"test/amazon/item_test.rb",
|
67
|
+
"test/test_helper.rb",
|
68
|
+
"test/utilities/filesystem_test_helper.rb"
|
69
|
+
]
|
70
|
+
s.homepage = %q{http://github.com/Empact/amazon-associates}
|
71
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
72
|
+
s.require_paths = ["lib"]
|
73
|
+
s.rubygems_version = %q{1.3.7}
|
74
|
+
s.summary = %q{Generic Amazon Associates Web Service (Formerly ECS) REST API. Supports ECS 4.0.}
|
75
|
+
s.test_files = [
|
76
|
+
"spec/requests/browse_node_lookup_spec.rb",
|
77
|
+
"spec/requests/item_search_spec.rb",
|
78
|
+
"spec/spec_helper.rb",
|
79
|
+
"spec/types/cart_spec.rb",
|
80
|
+
"spec/types/item_spec.rb",
|
81
|
+
"spec/types/measurement_spec.rb",
|
82
|
+
"test/amazon/browse_node_test.rb",
|
83
|
+
"test/amazon/cache_test.rb",
|
84
|
+
"test/amazon/caching/filesystem_cache_test.rb",
|
85
|
+
"test/amazon/item_test.rb",
|
86
|
+
"test/test_helper.rb",
|
87
|
+
"test/utilities/filesystem_test_helper.rb"
|
88
|
+
]
|
89
|
+
|
90
|
+
if s.respond_to? :specification_version then
|
91
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
92
|
+
s.specification_version = 3
|
93
|
+
|
94
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
95
|
+
s.add_runtime_dependency(%q<roxml>, [">= 3.1.3"])
|
96
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.4"])
|
97
|
+
s.add_runtime_dependency(%q<ruby-hmac>, [">= 0"])
|
98
|
+
s.add_runtime_dependency(%q<will_paginate>, [">= 0"])
|
99
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
100
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
101
|
+
else
|
102
|
+
s.add_dependency(%q<roxml>, [">= 3.1.3"])
|
103
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.4"])
|
104
|
+
s.add_dependency(%q<ruby-hmac>, [">= 0"])
|
105
|
+
s.add_dependency(%q<will_paginate>, [">= 0"])
|
106
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
107
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
108
|
+
end
|
109
|
+
else
|
110
|
+
s.add_dependency(%q<roxml>, [">= 3.1.3"])
|
111
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.4"])
|
112
|
+
s.add_dependency(%q<ruby-hmac>, [">= 0"])
|
113
|
+
s.add_dependency(%q<will_paginate>, [">= 0"])
|
114
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
115
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'roxml'
|
3
|
+
|
4
|
+
%w(caching/filesystem_cache requests/cart requests/browse_node requests/item).each do |file|
|
5
|
+
require File.join(File.dirname(__FILE__), 'amazon-associates', file)
|
6
|
+
end
|
7
|
+
|
8
|
+
module Amazon
|
9
|
+
module Associates
|
10
|
+
# Only we throw this
|
11
|
+
class ConfigurationError < RuntimeError; end
|
12
|
+
|
13
|
+
# Default search options
|
14
|
+
@options = {}
|
15
|
+
|
16
|
+
SORT_TYPES = {
|
17
|
+
'All' => nil,
|
18
|
+
'Apparel' => %w[relevancerank salesrank pricerank inverseprice -launch-date sale-flag],
|
19
|
+
'Automotive' => %w[salesrank price -price titlerank -titlerank],
|
20
|
+
'Baby' => %w[psrank salesrank price -price titlerank],
|
21
|
+
'Beauty' => %w[pmrank salesrank price -price -launch-date sale-flag],
|
22
|
+
'Books' => %w[relevancerank salesrank reviewrank pricerank inverse-pricerank daterank titlerank -titlerank],
|
23
|
+
'Classical' => %w[psrank salesrank price -price titlerank -titlerank orig-rel-date],
|
24
|
+
'DigitalMusic' => %w[songtitlerank uploaddaterank],
|
25
|
+
'DVD' => %w[relevancerank salesrank price -price titlerank -video-release-date],
|
26
|
+
'Electronics' => %w[pmrank salesrank reviewrank price -price titlerank],
|
27
|
+
'GourmetFood' => %w[relevancerank salesrank pricerank inverseprice launch-date sale-flag],
|
28
|
+
'HealthPersonalCare' => %w[pmrank salesrank pricerank inverseprice launch-date sale-flag],
|
29
|
+
'Jewelry' => %w[pmrank salesrank pricerank inverseprice launch-date],
|
30
|
+
'Kitchen' => %w[pmrank salesrank price -price titlerank -titlerank],
|
31
|
+
'Magazines' => %w[subslot-salesrank reviewrank price -price daterank titlerank -titlerank],
|
32
|
+
'Marketplace' => nil,
|
33
|
+
'Merchants' => %w[relevancerank salesrank pricerank inverseprice launch-date sale-flag],
|
34
|
+
'Miscellaneous' => %w[pmrank salesrank price -price titlerank -titlerank],
|
35
|
+
'Music' => %w[psrank salesrank price -price titlerank -titlerank artistrank orig-rel-date release-date],
|
36
|
+
'MusicalInstruments' => %w[pmrank salesrank price -price -launch-date sale-flag],
|
37
|
+
'MusicTracks' => %w[titlerank -titlerank],
|
38
|
+
'OfficeProducts' => %w[pmrank salesrank reviewrank price -price titlerank],
|
39
|
+
'OutdoorLiving' => %w[psrank salesrank price -price titlerank -titlerank],
|
40
|
+
'PCHardware' => %w[psrank salesrank price -price titlerank],
|
41
|
+
'PetSupplies' => %w[+pmrank salesrank price -price titlerank -titlerank],
|
42
|
+
'Photo' => %w[pmrank salesrank titlerank -titlerank],
|
43
|
+
'Restaurants' => %w[relevancerank titlerank],
|
44
|
+
'Software' => %w[pmrank salesrank titlerank price -price],
|
45
|
+
'SportingGoods' => %w[relevancerank salesrank pricerank inverseprice launch-date sale-flag],
|
46
|
+
'Tools' => %w[pmrank salesrank titlerank -titlerank price -price],
|
47
|
+
'Toys' => %w[pmrank salesrank price -price titlerank -age-min],
|
48
|
+
'VHS' => %w[relevancerank salesrank price -price titlerank -video-release-date],
|
49
|
+
'Video' => %w[relevancerank salesrank price -price titlerank -video-release-date],
|
50
|
+
'VideoGames' => %w[pmrank salesrank price -price titlerank],
|
51
|
+
'Wireless' => %w[daterank pricerank invers-pricerank reviewrank salesrank titlerank -titlerank],
|
52
|
+
'WirelessAccessories' => %w[psrank salesrank titlerank -titlerank]
|
53
|
+
}.freeze
|
54
|
+
|
55
|
+
SEARCH_INDEXES = (SORT_TYPES.keys + %w(Blended)).freeze
|
56
|
+
DEFAULT_SEARCH_INDEX = 'Blended'
|
57
|
+
|
58
|
+
class << self
|
59
|
+
def configure(&proc)
|
60
|
+
raise ArgumentError, "Block is required." unless block_given?
|
61
|
+
yield @options
|
62
|
+
case options[:caching_strategy]
|
63
|
+
when :filesystem
|
64
|
+
FilesystemCache.initialize_options(options)
|
65
|
+
when nil
|
66
|
+
nil
|
67
|
+
else
|
68
|
+
raise ConfigurationError, "Unrecognized caching_strategy"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def options
|
73
|
+
access_key = @options.delete(:aws_access_key_id)
|
74
|
+
@options[:aWS_access_key_id] ||= (ENV['AMAZON_ACCESS_KEY_ID'] || access_key)
|
75
|
+
@options
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
def self.log(s)
|
81
|
+
if defined? RAILS_DEFAULT_LOGGER
|
82
|
+
RAILS_DEFAULT_LOGGER.error(s)
|
83
|
+
elsif defined? LOGGER
|
84
|
+
LOGGER.error(s)
|
85
|
+
else
|
86
|
+
puts s if @debug
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "find"
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'md5'
|
6
|
+
rescue LoadError
|
7
|
+
require 'digest/md5'
|
8
|
+
end
|
9
|
+
|
10
|
+
module Amazon
|
11
|
+
module Associates
|
12
|
+
module FilesystemCache
|
13
|
+
#disk quota in megabytes
|
14
|
+
DEFAULT_DISK_QUOTA = 200
|
15
|
+
|
16
|
+
#frequency of sweeping in hours
|
17
|
+
DEFAULT_SWEEP_FREQUENCY = 2
|
18
|
+
|
19
|
+
def self.cache(request_url, response)
|
20
|
+
path = self.cache_path
|
21
|
+
cached_filename = Digest::SHA1.hexdigest(request_url)
|
22
|
+
cached_folder = File.join(path, cached_filename[0..2])
|
23
|
+
|
24
|
+
FileUtils.mkdir_p(cached_folder)
|
25
|
+
|
26
|
+
destination = File.join(cached_folder, cached_filename)
|
27
|
+
open(destination, "w") do |cached_file|
|
28
|
+
Marshal.dump(response, cached_file)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.get(request)
|
33
|
+
path = self.cache_path
|
34
|
+
cached_filename = Digest::SHA1.hexdigest(request)
|
35
|
+
file_path = File.join(path, cached_filename[0..2], cached_filename)
|
36
|
+
if FileTest.exists?(file_path)
|
37
|
+
open(file_path) {|f| Marshal.load(f) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.initialize_options(options)
|
42
|
+
#check for required options
|
43
|
+
unless cache_options = options[:caching_options]
|
44
|
+
raise ConfigurationError, "You must specify caching options for filesystem caching: :cache_path is required"
|
45
|
+
end
|
46
|
+
|
47
|
+
#default disk quota to 200MB
|
48
|
+
@@disk_quota = cache_options[:disk_quota] || DEFAULT_DISK_QUOTA
|
49
|
+
|
50
|
+
@@sweep_frequency = cache_options[:sweep_frequency] || DEFAULT_SWEEP_FREQUENCY
|
51
|
+
|
52
|
+
@@cache_path = cache_options[:cache_path]
|
53
|
+
|
54
|
+
if @@cache_path.nil?
|
55
|
+
raise ConfigurationError, "You must specify a cache path for filesystem caching"
|
56
|
+
end
|
57
|
+
|
58
|
+
if !File.directory?(@@cache_path)
|
59
|
+
raise ConfigurationError, "You must specify a valid cache path for filesystem caching"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.sweep
|
64
|
+
self.perform_sweep if must_sweep?
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.disk_quota
|
68
|
+
@@disk_quota
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.sweep_frequency
|
72
|
+
@@sweep_frequency
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.cache_path
|
76
|
+
@@cache_path
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def self.perform_sweep
|
81
|
+
FileUtils.rm_rf(Dir.glob("#{@@cache_path}/*"))
|
82
|
+
|
83
|
+
self.timestamp_sweep_performance
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.timestamp_sweep_performance
|
87
|
+
#remove the timestamp
|
88
|
+
FileUtils.rm_rf(self.timestamp_filename)
|
89
|
+
|
90
|
+
#create a new one its place
|
91
|
+
File.open(self.timestamp_filename, "w") do |timestamp|
|
92
|
+
timestamp.puts(Time.now)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.must_sweep?
|
97
|
+
sweep_time_expired? || disk_quota_exceeded?
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.sweep_time_expired?
|
101
|
+
FileTest.exists?(timestamp_filename) && Time.parse(File.read(timestamp_filename).chomp) < Time.now - (sweep_frequency * 3600)
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.disk_quota_exceeded?
|
105
|
+
cache_size > @@disk_quota
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.timestamp_filename
|
109
|
+
File.join(self.cache_path, ".amz_timestamp")
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.cache_size
|
113
|
+
size = 0
|
114
|
+
Find.find(@@cache_path) do|f|
|
115
|
+
size += File.size(f) if File.file?(f)
|
116
|
+
end
|
117
|
+
size / 1000000
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|