tobi-cached 0.5.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/History.txt +4 -0
- data/Manifest.txt +18 -0
- data/PostInstall.txt +0 -0
- data/README.rdoc +63 -0
- data/Rakefile +29 -0
- data/cached.gemspec +41 -0
- data/init.rb +4 -0
- data/lib/cached.rb +17 -0
- data/lib/cached/config.rb +18 -0
- data/lib/cached/config_compiler.rb +103 -0
- data/lib/cached/model.rb +27 -0
- data/lib/cached/record.rb +25 -0
- data/test/people_database.rb +12 -0
- data/test/test_cached.rb +85 -0
- data/test/test_cached_delegation.rb +41 -0
- data/test/test_config_compiler.rb +53 -0
- data/test/test_helper.rb +24 -0
- data/test/test_record.rb +56 -0
- metadata +108 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
cached.gemspec
|
7
|
+
init.rb
|
8
|
+
lib/cached.rb
|
9
|
+
lib/cached/config.rb
|
10
|
+
lib/cached/config_compiler.rb
|
11
|
+
lib/cached/model.rb
|
12
|
+
lib/cached/record.rb
|
13
|
+
test/people_database.rb
|
14
|
+
test/test_cached.rb
|
15
|
+
test/test_cached_delegation.rb
|
16
|
+
test/test_config_compiler.rb
|
17
|
+
test/test_helper.rb
|
18
|
+
test/test_record.rb
|
data/PostInstall.txt
ADDED
File without changes
|
data/README.rdoc
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
= cached
|
2
|
+
|
3
|
+
* http://github.com/tobi/cached
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Small trial project that attempts to accelerate common active record like operations without too much black magic.
|
8
|
+
|
9
|
+
|
10
|
+
== SYNOPSIS:
|
11
|
+
|
12
|
+
class Product < AR:Base
|
13
|
+
|
14
|
+
cache_by_key :id do
|
15
|
+
index :name
|
16
|
+
index [:brand, :name]
|
17
|
+
|
18
|
+
delegate_to :find
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Product.create :name => 'ipod', :brand => 'apple'
|
23
|
+
|
24
|
+
Product.lookup(1) # cache_hit, 1 memcached query
|
25
|
+
Product.lookup(2) # cache_miss, 1 memcached query, 1 db query: .find(2) (see delegates_to)
|
26
|
+
Product.lookup_by_name('ipod') # cache_hit, 2 memcached queries
|
27
|
+
Product.lookup_by_brand_and_name('apple','ipod') # cache_hit, 2 memcached queries
|
28
|
+
|
29
|
+
Product.lookup_by_something('else') # raise NoMethodError
|
30
|
+
|
31
|
+
|
32
|
+
== REQUIREMENTS:
|
33
|
+
|
34
|
+
* Active Record included with rails 2.3+
|
35
|
+
|
36
|
+
== INSTALL:
|
37
|
+
|
38
|
+
* Gem install or plugin install
|
39
|
+
|
40
|
+
== LICENSE:
|
41
|
+
|
42
|
+
(The MIT License)
|
43
|
+
|
44
|
+
Copyright (c) 2009 Tobias Lütke
|
45
|
+
|
46
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
47
|
+
a copy of this software and associated documentation files (the
|
48
|
+
'Software'), to deal in the Software without restriction, including
|
49
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
50
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
51
|
+
permit persons to whom the Software is furnished to do so, subject to
|
52
|
+
the following conditions:
|
53
|
+
|
54
|
+
The above copyright notice and this permission notice shall be
|
55
|
+
included in all copies or substantial portions of the Software.
|
56
|
+
|
57
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
58
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
59
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
60
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
61
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
62
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
63
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
BEGIN {$VERBOSE = nil;$DEBUG = nil}
|
2
|
+
|
3
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
4
|
+
%w[rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
5
|
+
require File.dirname(__FILE__) + '/lib/cached'
|
6
|
+
|
7
|
+
# Generate all the Rake tasks
|
8
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
9
|
+
$hoe = Hoe.new('cached', Cached::VERSION) do |p|
|
10
|
+
p.developer('Tobias Lütke', 'tobi@leetsoft.com')
|
11
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
12
|
+
p.extra_deps = [
|
13
|
+
['activesupport','>= 2.3.2'],
|
14
|
+
]
|
15
|
+
|
16
|
+
p.extra_dev_deps = [
|
17
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
18
|
+
]
|
19
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
20
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
21
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
22
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
26
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
27
|
+
|
28
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
29
|
+
# task :default => [:spec, :features]
|
data/cached.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{cached}
|
5
|
+
s.version = "0.5.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Tobias L\303\274tke"]
|
9
|
+
s.date = %q{2009-06-10}
|
10
|
+
s.description = %q{Small trial project that attempts to accelerate common active record like operations without too much black magic.}
|
11
|
+
s.email = ["tobi@leetsoft.com"]
|
12
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc"]
|
13
|
+
s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "cached.gemspec", "init.rb", "lib/cached.rb", "lib/cached/config.rb", "lib/cached/config_compiler.rb", "lib/cached/model.rb", "lib/cached/record.rb", "test/people_database.rb", "test/test_cached.rb", "test/test_cached_delegation.rb", "test/test_config_compiler.rb", "test/test_helper.rb", "test/test_record.rb"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://github.com/tobi/cached}
|
16
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{cached}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{Small trial project that attempts to accelerate common active record like operations without too much black magic.}
|
21
|
+
s.test_files = ["test/test_cached.rb", "test/test_cached_delegation.rb", "test/test_config_compiler.rb", "test/test_helper.rb", "test/test_record.rb"]
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 2
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
28
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.2"])
|
29
|
+
s.add_development_dependency(%q<newgem>, [">= 1.4.1"])
|
30
|
+
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.2"])
|
33
|
+
s.add_dependency(%q<newgem>, [">= 1.4.1"])
|
34
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
35
|
+
end
|
36
|
+
else
|
37
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.2"])
|
38
|
+
s.add_dependency(%q<newgem>, [">= 1.4.1"])
|
39
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
40
|
+
end
|
41
|
+
end
|
data/init.rb
ADDED
data/lib/cached.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_support'
|
6
|
+
|
7
|
+
module Cached
|
8
|
+
VERSION = '0.5.0'
|
9
|
+
|
10
|
+
mattr_accessor :store
|
11
|
+
self.store = ActiveSupport::Cache.lookup_store(:memory_store)
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
require 'cached/config'
|
16
|
+
require 'cached/config_compiler'
|
17
|
+
require 'cached/model'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Cached
|
2
|
+
class Config
|
3
|
+
attr_accessor :indexes, :class_name, :primary_key, :delegates
|
4
|
+
|
5
|
+
def initialize(class_name, primary_key)
|
6
|
+
@delegates, @indexes, @class_name, @primary_key = [], [], class_name.to_s, primary_key.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def index(*args)
|
10
|
+
@indexes.push args.flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
def delegate_to(*methods)
|
14
|
+
@delegates += methods.flatten
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Cached
|
2
|
+
|
3
|
+
class ConfigCompiler
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_ruby
|
10
|
+
[compiled_meta_methods, compiled_save_methods, compiled_fetch_methods].join
|
11
|
+
end
|
12
|
+
|
13
|
+
def compiled_meta_methods
|
14
|
+
|
15
|
+
# Instance methods
|
16
|
+
"def object_cache_primary_key; \"#{@config.primary_key}\"; end;" +
|
17
|
+
"def object_cache_key; \"\#{object_cache_prefix}:\#{#{@config.primary_key}}\"; end;" +
|
18
|
+
"def object_cache_prefix; \"#{@config.class_name}\"; end;" +
|
19
|
+
"def object_cache_hash(*args); args.join.hash; end;" +
|
20
|
+
|
21
|
+
# Class methods
|
22
|
+
"def self.object_cache_prefix; \"#{@config.class_name}\"; end;" +
|
23
|
+
"def self.object_cache_hash(*args); args.join.hash; end;"
|
24
|
+
end
|
25
|
+
|
26
|
+
def compiled_save_methods
|
27
|
+
compiled_save_object_method + compiled_save_index_method
|
28
|
+
end
|
29
|
+
|
30
|
+
def compiled_save_object_method
|
31
|
+
"def save_object_to_cache;" +
|
32
|
+
"Cached.store.write(object_cache_key, self);" +
|
33
|
+
"end;" +
|
34
|
+
|
35
|
+
"def expire_object_in_cache;" +
|
36
|
+
"Cached.store.delete(object_cache_key);" +
|
37
|
+
"end;"
|
38
|
+
end
|
39
|
+
|
40
|
+
def compiled_save_index_method
|
41
|
+
|
42
|
+
keys = @config.indexes.collect { |index| index_cache_key(index) }
|
43
|
+
|
44
|
+
"def save_indexes_to_cache;" +
|
45
|
+
"v = #{@config.primary_key};" +
|
46
|
+
keys.collect{|k| "Cached.store.write(#{k}, v);"}.join +
|
47
|
+
"end;" +
|
48
|
+
|
49
|
+
"def expire_indexes_in_cache;" +
|
50
|
+
keys.collect{|k| "Cached.store.delete(#{k});"}.join +
|
51
|
+
"end;"
|
52
|
+
end
|
53
|
+
|
54
|
+
def compiled_fetch_method_for(index)
|
55
|
+
index_name = index_name(index)
|
56
|
+
cache_key = index_cache_key(index)
|
57
|
+
|
58
|
+
method_suffix_and_parameters = "#{index_name}(#{index.join(', ')})"
|
59
|
+
|
60
|
+
delegation = @config.delegates.collect { |delegate| "|| #{delegate}_by_#{method_suffix_and_parameters}" }
|
61
|
+
|
62
|
+
"def self.lookup_by_#{method_suffix_and_parameters};" +
|
63
|
+
" if key = Cached.store.read(#{cache_key});"+
|
64
|
+
" lookup(key);"+
|
65
|
+
" else;"+
|
66
|
+
" obj = nil #{delegation};" +
|
67
|
+
" obj.save_indexes_to_cache if obj.respond_to?(:save_indexes_to_cache);" +
|
68
|
+
" obj;" +
|
69
|
+
" end;" +
|
70
|
+
"end;"
|
71
|
+
end
|
72
|
+
|
73
|
+
def compiled_fetch_method_for_primary_key
|
74
|
+
|
75
|
+
delegation = @config.delegates.collect{|c| "|| #{c}(pk)" }.join
|
76
|
+
|
77
|
+
"def self.lookup(pk);" +
|
78
|
+
" Cached.store.fetch(\"#\{object_cache_prefix}:#\{pk}\") { nil #{delegation} };" +
|
79
|
+
"end;" +
|
80
|
+
|
81
|
+
|
82
|
+
"def self.lookup_by_#{@config.primary_key}(pk);" +
|
83
|
+
" lookup(pk); "+
|
84
|
+
"end;"
|
85
|
+
end
|
86
|
+
|
87
|
+
def compiled_fetch_methods
|
88
|
+
compiled_fetch_method_for_primary_key + @config.indexes.collect { |index| compiled_fetch_method_for(index) }.join(';')
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def index_cache_key(index)
|
94
|
+
"\"#\{object_cache_prefix}/#{index_name(index)}:#\{object_cache_hash(#{index.join(', ')})}\""
|
95
|
+
end
|
96
|
+
|
97
|
+
def index_name(index)
|
98
|
+
index.collect(&:to_s).collect(&:downcase).collect(&:underscore).join('_and_')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
end
|
data/lib/cached/model.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Cached
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def cache_by_key(primary_key, &block)
|
7
|
+
config = Config.new(name.underscore.downcase, primary_key)
|
8
|
+
config.instance_eval(&block)
|
9
|
+
|
10
|
+
self.class_eval ConfigCompiler.new(config).to_ruby, __FILE__, __LINE__
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.extend ClassMethods
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def save_to_cache
|
20
|
+
save_object_to_cache
|
21
|
+
save_indexes_to_cache
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cached
|
2
|
+
module Record
|
3
|
+
|
4
|
+
def self.included?(base)
|
5
|
+
|
6
|
+
if base.respond_to?(:save)
|
7
|
+
base.alias_method_chain :save, :cached
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def save_with_cached
|
14
|
+
save_without_cached
|
15
|
+
|
16
|
+
# expire the cache, the object will be stored in it's prestine form
|
17
|
+
# after the next lookup call hopefull.
|
18
|
+
expire_object_in_cache
|
19
|
+
|
20
|
+
# update the indexes for the new values.
|
21
|
+
store_indexes_in_cache
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => '/tmp/people.db')
|
4
|
+
|
5
|
+
ActiveRecord::Schema.define do
|
6
|
+
|
7
|
+
create_table :people, :force => true do |t|
|
8
|
+
t.column :first_name, :string
|
9
|
+
t.column :last_name, :string
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
data/test/test_cached.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class Product < Struct.new(:id, :name, :price, :vendor)
|
4
|
+
include Cached::Model
|
5
|
+
|
6
|
+
cache_by_key :id do
|
7
|
+
index :name
|
8
|
+
index [:vendor, :name]
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
class TestCached < Test::Unit::TestCase
|
15
|
+
|
16
|
+
def setup
|
17
|
+
@product = Product.new(1, 'ipod', 149.00, 'apple')
|
18
|
+
end
|
19
|
+
|
20
|
+
context "cache storage" do
|
21
|
+
|
22
|
+
test "product can be stored to cache" do
|
23
|
+
@product.respond_to?(:save_to_cache)
|
24
|
+
end
|
25
|
+
|
26
|
+
test "product stores meta data in instance methods" do
|
27
|
+
assert_equal "id", @product.object_cache_primary_key
|
28
|
+
end
|
29
|
+
|
30
|
+
test "product has efficient object_cache_key instance method" do
|
31
|
+
assert_equal "product:1", @product.object_cache_key
|
32
|
+
end
|
33
|
+
|
34
|
+
test "product stores itself to memcached on save_to_cache call" do
|
35
|
+
assert @product.save_to_cache
|
36
|
+
assert_equal @product, Cached.store.read('product:1')
|
37
|
+
end
|
38
|
+
|
39
|
+
test "product stores defined indexes as backreference to product key" do
|
40
|
+
assert @product.save_to_cache
|
41
|
+
assert_equal 1, Cached.store.read("product/name:#{hash('ipod')}")
|
42
|
+
assert_equal 1, Cached.store.read("product/vendor_and_name:#{hash('appleipod')}")
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context "lookups" do
|
48
|
+
|
49
|
+
test "product explicit lookup by primary_key" do
|
50
|
+
@product.save_to_cache
|
51
|
+
Cached.store.expects(:read).with('product:1', {})
|
52
|
+
Product.lookup_by_id(1)
|
53
|
+
end
|
54
|
+
|
55
|
+
test "product lookup by primary_key" do
|
56
|
+
@product.save_to_cache
|
57
|
+
Cached.store.expects(:read).with('product:1', {})
|
58
|
+
Product.lookup(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
test "product lookup by index" do
|
62
|
+
@product.save_to_cache
|
63
|
+
Cached.store.expects(:read).with("product/name:#{hash('ipod')}").returns(1)
|
64
|
+
Cached.store.expects(:read).with('product:1', {})
|
65
|
+
Product.lookup_by_name('ipod')
|
66
|
+
end
|
67
|
+
|
68
|
+
test "product lookup by multi index" do
|
69
|
+
@product.save_to_cache
|
70
|
+
Cached.store.expects(:read).with("product/vendor_and_name:#{hash('appleipod')}").returns(1)
|
71
|
+
Cached.store.expects(:read).with('product:1', {})
|
72
|
+
Product.lookup_by_vendor_and_name('apple', 'ipod')
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def hash(text)
|
80
|
+
text.hash
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
|
4
|
+
class DelegatingProduct < Struct.new(:id, :name, :price, :vendor)
|
5
|
+
include Cached::Model
|
6
|
+
|
7
|
+
cache_by_key :id do
|
8
|
+
index :name
|
9
|
+
index [:vendor, :name]
|
10
|
+
|
11
|
+
delegate_to :find
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestCachedDelegation < Test::Unit::TestCase
|
16
|
+
|
17
|
+
def setup
|
18
|
+
@delegating_product = DelegatingProduct.new(1, 'ipod', 149.00, 'apple')
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
test "lookup miss will delegate to find method" do
|
23
|
+
DelegatingProduct.expects(:find).with(2)
|
24
|
+
DelegatingProduct.lookup(2)
|
25
|
+
end
|
26
|
+
|
27
|
+
test "lookup by index miss will delegate to find_by_index method" do
|
28
|
+
DelegatingProduct.expects(:find).with(2)
|
29
|
+
DelegatingProduct.lookup(2)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def hash(text)
|
35
|
+
text.hash
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestConfigCompiler < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@dump = Class.new
|
7
|
+
@config = Cached::ConfigCompiler.new(Cached::Config.new('product', 'id'))
|
8
|
+
end
|
9
|
+
|
10
|
+
test "syntax compiled_meta_methods" do
|
11
|
+
@dump.instance_eval @config.compiled_meta_methods
|
12
|
+
@dump.respond_to?(:object_cache_primary_key)
|
13
|
+
@dump.respond_to?(:object_cache_key)
|
14
|
+
@dump.respond_to?(:object_cache_prefix)
|
15
|
+
@dump.respond_to?(:object_cache_hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
test "syntax compiled_save_object_method" do
|
19
|
+
@dump.instance_eval @config.compiled_save_object_method
|
20
|
+
|
21
|
+
@dump.respond_to?(:save_object_to_cache)
|
22
|
+
end
|
23
|
+
|
24
|
+
test "syntax compiled_save_index_method" do
|
25
|
+
@dump.instance_eval @config.compiled_save_index_method
|
26
|
+
|
27
|
+
@dump.respond_to?(:save_indexes_to_cache)
|
28
|
+
end
|
29
|
+
|
30
|
+
test "syntax compiled_fetch_method_for single column" do
|
31
|
+
@dump.instance_eval @config.compiled_fetch_method_for([:name])
|
32
|
+
|
33
|
+
@dump.respond_to?(:lookup_by_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
test "syntax compiled_fetch_method_for multi column" do
|
37
|
+
@dump.instance_eval @config.compiled_fetch_method_for([:name, :brand])
|
38
|
+
|
39
|
+
@dump.respond_to?(:lookup_by_name_and_brand)
|
40
|
+
end
|
41
|
+
|
42
|
+
test "syntax compiled_fetch_method_for_primary_key" do
|
43
|
+
@dump.instance_eval @config.compiled_fetch_method_for_primary_key
|
44
|
+
|
45
|
+
@dump.respond_to?(:lookup)
|
46
|
+
@dump.respond_to?(:lookup_by_id)
|
47
|
+
end
|
48
|
+
|
49
|
+
test "all syntax" do
|
50
|
+
@dump.instance_eval @config.to_ruby
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'mocha'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/cached'
|
6
|
+
|
7
|
+
require 'active_support/cache'
|
8
|
+
require 'active_record'
|
9
|
+
|
10
|
+
Cached.store = ActiveSupport::Cache.lookup_store(:memory_store)
|
11
|
+
#Cached.store = ActiveSupport::Cache.lookup_store(:mem_cache_store)
|
12
|
+
|
13
|
+
|
14
|
+
class Test::Unit::TestCase
|
15
|
+
|
16
|
+
def self.test(string, &block)
|
17
|
+
define_method("test:#{string}", &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.context(string)
|
21
|
+
yield
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/test/test_record.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/people_database'
|
3
|
+
|
4
|
+
require 'cached/record'
|
5
|
+
|
6
|
+
class Person < ActiveRecord::Base
|
7
|
+
include Cached::Model
|
8
|
+
include Cached::Record
|
9
|
+
|
10
|
+
def name
|
11
|
+
"#{first_name} #{last_name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
cache_by_key :id do
|
15
|
+
index :first_name
|
16
|
+
index :last_name
|
17
|
+
index [:first_name, :last_name]
|
18
|
+
|
19
|
+
delegate_to :find
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class TestRecord < Test::Unit::TestCase
|
24
|
+
|
25
|
+
def setup
|
26
|
+
@bob = Person.create(:first_name => 'Bob', :last_name => 'Bobsen')
|
27
|
+
end
|
28
|
+
|
29
|
+
test "cachemiss delegates to find" do
|
30
|
+
assert !Cached.store.read("person:1")
|
31
|
+
assert_equal @bob, Person.lookup(@bob.id)
|
32
|
+
end
|
33
|
+
|
34
|
+
test "load bob from cache store" do
|
35
|
+
@bob.save_to_cache
|
36
|
+
|
37
|
+
assert_equal @bob, Person.lookup_by_first_name('Bob')
|
38
|
+
assert_equal @bob, Person.lookup_by_last_name('Bobsen')
|
39
|
+
assert_equal @bob, Person.lookup_by_first_name_and_last_name('Bob', 'Bobsen')
|
40
|
+
end
|
41
|
+
|
42
|
+
test "cache miss on index query will make returned object re-save its indexes" do
|
43
|
+
assert !Cached.store.read("person/first_name:#{hash('Bob')}")
|
44
|
+
assert_equal @bob, Person.lookup_by_first_name('Bob')
|
45
|
+
|
46
|
+
assert Cached.store.read("person/first_name:#{hash('Bob')}")
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def hash(text)
|
53
|
+
text.hash
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tobi-cached
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Tobias L\xC3\xBCtke"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-10 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.3.2
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: newgem
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.4.1
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.0
|
44
|
+
version:
|
45
|
+
description: Small trial project that attempts to accelerate common active record like operations without too much black magic.
|
46
|
+
email:
|
47
|
+
- tobi@leetsoft.com
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- PostInstall.txt
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- History.txt
|
59
|
+
- Manifest.txt
|
60
|
+
- PostInstall.txt
|
61
|
+
- README.rdoc
|
62
|
+
- Rakefile
|
63
|
+
- cached.gemspec
|
64
|
+
- init.rb
|
65
|
+
- lib/cached.rb
|
66
|
+
- lib/cached/config.rb
|
67
|
+
- lib/cached/config_compiler.rb
|
68
|
+
- lib/cached/model.rb
|
69
|
+
- lib/cached/record.rb
|
70
|
+
- test/people_database.rb
|
71
|
+
- test/test_cached.rb
|
72
|
+
- test/test_cached_delegation.rb
|
73
|
+
- test/test_config_compiler.rb
|
74
|
+
- test/test_helper.rb
|
75
|
+
- test/test_record.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://github.com/tobi/cached
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options:
|
80
|
+
- --main
|
81
|
+
- README.rdoc
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
version:
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project: cached
|
99
|
+
rubygems_version: 1.2.0
|
100
|
+
signing_key:
|
101
|
+
specification_version: 2
|
102
|
+
summary: Small trial project that attempts to accelerate common active record like operations without too much black magic.
|
103
|
+
test_files:
|
104
|
+
- test/test_cached.rb
|
105
|
+
- test/test_cached_delegation.rb
|
106
|
+
- test/test_config_compiler.rb
|
107
|
+
- test/test_helper.rb
|
108
|
+
- test/test_record.rb
|