amazon-associates 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
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