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.
Files changed (50) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG +29 -0
  3. data/LICENSE +21 -0
  4. data/README.rdoc +84 -0
  5. data/Rakefile +62 -0
  6. data/VERSION.yml +5 -0
  7. data/amazon-associates.gemspec +118 -0
  8. data/lib/amazon-associates.rb +90 -0
  9. data/lib/amazon-associates/caching/filesystem_cache.rb +121 -0
  10. data/lib/amazon-associates/errors.rb +23 -0
  11. data/lib/amazon-associates/extensions/core.rb +31 -0
  12. data/lib/amazon-associates/extensions/hpricot.rb +115 -0
  13. data/lib/amazon-associates/request.rb +143 -0
  14. data/lib/amazon-associates/requests/browse_node.rb +10 -0
  15. data/lib/amazon-associates/requests/cart.rb +81 -0
  16. data/lib/amazon-associates/requests/item.rb +13 -0
  17. data/lib/amazon-associates/responses/browse_node_lookup_response.rb +10 -0
  18. data/lib/amazon-associates/responses/cart_responses.rb +26 -0
  19. data/lib/amazon-associates/responses/item_lookup_response.rb +16 -0
  20. data/lib/amazon-associates/responses/item_search_response.rb +20 -0
  21. data/lib/amazon-associates/responses/response.rb +27 -0
  22. data/lib/amazon-associates/responses/similarity_lookup_response.rb +9 -0
  23. data/lib/amazon-associates/types/api_result.rb +8 -0
  24. data/lib/amazon-associates/types/browse_node.rb +48 -0
  25. data/lib/amazon-associates/types/cart.rb +87 -0
  26. data/lib/amazon-associates/types/customer_review.rb +15 -0
  27. data/lib/amazon-associates/types/editorial_review.rb +8 -0
  28. data/lib/amazon-associates/types/error.rb +8 -0
  29. data/lib/amazon-associates/types/image.rb +37 -0
  30. data/lib/amazon-associates/types/image_set.rb +11 -0
  31. data/lib/amazon-associates/types/item.rb +156 -0
  32. data/lib/amazon-associates/types/listmania_list.rb +9 -0
  33. data/lib/amazon-associates/types/measurement.rb +47 -0
  34. data/lib/amazon-associates/types/offer.rb +10 -0
  35. data/lib/amazon-associates/types/ordinal.rb +24 -0
  36. data/lib/amazon-associates/types/price.rb +29 -0
  37. data/lib/amazon-associates/types/requests.rb +50 -0
  38. data/spec/requests/browse_node_lookup_spec.rb +41 -0
  39. data/spec/requests/item_search_spec.rb +27 -0
  40. data/spec/spec_helper.rb +8 -0
  41. data/spec/types/cart_spec.rb +294 -0
  42. data/spec/types/item_spec.rb +55 -0
  43. data/spec/types/measurement_spec.rb +43 -0
  44. data/test/amazon/browse_node_test.rb +34 -0
  45. data/test/amazon/cache_test.rb +33 -0
  46. data/test/amazon/caching/filesystem_cache_test.rb +198 -0
  47. data/test/amazon/item_test.rb +397 -0
  48. data/test/test_helper.rb +9 -0
  49. data/test/utilities/filesystem_test_helper.rb +35 -0
  50. metadata +216 -0
@@ -0,0 +1,3 @@
1
+ pkg
2
+ test/utilities/cache
3
+ .loadpath
@@ -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.
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 0
3
+ :build:
4
+ :minor: 6
5
+ :patch: 3
@@ -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