cachesrb 0.2.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/README ADDED
@@ -0,0 +1,24 @@
1
+ Caches.rb
2
+ =========
3
+
4
+ Caches.rb is a simplistic method cache for Ruby
5
+
6
+ Simply do `BaseClass.extend Caches' and you will be able to specify
7
+
8
+ caches :method_name
9
+ or
10
+ caches :method_name, :timeout => 2.minutes
11
+
12
+ Default timeout is 60 seconds.
13
+
14
+ What is important is that this solution caches calls with arguments. For example, if you will make a method_name(“Hello”) call you’ll get next method_name(“Hello”) cached, but method_name(“Bye”) will not be cached.
15
+
16
+ Also you will be able to invalidate cache explicitly with invalidate_method_name_cache and invalidate_all_method_name_caches calls.
17
+
18
+ Also you can invalidate caches with
19
+
20
+ invalidate_all_caches
21
+ or
22
+ invalidate_all_caches :except => :name
23
+ or
24
+ invalidate_all_caches :except => [:name, :name1]
@@ -0,0 +1,52 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ desc 'Default: run unit tests.'
7
+ task :default => :test
8
+
9
+ desc 'Test the request_routing plugin.'
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the Caches.rb plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'Caches.rb'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ PKG_VERSION = "0.2.0"
26
+ PKG_NAME = "cachesrb"
27
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
28
+
29
+ PKG_FILES = FileList[
30
+ "lib/**/*", "test/**/*", "[A-Z]*", "Rakefile", "init.rb"
31
+ ].exclude(/\bCVS\b|~$|\.svn/)
32
+
33
+ spec = Gem::Specification.new do |s|
34
+ s.name = PKG_NAME
35
+ s.version = PKG_VERSION
36
+ s.summary = "Caches.rb -- simple Ruby method caching"
37
+ s.has_rdoc = true
38
+ s.files = PKG_FILES
39
+
40
+ s.require_path = 'lib'
41
+ s.autorequire = 'caches'
42
+ s.author = "Yurii Rashkovskii"
43
+ s.email = "yrashk@verbdev.com"
44
+ s.homepage = "http://rashkovskii.com/articles/tag/caches"
45
+ s.rubyforge_project = "cachesrb"
46
+ end
47
+
48
+ Rake::GemPackageTask.new(spec) do |p|
49
+ p.gem_spec = spec
50
+ p.need_tar = true
51
+ p.need_zip = true
52
+ end
data/init.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'caches'
2
+
3
+ begin
4
+ Kernel.const_get :ActiveRecord
5
+ ActiveRecord::Base.extend Caches
6
+ rescue
7
+ end
@@ -0,0 +1,68 @@
1
+ class String
2
+ def starts_with?(prefix)
3
+ prefix = prefix.to_s
4
+ self[0, prefix.length] == prefix
5
+ end
6
+ end
7
+
8
+ module Caches
9
+ def caches(name,options = { :timeout => 60})
10
+ sanitized_name = name.to_s.delete('?')
11
+ saved_getter = "getter_#{name}"
12
+ saved_setter = "setter_#{name}"
13
+ setter = "#{name}="
14
+
15
+ has_setter = new.respond_to? setter.to_sym
16
+
17
+ module_eval "alias #{saved_getter} #{name}"
18
+ module_eval "alias #{saved_setter} #{setter.to_sym}" if has_setter
19
+ module_eval do
20
+ define_method("invalidate_all_caches") do |*opts|
21
+ unless opts.empty?
22
+ opthash = opts.first
23
+ except = opthash[:except]
24
+ if except
25
+ except = [except] unless except.kind_of? Enumerable
26
+ @propcache.each_pair do |k,v|
27
+ @propcache[k] = nil unless except.any? {|exception| k.starts_with?(exception.to_s)}
28
+ end
29
+ end
30
+ else
31
+ @propcache = {}
32
+ end
33
+ end
34
+ define_method("invalidate_#{sanitized_name}_cache") do |*args|
35
+ @propcache ||= {}
36
+ key = name.to_s+args.hash.to_s
37
+ @propcache[key] = nil
38
+ end
39
+ define_method("invalidate_all_#{sanitized_name}_caches") do
40
+ @propcache ||= {}
41
+ key = name.to_s
42
+ @propcache.keys.each {|k| @propcache[k] = nil if k.starts_with?(key) }
43
+ end
44
+ define_method("#{name}") do |*args| # FIXME: this implementation smells bad
45
+ @propcache ||= {}
46
+ key = name.to_s+args.hash.to_s
47
+ cached = @propcache[key]
48
+ unless cached
49
+ @propcache[key] = { :value => self.send(saved_getter.to_sym,*args), :expires_at => Time.now.to_i + options[:timeout]}
50
+ return @propcache[key][:value]
51
+ else
52
+ unless Time.now.to_i > cached[:expires_at]
53
+ cached[:value]
54
+ else
55
+ self.send "invalidate_#{sanitized_name}_cache".to_sym, *args
56
+ self.send name.to_sym, *args
57
+ end
58
+ end
59
+ end
60
+ if has_setter
61
+ define_method("#{setter}") do |new_val|
62
+ self.send "invalidate_#{sanitized_name}_cache".to_sym
63
+ self.send saved_setter.to_sym, new_val
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,178 @@
1
+ require 'test/unit'
2
+ require 'test/time_mock'
3
+
4
+ require File.dirname(__FILE__) + "/../init"
5
+
6
+ class CachedBase ; end ; CachedBase.extend Caches
7
+
8
+
9
+ class CachedClass < CachedBase
10
+
11
+ attr_reader :accessor_counter
12
+
13
+ def initialize
14
+ @accessor = "Value"
15
+ @accessor_counter = 0
16
+ end
17
+
18
+ def accessor
19
+ @accessor_counter += 1
20
+ @accessor
21
+ end
22
+
23
+ def accessor=(new_val)
24
+ @accessor = new_val
25
+ end
26
+
27
+ alias :accessor2 :accessor
28
+
29
+ caches :accessor
30
+ caches :accessor2, :timeout => 120
31
+
32
+ def method_with_args(a,b)
33
+ "#{a}#{b}#{Time.now}"
34
+ end
35
+
36
+ caches :method_with_args
37
+
38
+ end
39
+
40
+ class CachesTest < Test::Unit::TestCase
41
+
42
+ def setup
43
+ @cached = CachedClass.new
44
+ end
45
+
46
+ def teardwon
47
+ Time.forced_now_time = nil
48
+ end
49
+
50
+ def test_default_timeout
51
+ val = @cached.accessor
52
+ assert_equal 1, @cached.accessor_counter
53
+ Time.forced_now_time = Time.now
54
+ 10.times { @cached.accessor }
55
+ assert_equal 1, @cached.accessor_counter
56
+ # In 60 seconds, we still cache
57
+ Time.forced_now_time = Time.now + 60
58
+ val = @cached.accessor
59
+ assert_equal 1, @cached.accessor_counter
60
+ # In 61, cache is invalidated
61
+ Time.forced_now_time = Time.now + 61
62
+ val = @cached.accessor
63
+ assert_equal 2, @cached.accessor_counter
64
+ end
65
+
66
+ def test_not_default_timeout
67
+ val = @cached.accessor2
68
+ assert_equal 1, @cached.accessor_counter
69
+ Time.forced_now_time = Time.now
70
+ 10.times { @cached.accessor2 }
71
+ assert_equal 1, @cached.accessor_counter
72
+ # In 2 minutes, we still cache
73
+ Time.forced_now_time = Time.now + 120
74
+ val = @cached.accessor2
75
+ assert_equal 1, @cached.accessor_counter
76
+ # In 2m1sec, cache is invalidated
77
+ Time.forced_now_time = Time.now + 121
78
+ val = @cached.accessor2
79
+ assert_equal 2, @cached.accessor_counter
80
+ end
81
+
82
+ def test_invalidate_accessor_by_assignment
83
+ val = @cached.accessor
84
+ assert_equal 1, @cached.accessor_counter
85
+
86
+ @cached.accessor = "Hello"
87
+ val = @cached.accessor
88
+ assert_equal 2, @cached.accessor_counter
89
+ end
90
+
91
+ def test_invalidate_accessor_explicitely
92
+ val = @cached.accessor
93
+ assert_equal 1, @cached.accessor_counter
94
+
95
+ @cached.invalidate_accessor_cache
96
+ val = @cached.accessor
97
+ assert_equal 2, @cached.accessor_counter
98
+
99
+ @cached.invalidate_all_accessor_caches
100
+ val = @cached.accessor
101
+ assert_equal 3, @cached.accessor_counter
102
+
103
+ @cached.invalidate_all_caches
104
+ val = @cached.accessor
105
+ assert_equal 4, @cached.accessor_counter
106
+ end
107
+
108
+ def test_caches_method_with_args
109
+ val = @cached.method_with_args("a","b")
110
+ valA = @cached.method_with_args("a","C")
111
+ assert !(val==valA)
112
+ Time.forced_now_time = Time.now + 1
113
+ val1 = @cached.method_with_args("a","b")
114
+ Time.forced_now_time = nil
115
+ assert_equal val, val1
116
+ end
117
+
118
+
119
+ def test_invalidate_method_with_args_explicitely
120
+ # Invalidating with another arguments does not work:
121
+ val = @cached.method_with_args("a","b")
122
+ @cached.invalidate_method_with_args_cache("A","B")
123
+ Time.forced_now_time = Time.now + 1
124
+ val1 = @cached.method_with_args("a","b")
125
+ Time.forced_now_time = nil
126
+ assert (val==val1)
127
+
128
+ @cached.invalidate_all_caches
129
+
130
+ val = @cached.method_with_args("a","b")
131
+ @cached.invalidate_method_with_args_cache("a","b")
132
+ Time.forced_now_time = Time.now + 1
133
+ val1 = @cached.method_with_args("a","b")
134
+ Time.forced_now_time = nil
135
+ assert !(val==val1)
136
+
137
+ @cached.invalidate_all_caches
138
+
139
+ val = @cached.method_with_args("a","b")
140
+ @cached.invalidate_all_method_with_args_caches
141
+ Time.forced_now_time = Time.now + 1
142
+ val1 = @cached.method_with_args("a","b")
143
+ Time.forced_now_time = nil
144
+ assert !(val==val1)
145
+
146
+ @cached.invalidate_all_caches
147
+
148
+ val = @cached.method_with_args("a","b")
149
+ @cached.invalidate_all_caches
150
+ Time.forced_now_time = Time.now + 1
151
+ val1 = @cached.method_with_args("a","b")
152
+ Time.forced_now_time = nil
153
+ assert !(val==val1)
154
+ end
155
+
156
+ def test_invalidate_all_caches_except
157
+ val = @cached.accessor
158
+ val2 = @cached.accessor2
159
+ valM = @cached.method_with_args("a","b")
160
+ @cached.invalidate_all_caches :except => :method_with_args
161
+ Time.forced_now_time = Time.now + 1
162
+ valM1 = @cached.method_with_args("a","b")
163
+ Time.forced_now_time = nil
164
+ assert (valM==valM1)
165
+
166
+ @cached.invalidate_all_caches
167
+
168
+ val = @cached.accessor
169
+ val2 = @cached.accessor2
170
+ valM = @cached.method_with_args("a","b")
171
+ @cached.invalidate_all_caches :except => [:method_with_args]
172
+ Time.forced_now_time = Time.now + 1
173
+ valM1 = @cached.method_with_args("a","b")
174
+ Time.forced_now_time = nil
175
+ assert (valM==valM1)
176
+ end
177
+
178
+ end
@@ -0,0 +1,23 @@
1
+ class Time
2
+ @@forced_now_time = nil
3
+
4
+ def self.forced_now_time
5
+ @@forced_now_time
6
+ end
7
+
8
+ def self.forced_now_time=(time)
9
+ @@forced_now_time = time
10
+ end
11
+
12
+ class << self
13
+ def now_with_forcing
14
+ if @@forced_now_time
15
+ @@forced_now_time
16
+ else
17
+ now_without_forcing
18
+ end
19
+ end
20
+ alias_method :now_without_forcing, :now
21
+ alias_method :now, :now_with_forcing
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: cachesrb
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.2.0
7
+ date: 2006-09-25 00:00:00 +03:00
8
+ summary: Caches.rb -- simple Ruby method caching
9
+ require_paths:
10
+ - lib
11
+ email: yrashk@verbdev.com
12
+ homepage: http://rashkovskii.com/articles/tag/caches
13
+ rubyforge_project: cachesrb
14
+ description:
15
+ autorequire: caches
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Yurii Rashkovskii
31
+ files:
32
+ - lib/caches.rb
33
+ - test/caches_test.rb
34
+ - test/time_mock.rb
35
+ - Rakefile
36
+ - README
37
+ - init.rb
38
+ test_files: []
39
+
40
+ rdoc_options: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ executables: []
45
+
46
+ extensions: []
47
+
48
+ requirements: []
49
+
50
+ dependencies: []
51
+