feedtools_ram_cache 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.
data/CHANGELOG ADDED
@@ -0,0 +1 @@
1
+ v1.0. Initial release, tested to work with FeedTools 0.2.29.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Zergling.Net
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,9 @@
1
+ CHANGELOG
2
+ LICENSE
3
+ Manifest
4
+ README.textile
5
+ Rakefile
6
+ lib/feed_tools_ram_cache.rb
7
+ test/cache_test.rb
8
+ test/fixtures/unit.yml
9
+ test/integration_test.rb
data/README.textile ADDED
@@ -0,0 +1,63 @@
1
+ h1. RAM-backed cache for FeedTools
2
+
3
+ FeedTools is a Ruby gem and Rails plug-in than parses both RSS and Atom feeds
4
+ reliably. The library was intended to be used inside Rails, and comes with the
5
+ entire kitchen sink, including a caching mechanism based on ActiveRecord and a
6
+ database table.
7
+
8
+ This gem implements a RAM-backed cache, so FeedTools clients can get the
9
+ benefits of caching without taking a dependency on ActiveRecord. The RAM-backed
10
+ cache's contents can be serialized using YAML.
11
+
12
+ h2. Setup
13
+
14
+ bc. rubygems install feedtools_ram_cache
15
+
16
+ h2. Usage
17
+
18
+ The gem does not interfere with the FeedTools API. To enable RAM-based caching,
19
+ require the gem and configure FeedTools to use it:
20
+
21
+ bc. require 'feed_tools_ram_cache'
22
+ FeedTools.configurations[:feed_cache] = 'RamFeedCache'
23
+
24
+ h3. Serializing the Cache State
25
+
26
+ The cache state is a Ruby @Hash@ available via the class attribute @state@. It
27
+ can be stored in a YAML file, or loaded from a file. This can come in handy for
28
+ tests.
29
+
30
+ bc. # Save the cache state to state.yml
31
+ File.open('state.yml', 'w') { |f| YAML.dump FeedTools::RamFeedCache.state, f }
32
+ # Load the cache state from state.yml
33
+ FeedTools::RamFeedCache.state = YAML.load File.read('state.yml')
34
+
35
+ h3. Clearing the Cache
36
+
37
+ The RAM-backed cache does not implement a size limitation. However, the cache
38
+ can be cleared. This prevents it from growing indefinitely, and also provides a
39
+ clean slate for testing.
40
+
41
+ bc. FeedTools::RamFeedCache.clear
42
+
43
+ h2. Naming Convention
44
+
45
+ The naming scheme for this gem is confusing, but it is consistent with the
46
+ naming of the FeedTools gem that it extends.
47
+
48
+ The gem is named +feedtools_ram_cache+, but the name of the library to be
49
+ required in Ruby is +feed_tools_ram_cache. This is consistent with FeedTools,
50
+ whose gem name is +feedtools+, providing the library +feed_tools+.
51
+
52
+ h2. Requirements
53
+
54
+ The gem depends on the +feedtools+ version used to test the code. If you need
55
+ the RAM-based cache for an older version of +feedtools+, try to run the tests
56
+ (via @rake test@) against your version of +feedtools+. If the tests pass, send
57
+ me a pull request.
58
+
59
+ h2. Contributions
60
+
61
+ I wrote the RAM-backed cache to fulfill my very specific needs. I will not be
62
+ adding features, but I will do my best to fix bugs. If you want a feature,
63
+ please don't hesitate to fork the project and send me pull requests.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ # Rakefile that uses echoe to manage feed_tools_ram_cache's gemspec.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'rubygems'
8
+ require 'echoe'
9
+
10
+ Echoe.new('feedtools_ram_cache') do |p|
11
+ p.project = 'zerglings' # RubyForge project.
12
+
13
+ p.author = 'Victor Costan'
14
+ p.email = 'victor@zergling.net'
15
+ p.summary = "RAM-based cache for FeedTools."
16
+ p.url = 'http://github.com/costan/feed_tools_ram_cache'
17
+ p.dependencies = ["feed_tools >=0.2.29"]
18
+ p.development_dependencies = ["echoe >=3.1.1", "flexmock >=0.8.6"]
19
+
20
+ p.need_tar_gz = true
21
+ p.need_zip = true
22
+ p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
23
+ end
24
+
25
+ if $0 == __FILE__
26
+ Rake.application = Rake::Application.new
27
+ Rake.application.run
28
+ end
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{feedtools_ram_cache}
5
+ s.version = "1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Victor Costan"]
9
+ s.date = %q{2009-09-20}
10
+ s.description = %q{RAM-based cache for FeedTools.}
11
+ s.email = %q{victor@zergling.net}
12
+ s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.textile", "lib/feed_tools_ram_cache.rb"]
13
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.textile", "Rakefile", "lib/feed_tools_ram_cache.rb", "test/cache_test.rb", "test/fixtures/unit.yml", "test/integration_test.rb", "feedtools_ram_cache.gemspec"]
14
+ s.homepage = %q{http://github.com/costan/feed_tools_ram_cache}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Feedtools_ram_cache", "--main", "README.textile"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{zerglings}
18
+ s.rubygems_version = %q{1.3.5}
19
+ s.summary = %q{RAM-based cache for FeedTools.}
20
+ s.test_files = ["test/cache_test.rb", "test/integration_test.rb"]
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 3
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<feed_tools>, [">= 0.2.29"])
28
+ s.add_development_dependency(%q<echoe>, [">= 3.1.1"])
29
+ s.add_development_dependency(%q<flexmock>, [">= 0.8.6"])
30
+ else
31
+ s.add_dependency(%q<feed_tools>, [">= 0.2.29"])
32
+ s.add_dependency(%q<echoe>, [">= 3.1.1"])
33
+ s.add_dependency(%q<flexmock>, [">= 0.8.6"])
34
+ end
35
+ else
36
+ s.add_dependency(%q<feed_tools>, [">= 0.2.29"])
37
+ s.add_dependency(%q<echoe>, [">= 3.1.1"])
38
+ s.add_dependency(%q<flexmock>, [">= 0.8.6"])
39
+ end
40
+ end
@@ -0,0 +1,122 @@
1
+ # RAM-backed cache for FeedTools.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'rubygems'
8
+ require 'feed_tools'
9
+
10
+ # :nodoc: namespace
11
+ module FeedTools
12
+
13
+
14
+ # RAM-backed cache for FeedTools.
15
+ #
16
+ # This is a drop-in replacement for FeedTools::DatabaseFeedCache that does not
17
+ # require a database.
18
+ class RamFeedCache
19
+ # Creates a new cache item.
20
+ def initialize(initial_values = {})
21
+ @fields = initial_values.dup
22
+ end
23
+
24
+ # The fields in this cache item.
25
+ attr_reader :fields
26
+
27
+ # The cache state, in a format that can be serialized.
28
+ def self.state
29
+ @by_id.values.map { |value| value.fields }
30
+ end
31
+
32
+ # Loads previously saved state into the cache.
33
+ def self.state=(new_state)
34
+ clear
35
+ new_state.each { |fields| write_item RamFeedCache.new(fields) }
36
+ end
37
+
38
+ # Removes all the cache items.
39
+ def self.clear
40
+ @by_id, @by_href = nil, nil
41
+ initialize_cache
42
+ end
43
+
44
+ # Fields required by FeedTools.
45
+ #
46
+ # The FeedTools documentation is outdated. The correct fields can be inferred
47
+ # from the migration found at:
48
+ # http://feedtools.rubyforge.org/svn/trunk/db/migration.rb
49
+ FIELDS = [:id, :href, :title, :link, :feed_data, :feed_data_type,
50
+ :http_headers, :last_retrieved, :time_to_live, :serialized]
51
+ FIELDS.each do |field_name|
52
+ define_method(field_name) { @fields[field_name] }
53
+ define_method(:"#{field_name}=") { |value| @fields[field_name] = value }
54
+ end
55
+
56
+ # Cache indexes.
57
+ @by_id, @by_href = nil, nil
58
+
59
+ # Writes an item into the cache
60
+ def self.write_item(item)
61
+ # NOTE: FeedTools seems to rely on ActiveRecord's auto-incrementing IDs.
62
+ item.id = @by_id.length + 1 unless item.id
63
+
64
+ @by_id[item.id] = item
65
+ @by_href[item.href] = item
66
+ end
67
+
68
+ # Called by FeedTools to save the cache item.
69
+ def save
70
+ self.class.write_item(self)
71
+ true
72
+ end
73
+
74
+ # Called by FeedTools to initialize the cache.
75
+ def self.initialize_cache
76
+ # NOTE: the FeedTools documentation says this will be called once. In fact,
77
+ # the method is called over and over again.
78
+ @by_id ||= {}
79
+ @by_href ||= {}
80
+ end
81
+
82
+ # Called by FeedTools to determine if the cache is online.
83
+ def self.connected?
84
+ @by_id != nil
85
+ end
86
+
87
+ # FeedTools documentation doesn't specify this method, but the implementation
88
+ # calls it.
89
+ def self.set_up_correctly?
90
+ connected?
91
+ end
92
+
93
+ # FeedTools documentation doesn't specify this method, but the implementation
94
+ # calls it.
95
+ def self.table_exists?
96
+ true
97
+ end
98
+
99
+ # Required by FeedTools.
100
+ def self.find_by_id(id)
101
+ @by_id[id]
102
+ end
103
+
104
+ # Required by FeedTools.
105
+ def self.find_by_href(url)
106
+ @by_href[url]
107
+ end
108
+
109
+ # Good idea: override equality comparison so it works for equal cache entries.
110
+ # Must also override hash, since we're overriding ==.
111
+
112
+ def ==(other)
113
+ return false unless other.kind_of?(RamFeedCache)
114
+ @fields == other.fields
115
+ end
116
+
117
+ def hash
118
+ @fields.hash
119
+ end
120
+ end # class RamFeedCache
121
+
122
+ end # namespace FeedTools
@@ -0,0 +1,113 @@
1
+ # Author:: Victor Costan
2
+ # Copyright:: Copyright (C) 2009 Zergling.Net
3
+ # License:: MIT
4
+
5
+ require 'feed_tools_ram_cache'
6
+ require 'test/unit'
7
+
8
+
9
+ class CacheTest < Test::Unit::TestCase
10
+ Cache = FeedTools::RamFeedCache
11
+
12
+ def setup
13
+ Cache.state = []
14
+ Cache.new(:href => "google").save
15
+ Cache.new(:href => "yahoo").save
16
+ Cache.new(:href => "bing", :id => 100).save
17
+ Cache.new(:href => "ask").save
18
+
19
+ @golden_fields = Hash[*Cache::FIELDS.map { |f| [f, _value(f)] }.flatten]
20
+ fixtures_path = File.join File.dirname(__FILE__), 'fixtures'
21
+ @golden_state = YAML.load File.read(File.join(fixtures_path, 'unit.yml'))
22
+ end
23
+
24
+ # The golden value for a field. This is the field's value in golden_state.
25
+ def _value(field)
26
+ "Value: #{field} - #{field.to_s.reverse}"
27
+ end
28
+
29
+ def _check_field_fetch(item)
30
+ Cache::FIELDS.each do |field|
31
+ assert_equal _value(field), item.send(field),
32
+ "Field fetch check failed for field #{field}"
33
+ end
34
+ end
35
+
36
+ def _check_state(item)
37
+ assert_equal @golden_fields, item.fields, 'Item state check failed'
38
+ end
39
+
40
+ def test_item_fields
41
+ item = Cache.new
42
+ Cache::FIELDS.each { |field| item.send :"#{field}=", _value(field) }
43
+ _check_field_fetch item
44
+ _check_state item
45
+ end
46
+
47
+ def test_item_state_load
48
+ item = Cache.new @golden_fields
49
+ _check_field_fetch item
50
+ _check_state item
51
+ end
52
+
53
+ def test_find_by_id
54
+ assert_equal nil, Cache.find_by_id(0), 'ID 0 should not exist'
55
+ assert_equal 'google', Cache.find_by_id(1).href
56
+ assert_equal 'yahoo', Cache.find_by_id(2).href
57
+ assert_equal 'bing', Cache.find_by_id(100).href
58
+ assert_equal 'ask', Cache.find_by_id(4).href
59
+ end
60
+
61
+ def test_find_by_href
62
+ assert_equal nil, Cache.find_by_href('inktomi'), 'Inktomi should not exist'
63
+ assert_equal 1, Cache.find_by_href('google').id
64
+ assert_equal 2, Cache.find_by_href('yahoo').id
65
+ assert_equal 100, Cache.find_by_href('bing').id
66
+ assert_equal 4, Cache.find_by_href('ask').id
67
+ end
68
+
69
+ def test_equality
70
+ assert_equal Cache.find_by_id(1), Cache.find_by_href('google')
71
+ assert_not_equal Cache.find_by_id(2), Cache.find_by_href('google')
72
+ assert_not_equal Cache.find_by_id(2), nil
73
+ assert_not_equal nil, Cache.find_by_href('google')
74
+ end
75
+
76
+ def test_hash
77
+ assert_equal Cache.new(Cache.find_by_id(1).fields).hash,
78
+ Cache.find_by_href('google').hash
79
+ assert_not_equal Cache.find_by_id(2).hash, Cache.find_by_href('google').hash
80
+ end
81
+
82
+ def test_state_storing
83
+ #File.open('test/fixtures/unit.yml', 'w') do |f|
84
+ # YAML.dump Cache.state, f
85
+ #end
86
+ assert_equal @golden_state, Cache.state
87
+ end
88
+
89
+ def test_clear
90
+ Cache.clear
91
+ assert_equal nil, Cache.find_by_id(1), 'Cache not deleted - found ID 1'
92
+ assert_equal nil, Cache.find_by_href('google'),
93
+ 'Cache not deleted - found HREF google'
94
+ end
95
+
96
+ def test_state_loading
97
+ Cache.state = [{:id => 1}]
98
+
99
+ assert_equal nil, Cache.find_by_id(2), 'Cache state not reset - found ID 2'
100
+ assert_equal nil, Cache.find_by_href('google'),
101
+ 'Cache state not reset - found google'
102
+ end
103
+
104
+ def test_state_reloading
105
+ Cache.clear
106
+ Cache.state = @golden_state
107
+
108
+ test_find_by_id
109
+ test_find_by_href
110
+ test_equality
111
+ test_hash
112
+ end
113
+ end
@@ -0,0 +1,9 @@
1
+ ---
2
+ - :href: bing
3
+ :id: 100
4
+ - :href: google
5
+ :id: 1
6
+ - :href: yahoo
7
+ :id: 2
8
+ - :href: ask
9
+ :id: 4
@@ -0,0 +1,54 @@
1
+ # Author:: Victor Costan
2
+ # Copyright:: Copyright (C) 2009 Zergling.Net
3
+ # License:: MIT
4
+
5
+ require 'feed_tools_ram_cache'
6
+ require 'test/unit'
7
+
8
+ require 'benchmark'
9
+ require 'open-uri'
10
+
11
+
12
+ class IntegrationTest < Test::Unit::TestCase
13
+ def setup
14
+ FeedTools.configurations[:feed_cache] = 'RamFeedCache'
15
+ FeedTools::RamFeedCache.clear
16
+ end
17
+
18
+ def test_with_atom
19
+ uri = 'http://feeds2.feedburner.com/VictorCostan'
20
+ _test_cache uri
21
+ end
22
+
23
+ def test_with_rss
24
+ uri = 'http://events.mit.edu/rss/'
25
+ _test_cache uri
26
+ end
27
+
28
+ def _test_cache(uri)
29
+ feed, cached_feed = nil, nil
30
+ time = Benchmark.realtime { feed = FeedTools::Feed.open uri }
31
+
32
+ # Make sure the cache's state is YAML-serializable
33
+ yaml_state = FeedTools::RamFeedCache.state.to_yaml
34
+ FeedTools::RamFeedCache.state = YAML.load yaml_state
35
+
36
+ cached_time = Benchmark.realtime { cached_feed = FeedTools::Feed.open uri }
37
+
38
+ assert !cached_feed.live?, 'cached_feed is live?'
39
+ assert_operator cached_time, :< , time / 3,
40
+ 'Cached retrieval should be faster'
41
+
42
+ assert_equal feed.items.length, cached_feed.items.length,
43
+ 'Incorrect cached data (item count)'
44
+ feed.items.each_index do |i|
45
+ [:guid, :published, :link, :summary, :description].each do |field|
46
+ assert_equal cached_feed.items[i].send(field),
47
+ feed.items[i].send(field),
48
+ "Incorrect cached data at index #{i} field #{field}: " +
49
+ feed.items[i].inspect + ' vs ' +
50
+ cached_feed.items[i].inspect
51
+ end
52
+ end
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feedtools_ram_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: "1.0"
5
+ platform: ruby
6
+ authors:
7
+ - Victor Costan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-20 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: feed_tools
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.2.29
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: echoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: flexmock
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.8.6
44
+ version:
45
+ description: RAM-based cache for FeedTools.
46
+ email: victor@zergling.net
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - CHANGELOG
53
+ - LICENSE
54
+ - README.textile
55
+ - lib/feed_tools_ram_cache.rb
56
+ files:
57
+ - CHANGELOG
58
+ - LICENSE
59
+ - Manifest
60
+ - README.textile
61
+ - Rakefile
62
+ - lib/feed_tools_ram_cache.rb
63
+ - test/cache_test.rb
64
+ - test/fixtures/unit.yml
65
+ - test/integration_test.rb
66
+ - feedtools_ram_cache.gemspec
67
+ has_rdoc: true
68
+ homepage: http://github.com/costan/feed_tools_ram_cache
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options:
73
+ - --line-numbers
74
+ - --inline-source
75
+ - --title
76
+ - Feedtools_ram_cache
77
+ - --main
78
+ - README.textile
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "1.2"
92
+ version:
93
+ requirements: []
94
+
95
+ rubyforge_project: zerglings
96
+ rubygems_version: 1.3.5
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: RAM-based cache for FeedTools.
100
+ test_files:
101
+ - test/cache_test.rb
102
+ - test/integration_test.rb