markbates-cachetastic 3.0.0.20090611142033

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Mark Bates
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/README ADDED
@@ -0,0 +1,89 @@
1
+ =What is Cachetastic?
2
+ Cachetastic is an incredibly easy to use and administer caching framework. Just because it is easy to use, does not mean that
3
+ it is light with features. Cachetastic allows you to create classes that extend <tt>Cachetastic::Cache</tt>, configure them
4
+ each individually, and much more.
5
+
6
+ Unlike other systems each cache in your system can use different backends via the use of adapters that get assigned to each
7
+ cache, and globally. You can define different expiration times, loggers, marshal methods, and more! And again, you can choose to
8
+ define these settings globally, or for each cache!
9
+
10
+ Adapters are easy to write, so if the built in adapters don't float your boat, you can easily knock one off in short order.
11
+
12
+ ==Configuration:
13
+ Configuration of Cachetastic is done using the Configatron gem. I would recommend reading the documentation on it first, http://configatron.mackframework.com, before continuing.
14
+
15
+ All configuration settings hang off of the <tt>cachetastic</tt> namespace on <tt>configatron</tt>. The default settings
16
+ all hang off the <tt>defaults</tt> namespace on the <tt>cachetastic</tt> namespace, as shown below:
17
+
18
+ # This will write detailed information to the logger.
19
+ configatron.cachetastic.defaults.debug = false
20
+
21
+ # This is the type of file store to be used for this cache.
22
+ # More adapters can be developed and plugged in as desired.
23
+ # The default is Cachetastic::Adapters::LocalMemory
24
+ configatron.cachetastic.defaults.adapter = Cachetastic::Adapters::LocalMemory
25
+ configatron.cachetastic.defaults.adapter = Cachetastic::Adapters::File
26
+ configatron.cachetastic.defaults.adapter = Cachetastic::Adapters::Memcached
27
+
28
+ # This will marshall objects into and out of the store.
29
+ # The default is :none, except for Cachetastic::Adapters::File, which defaults to :yaml
30
+ configatron.cachetastic.defaults.marshall_method = :none
31
+ configatron.cachetastic.defaults.marshall_method = :yaml
32
+ configatron.cachetastic.defaults.marshall_method = :ruby
33
+
34
+ # This sets how long objects will live in the cache before they are auto expired.
35
+ configatron.cachetastic.defaults.default_expiry = 86400 # time in seconds (default: 24 hours)
36
+
37
+ # When setting objects into the cache the expiry_swing is +/- to the expiry time.
38
+ # Example: if the expiry time is 1 minute, and the swing is 15 seconds,
39
+ # objects will go into the cache with an expiry time sometime between 45 seconds and 75 seconds.
40
+ # The default is 0 seconds.
41
+ configatron.cachetastic.defaults.expiry_swing = 15
42
+
43
+ # Configure logging for the system.
44
+ # n number of logs can be configured for a cache.
45
+ log_1 = Logger.new(STDOUT)
46
+ log_1.level = Logger::DEBUG
47
+ log_2 = Logger.new("log/cachetastic.log")
48
+ log_2.level = Logger::ERROR
49
+ configatron.cachetastic.defaults.logger = [log_1, log_2]
50
+
51
+ Overriding settings per cache is very simple. Let's take the following two caches:
52
+
53
+ class UserCache < Cachetastic::Cache
54
+ end
55
+
56
+ class Admin::UserCache < Cachetastic::Cache
57
+ end
58
+
59
+ If we wanted to set the <tt>UserCache</tt> to use the <tt>Cachetastic::Adapters::File</tt> adapter and we wanted to set the adapter for
60
+ the <tt>Admin::UserCache</tt> to use <tt>Cachetastic::Adapters::Memcached</tt>, we would configure them like such:
61
+
62
+ configatron.cachetastic.user_cache.adapter = Cachetastic::Adapters::File
63
+ configatron.cachetastic.admin.user_cache.adapter = Cachetastic::Adapters::Memcached
64
+
65
+ In this scenario we have changed the adapters for each of the classes. All of the other default settings will remain intact for
66
+ each of those classes. This makes it incredibly easy to just change the one parameter you need, and not have to reset them all.
67
+
68
+ ==Examples:
69
+
70
+ class MyAwesomeCache < Cachetastic::Cache
71
+ end
72
+
73
+ MyAwesomeCache.set(1, [1,2,3])
74
+ MyAwesomeCache.get(1) # => [1,2,3]
75
+
76
+ class MyAwesomeCache < Cachetastic::Cache
77
+ class << self
78
+ def get(key, x, y)
79
+ super(key) do
80
+ n = x + y
81
+ set(key, n)
82
+ n
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ MyAwesomeCache.get(1, 2, 4) # => 8
89
+ MyAwesomeCache.get(1, 4, 4) # => 8
@@ -0,0 +1,178 @@
1
+ module Cachetastic # :nodoc:
2
+ module Adapters
3
+
4
+ class << self
5
+
6
+ # This method will return the appropriate
7
+ # <tt>Cachetastic::Adapters::Base</tt> class that is defined
8
+ # for the Class passed in. If an adapter has not been
9
+ # defined for the Class than the default adapter is returned.
10
+ #
11
+ # Examples:
12
+ # configatron.cachetastic.defaults.adapter = Cachetastic::Adapters::LocalMemory
13
+ # configatron.cachetastic.user.adapter = Cachetastic::Adapters::Memcached
14
+ # Cachetastic::Adapters.build(User).class # => Cachetastic::Adapters::Memcached
15
+ # Cachetastic::Adapters.build(Comment).class # => Cachetastic::Adapters::LocalMemory
16
+ def build(klass)
17
+ adp = klass.to_configatron(:cachetastic).adapter
18
+ if adp.nil?
19
+ adp = configatron.cachetastic.defaults.adapter
20
+ end
21
+ adp.new(klass)
22
+ end
23
+
24
+ end # class << self
25
+
26
+ # This class should be extended to create new adapters for various
27
+ # backends. It is important that all subclasses call the <tt>initialize</tt>
28
+ # method in this base, otherwise things just will not work right.
29
+ #
30
+ # This base class provides common functionality and an API for all
31
+ # adapters to be used with Cachetastic.
32
+ #
33
+ # The default settings for all adapters are:
34
+ #
35
+ # configatron.cachetastic.defaults.marshal_method = :none
36
+ # configatron.cachetastic.defaults.expiry_swing = 0
37
+ # configatron.cachetastic.defaults.default_expiry = 86400
38
+ # configatron.cachetastic.defaults.debug = true
39
+ # configatron.cachetastic.defaults.adapter = Cachetastic::Adapters::LocalMemory
40
+ # logger = ::Logger.new(File.join(FileUtils.pwd, 'log', 'cachetastic.log'))
41
+ # logger.level = ::Logger::DEBUG
42
+ # configatron.cachetastic.defaults.logger = logger
43
+ #
44
+ # See the README for more information on what each of those settings mean,
45
+ # and what are values may be used for each one.
46
+ class Base
47
+
48
+ # The Class that this adapter is associated with. Note that it is a
49
+ # <i>class</i> reference and not an instance reference.
50
+ attr_accessor :klass
51
+
52
+ # Creates a new adapter. It takes a class reference to tie the
53
+ # instance of the adapter to a particular class. Note that it is a
54
+ # <i>class</i> reference and not an instance reference.
55
+ #
56
+ # Examples:
57
+ # Cachetastic::Adapters::Base.new(User)
58
+ #
59
+ # Adapters are configured using the Configatron gem.
60
+ #
61
+ # Examples:
62
+ # configatron.cachetastic.user.adapter = Cachetastic::Adapters::File
63
+ # configatron.cachetastic.user.expiry_time = 5.hours
64
+ # configatron.cachetastic.defaults.expiry_time = 24.hours
65
+ #
66
+ # Refered to each adapter for its specific configuration settings.
67
+ def initialize(klass)
68
+ self.klass = klass
69
+ configatron.cachetastic.defaults.configatron_keys.each do |key|
70
+ define_accessor(key)
71
+ self.send("#{key}=", configatron.cachetastic.defaults.send(key))
72
+ end
73
+ klass.to_configatron(:cachetastic).configatron_keys.each do |key|
74
+ define_accessor(key)
75
+ self.send("#{key}=", klass.to_configatron(:cachetastic).send(key))
76
+ end
77
+ end
78
+
79
+ # <b>This method MUST be implemented by a subclass!</b>
80
+ #
81
+ # The implementation of this method should take a key and return
82
+ # an associated object, if available, from the underlying persistence
83
+ # layer.
84
+ def get(key)
85
+ raise NoMethodError.new('get')
86
+ end # get
87
+
88
+ # <b>This method MUST be implemented by a subclass!</b>
89
+ #
90
+ # The implementation of this method should take a key, a value, and
91
+ # an expiry time and save it to the persistence store, where it should
92
+ # live until it is either deleted by the user of the expiry time has passed.
93
+ def set(key, value, expiry_time = configatron.cachetastic.defaults.default_expiry)
94
+ raise NoMethodError.new('set')
95
+ end # set
96
+
97
+ # <b>This method MUST be implemented by a subclass!</b>
98
+ #
99
+ # The implementation of this method should take a key and remove
100
+ # an object, if it exists, from an underlying persistence store.
101
+ def delete(key)
102
+ raise NoMethodError.new('delete')
103
+ end # delete
104
+
105
+ # <b>This method MUST be implemented by a subclass!</b>
106
+ #
107
+ # The implementation of this method is expected to delete all
108
+ # objects belonging to the associated cache from the underlying
109
+ # persistence store. It is <b>NOT</b> meant to delete <b>ALL</b>
110
+ # objects across <b>ALL</b> caches for the underlying persistence
111
+ # store. That would be very very bad!!
112
+ def expire_all
113
+ raise NoMethodError.new('expire_all')
114
+ end # expire_all
115
+
116
+ # Allows an adapter to transform the key
117
+ # to a safe representation for it's backend.
118
+ # For example, the key: '$*...123()%~q' is not a
119
+ # key for the file system, so the
120
+ # Cachetastic::Adapters::File class should override
121
+ # this to make it safe for the file system.
122
+ def transform_key(key)
123
+ key
124
+ end
125
+
126
+ # <b>This method MUST be implemented by a subclass!</b>
127
+ #
128
+ # The implementation of this method should return <tt>true</tt>
129
+ # if the adapter is in a valid state, and <tt>false</tt> if it is
130
+ # not.
131
+ def valid?
132
+ true
133
+ end
134
+
135
+ def debug? # :nodoc:
136
+ return self.debug if self.respond_to?(:debug)
137
+ return false
138
+ end
139
+
140
+ def marshal(value) # :nodoc:
141
+ return nil if value.nil?
142
+ case self.marshal_method.to_sym
143
+ when :yaml
144
+ return YAML.dump(value)
145
+ when :ruby
146
+ return Marshal.dump(value)
147
+ else
148
+ return value
149
+ end
150
+ end
151
+
152
+ def unmarshal(value) # :nodoc:
153
+ return nil if value.nil?
154
+ case self.marshal_method.to_sym
155
+ when :yaml
156
+ return YAML.load(value)
157
+ when :ruby
158
+ return Marshal.load(value)
159
+ else
160
+ return value
161
+ end
162
+ end
163
+
164
+ private
165
+ def define_accessor(key)
166
+ instance_eval(%{
167
+ def #{key}
168
+ @#{key}
169
+ end
170
+ def #{key}=(x)
171
+ @#{key} = x
172
+ end
173
+ })
174
+ end
175
+
176
+ end # Base
177
+ end # Adapters
178
+ end # Cachetastic
@@ -0,0 +1,66 @@
1
+ module Cachetastic # :nodoc:
2
+ module Adapters
3
+ # An adapter to cache objects to the file system.
4
+ #
5
+ # This adapter supports the following configuration settings,
6
+ # in addition to the default settings:
7
+ #
8
+ # configatron.cachetastic.defaults.storage_path = ::File.join(FileUtils.pwd, 'cachetastic')
9
+ # configatron.cachetastic.defaults.marshal_method = :yaml
10
+ #
11
+ # The <tt>storage_path</tt> setting defines the path to where cached
12
+ # objects are written to on disk.
13
+ #
14
+ # See <tt>Cachetastic::Adapters::Base</tt> for a list of public API
15
+ # methods.
16
+ class File < Cachetastic::Adapters::Base
17
+
18
+ def initialize(klass) # :nodoc:
19
+ define_accessor(:storage_path)
20
+ self.storage_path = ::File.join(FileUtils.pwd, 'cachetastic')
21
+ super
22
+ self.marshal_method = :yaml if self.marshal_method == :none
23
+ @_file_paths = {}
24
+ end
25
+
26
+ def get(key) # :nodoc:
27
+ path = file_path(key)
28
+ val = nil
29
+ val = ::File.read(path) if ::File.exists?(path)
30
+ return val
31
+ end # get
32
+
33
+ def set(key, value, expiry_time = configatron.cachetastic.defaults.default_expiry) # :nodoc:
34
+ so = Cachetastic::Cache::StoreObject.new(key, value, expiry_time.from_now)
35
+ path = file_path(key)
36
+ ::File.open(path, 'w') {|f| f.write marshal(so)}
37
+ value
38
+ end # set
39
+
40
+ def delete(key) # :nodoc:
41
+ FileUtils.rm(file_path(key))
42
+ end # delete
43
+
44
+ def expire_all # :nodoc:
45
+ @_file_paths = {}
46
+ ::FileUtils.rm_rf(::File.join(self.storage_path, klass.name.underscore))
47
+ return nil
48
+ end # expire_all
49
+
50
+ def transform_key(key) # :nodoc:
51
+ key.to_s.hexdigest
52
+ end
53
+
54
+ def file_path(key) # :nodoc:
55
+ path = @_file_paths[key]
56
+ if path.nil?
57
+ path = ::File.join(self.storage_path, klass.name.underscore, transform_key(key).scan(/(.{1,4})/).flatten, 'cache.data')
58
+ @_file_paths[key] = path
59
+ FileUtils.mkdir_p(::File.dirname(path))
60
+ end
61
+ return path
62
+ end
63
+
64
+ end # File
65
+ end # Adapters
66
+ end # Cachetastic
@@ -0,0 +1,37 @@
1
+ module Cachetastic # :nodoc:
2
+ module Adapters
3
+ # An adapter to cache objects to memory. It is important to note
4
+ # that this cache is <b>volatile</b>. If the VM it is running in
5
+ # shuts down, everything in the cache gets vaporized.
6
+ #
7
+ # See <tt>Cachetastic::Adapters::Base</tt> for a list of public API
8
+ # methods.
9
+ class LocalMemory < Cachetastic::Adapters::Base
10
+
11
+ def initialize(klass) # :nodoc:
12
+ super
13
+ @_store = {}
14
+ end
15
+
16
+ def get(key) # :nodoc:
17
+ @_store[key]
18
+ end # get
19
+
20
+ def set(key, value, expiry_time = configatron.cachetastic.defaults.default_expiry) # :nodoc:
21
+ so = Cachetastic::Cache::StoreObject.new(key, value, expiry_time.from_now)
22
+ @_store[key] = marshal(so)
23
+ value
24
+ end # set
25
+
26
+ def delete(key) # :nodoc:
27
+ @_store.delete(key)
28
+ end # delete
29
+
30
+ def expire_all # :nodoc:
31
+ @_store = {}
32
+ return nil
33
+ end # expire_all
34
+
35
+ end # LocalMemory
36
+ end # Adapters
37
+ end # Cachetastic
@@ -0,0 +1,114 @@
1
+ module Cachetastic # :nodoc:
2
+ module Adapters
3
+ # An adapter to cache objects to the file system.
4
+ #
5
+ # This adapter supports the following configuration settings,
6
+ # in addition to the default settings:
7
+ #
8
+ # configatron.cachetastic.defaults.servers = ['127.0.0.1:11211']
9
+ # configatron.cachetastic.defaults.mc_options = {:c_threshold => 10_000,
10
+ # :compression => true,
11
+ # :debug => false,
12
+ # :readonly => false,
13
+ # :urlencode => false}
14
+ # configatron.cachetastic.delete_delay = 0
15
+ #
16
+ # The <tt>servers</tt> setting defines an <tt>Array</tt> of Mecached
17
+ # servers, represented as "<host>:<port>".
18
+ #
19
+ # The <tt>mc_options</tt> setting is a <tt>Hash</tt> of settings required
20
+ # by Memcached. See the Memcached documentation for more information on
21
+ # what the settings mean.
22
+ #
23
+ # The <tt>delete_delay</tt> setting tells Memcached how long to wait
24
+ # before it deletes the object. This is not the same as <tt>expiry_time</tt>.
25
+ # It is only used when the <tt>delete</tt> method is called.
26
+ #
27
+ # See <tt>Cachetastic::Adapters::Base</tt> for a list of public API
28
+ # methods.
29
+ class Memcached < Cachetastic::Adapters::Base
30
+
31
+ def initialize(klass) # :nodoc:
32
+ define_accessor(:servers)
33
+ define_accessor(:mc_options)
34
+ define_accessor(:delete_delay)
35
+ self.delete_delay = 0
36
+ self.servers = ['127.0.0.1:11211']
37
+ self.mc_options = {:c_threshold => 10_000,
38
+ :compression => true,
39
+ :debug => false,
40
+ :readonly => false,
41
+ :urlencode => false}
42
+ super
43
+ connection
44
+ end
45
+
46
+ def get(key) # :nodoc:
47
+ connection.get(transform_key(key), false)
48
+ end # get
49
+
50
+ def set(key, value, expiry_time = configatron.cachetastic.defaults.default_expiry) # :nodoc:
51
+ connection.set(transform_key(key), marshal(value), expiry_time, false)
52
+ end # set
53
+
54
+ def delete(key) # :nodoc:
55
+ connection.delete(transform_key(key), self.delete_delay)
56
+ end # delete
57
+
58
+ def expire_all # :nodoc:
59
+ increment_version
60
+ @_mc_connection = nil
61
+ return nil
62
+ end # expire_all
63
+
64
+ def transform_key(key) # :nodoc:
65
+ key.to_s.hexdigest
66
+ end
67
+
68
+ # Return <tt>false</tt> if the connection to Memcached is
69
+ # either <tt>nil</tt> or not active.
70
+ def valid?
71
+ return false if @_mc_connection.nil?
72
+ return false unless @_mc_connection.active?
73
+ return true
74
+ end
75
+
76
+ private
77
+ def connection
78
+ unless @_mc_connection && valid? && @_ns_version == get_version
79
+ @_mc_connection = MemCache.new(self.servers, self.mc_options.merge(:namespace => namespace))
80
+ end
81
+ @_mc_connection
82
+ end
83
+
84
+ def ns_connection
85
+ unless @_ns_connection
86
+ @_ns_connection = MemCache.new(self.servers, self.mc_options.merge(:namespace => :namespace_versions))
87
+ end
88
+ @_ns_connection
89
+ end
90
+
91
+ def increment_version
92
+ name = self.klass.name
93
+ v = get_version
94
+ ns_connection.set(name, v + 1)
95
+ end
96
+
97
+ def get_version
98
+ name = self.klass.name
99
+ v = ns_connection.get(name)
100
+ if v.nil?
101
+ ns_connection.set(name, 1)
102
+ v = 1
103
+ end
104
+ v
105
+ end
106
+
107
+ def namespace
108
+ @_ns_version = get_version
109
+ "#{self.klass.name}.#{@_ns_version}"
110
+ end
111
+
112
+ end # Memcached
113
+ end # Adapters
114
+ end # Cachetastic
@@ -0,0 +1,165 @@
1
+ module Cachetastic # :nodoc:
2
+ # When creating a new 'Cache' this class should be extended.
3
+ # Once extended you'll only need to override just the methods
4
+ # that are different for your cache.
5
+ # class MyAwesomeCache < Cachetastic::Cache
6
+ # end
7
+ #
8
+ # MyAwesomeCache.set(1, "One")
9
+ # MyAwesomeCache.get(1) # => "One"
10
+ # MyAwesomeCache.update(1, "One!!")
11
+ # MyAwesomeCache.get(1) # => "One!!"
12
+ # MyAwesomeCache.delete(1)
13
+ # MyAwesomeCache.get(1) # => nil
14
+ #
15
+ # class MyAwesomeCache < Cachetastic::Cache
16
+ # class << self
17
+ # def get(key)
18
+ # super(key) do
19
+ # set(key, key * 10)
20
+ # end
21
+ # end
22
+ # end
23
+ # end
24
+ #
25
+ # MyAwesomeCache.set(1, "One")
26
+ # MyAwesomeCache.get(1) # => "One"
27
+ # MyAwesomeCache.delete(1)
28
+ # MyAwesomeCache.get(1) # => 10
29
+ class Cache
30
+
31
+ # everything is done at the class level. there won't be any 'instances of it'
32
+ # using class << self means we don't have to prefix each method with 'self.'
33
+ class << self
34
+
35
+ # Returns an object from the cache for a given key.
36
+ # If the object comes back as nil and a block is given
37
+ # that block will be run and the results of the block
38
+ # will be returned. This can be used to JIT caches, just make
39
+ # sure in the block to call the set method because the
40
+ # results of the block are not automatically cached.
41
+ def get(key, &block)
42
+ do_with_logging(:get, key) do
43
+ val = self.adapter.get(key)
44
+ handle_store_object(key, adapter.unmarshal(val), &block)
45
+ end
46
+ end # get
47
+
48
+ # Set a particular object info the cache for the given key.
49
+ #
50
+ # An optional third parameter sets the expiry time for the object in the cache.
51
+ # If no expiry_time is passed in then the default expiry_time that has been configured
52
+ # will be used.
53
+ #
54
+ # If there is an the expiry_swing setting is configured it will be +/- to the
55
+ # expiry time.
56
+ def set(key, value, expiry_time = nil)
57
+ do_with_logging(:set, key) do
58
+ self.adapter.set(key, value, calculate_expiry_time(expiry_time))
59
+ end
60
+ end # set
61
+
62
+ # Deletes an object from the cache.
63
+ def delete(key)
64
+ do_with_logging(:delete, key) do
65
+ self.adapter.delete(key)
66
+ nil
67
+ end
68
+ end # delete
69
+
70
+ # Expires all objects for this cache.
71
+ def expire_all
72
+ do_with_logging(:expire_all, nil) do
73
+ self.adapter.expire_all
74
+ nil
75
+ end
76
+ end # expire_all
77
+
78
+ # Returns the underlying Cachetastic::Adapters::Base for this cache.
79
+ def adapter
80
+ unless @_adapter && @_adapter.valid?
81
+ @_adapter = Cachetastic::Adapters.build(cache_klass)
82
+ end
83
+ @_adapter
84
+ end # adapter
85
+
86
+ # Clears the adapter so it can be redefined. This is useful if you have
87
+ # reconfigured the cache to use a different adapater, or different settings.
88
+ def clear_adapter!
89
+ @_adapter = nil
90
+ end
91
+
92
+ def cache_klass # :nodoc:
93
+ self
94
+ end
95
+
96
+ # Returns the Cachetastic::Logger for this cache.
97
+ def logger
98
+ unless @_logger
99
+ @_logger = Cachetastic::Logger.new(adapter.logger)
100
+ end
101
+ @_logger
102
+ end
103
+
104
+ private
105
+ # If the expiry time is set to 60 minutes and the expiry_swing time is set to
106
+ # 15 minutes, this method will return a number between 45 minutes and 75 minutes.
107
+ def calculate_expiry_time(expiry_time) # :doc:
108
+ expiry_time = self.adapter.default_expiry if expiry_time.nil?
109
+ exp_swing = self.adapter.expiry_swing
110
+ if exp_swing && exp_swing != 0
111
+ swing = rand(exp_swing.to_i)
112
+ case rand(2)
113
+ when 0
114
+ expiry_time = (expiry_time.to_i + swing)
115
+ when 1
116
+ expiry_time = (expiry_time.to_i - swing)
117
+ end
118
+ end
119
+ expiry_time
120
+ end
121
+
122
+ def handle_store_object(key, val, &block)
123
+ if val.is_a?(Cachetastic::Cache::StoreObject)
124
+ if val.expired?
125
+ self.delete(key)
126
+ val = nil
127
+ else
128
+ val = val.value
129
+ end
130
+ end
131
+
132
+ if val.respond_to?(:empty?)
133
+ val = nil if val.empty?
134
+ elsif val.respond_to?(:blank?)
135
+ val = nil if val.blank?
136
+ end
137
+ return val unless val.nil?
138
+
139
+ val = yield if block_given?
140
+ return val
141
+ end
142
+
143
+ def do_with_logging(action, key)
144
+ if adapter.debug?
145
+ start_time = Time.now
146
+ logger.debug(:starting, action, cache_klass.name, key)
147
+ res = yield if block_given?
148
+ end_time = Time.now
149
+ str = ''
150
+ unless res.nil?
151
+ str = "[#{res.class.name}]"
152
+ str << "\t[Size = #{res.size}]" if res.respond_to? :size
153
+ str << "\t" << res.inspect
154
+ end
155
+ logger.debug(:finished, action, cache_klass.name, key, (end_time - start_time), str)
156
+ return res
157
+ else
158
+ return yield if block_given?
159
+ end
160
+ end
161
+
162
+ end # class << self
163
+
164
+ end # Cache
165
+ end # Cachetastic
@@ -0,0 +1,202 @@
1
+ module Cachetastic # :nodoc:
2
+ # Include this module into an Object to achieve simplistic Object level caching.
3
+ #
4
+ # Example:
5
+ # class Person
6
+ # include Cachetastic::Cacheable
7
+ #
8
+ # attr_accessor :name
9
+ #
10
+ # def cachetastic_key
11
+ # self.name
12
+ # end
13
+ #
14
+ # def always_the_same(x, y)
15
+ # cacher("always_the_same") do
16
+ # x + y
17
+ # end
18
+ # end
19
+ #
20
+ # end
21
+ module Cacheable
22
+
23
+ module ClassAndInstanceMethods
24
+ # Returns the Cachetastic::Cache object associated with the object.
25
+ # If a cache hasn't been defined the one will be created on the fly.
26
+ # The cache for the object is expected to be defined as:
27
+ # Cachetastic::Cacheable::{CLASS_NAME_HERE}Cache
28
+ #
29
+ # Examples:
30
+ # class Person
31
+ # include Cachetastic::Cacheable
32
+ # end
33
+ #
34
+ # Person.cache_class # => Cachetastic::Cacheable::PersonCache
35
+ #
36
+ # class Admin::Person
37
+ # include Cachetastic::Cacheable
38
+ # end
39
+ #
40
+ # Admin::Person.cache_class # => Cachetastic::Cacheable::Admin_PersonCache
41
+ def cache_class
42
+ n = self.class.name
43
+ n = self.name if n == "Class"
44
+ c_name = "Cachetastic::Cacheable::#{n.gsub('::', '_')}Cache"
45
+ begin
46
+ return c_name.constantize
47
+ rescue NameError => e
48
+ eval %{
49
+ class #{c_name} < Cachetastic::Cache
50
+
51
+ def self.cache_klass
52
+ #{n}
53
+ end
54
+
55
+ end
56
+ }
57
+ return c_name.constantize
58
+ end
59
+
60
+ end
61
+
62
+ # How much did I want to call this method cache?? It originally was that, but
63
+ # in Rails 2.0 they decided to use that name, so I had to rename this method.
64
+ # This method will attempt to get an object from the cache for a given key.
65
+ # If the object is nil and a block is given the block will be run, and the results
66
+ # of the block will be automatically cached.
67
+ #
68
+ # Example:
69
+ # class Person
70
+ # include Cachetastic::Cacheable
71
+ #
72
+ # def always_the_same(x,y)
73
+ # cacher("always_the_same") do
74
+ # x + y
75
+ # end
76
+ # end
77
+ # end
78
+ #
79
+ # Person.new.always_the_same(1,2) # => 3
80
+ # Person.new.always_the_same(2,2) # => 3
81
+ # Person.new.always_the_same(3,3) # => 3
82
+ # Person.cacher("always_the_same") # => 3
83
+ # Person.get_from_cache("always_the_same") # => 3
84
+ # Cachetastic::Cacheable::PersonCache.get("always_the_same") # => 3
85
+ #
86
+ # Person.cacher("say_hi") {"Hi There"} # => "Hi There"
87
+ # Person.get_from_cache("say_hi") # => "Hi There"
88
+ # Cachetastic::Cacheable::PersonCache.get("say_hi") # => "Hi There"
89
+ def cacher(key, expiry = nil)
90
+ cache_class.get(key) do
91
+ if block_given?
92
+ res = yield
93
+ cache_class.set(key, res, expiry)
94
+ end
95
+ end
96
+ end
97
+
98
+ # Expires the entire cache associated with this objects's cache.
99
+ #
100
+ # Example:
101
+ # class Person
102
+ # include Cachetastic::Cacheable
103
+ # attr_accessor :name
104
+ # def cachetastic_key
105
+ # self.name
106
+ # end
107
+ # end
108
+ #
109
+ # Person.set_into_cache(1, "one")
110
+ # Person.get_from_cache(1) # => "one"
111
+ # Person.expire_all
112
+ # Person.get_from_cache(1) # => nil
113
+ # Person.set_into_cache(1, "one")
114
+ # Person.get_from_cache(1) # => "one"
115
+ # Cachetastic::Cacheable::PersonCache.expire_all
116
+ # Person.get_from_cache(1) # => nil
117
+ def expire_all
118
+ cache_class.expire_all
119
+ end
120
+
121
+ end
122
+
123
+ # --------------------------
124
+ # Instance only methods:
125
+
126
+ # Unless the object's cachetastic_key method returns nil this method will store
127
+ # the object in the cache using the object's cachetastic_key as the key.
128
+ # You *MUST* create an instance level method called cachetastic_key and
129
+ # have it return a valid key! If you return nil from the cachetastic_key method or you will not be
130
+ # able to use the cache_self and uncache_self methods.
131
+ #
132
+ # Example:
133
+ # class Person
134
+ # include Cachetastic::Cacheable
135
+ # attr_accessor :name
136
+ # def cachetastic_key
137
+ # self.name
138
+ # end
139
+ # end
140
+ #
141
+ # Person.get_from_cache("Mark Bates") # => nil
142
+ # p = Person.new
143
+ # p.name = "Mark Bates"
144
+ # p.cache_self
145
+ # Person.get_from_cache("Mark Bates") # => "Mark Bates"
146
+ def cache_self
147
+ cache_class.set(self.cachetastic_key, self) unless self.cachetastic_key.nil?
148
+ end
149
+
150
+ # Unless the object's cachetastic_key method returns nil this method will delete
151
+ # the object in the cache using the object's cachetastic_key as the key.
152
+ # You *MUST* create an instance level method called cachetastic_key and
153
+ # have it return a valid key! If you return nil from the cachetastic_key method or you will not be
154
+ # able to use the cache_self and uncache_self methods.
155
+ #
156
+ # Example:
157
+ # class Person
158
+ # include Cachetastic::Cacheable
159
+ # attr_accessor :name
160
+ # def cachetastic_key
161
+ # self.name
162
+ # end
163
+ # end
164
+ #
165
+ # Person.get_from_cache("Mark Bates") # => nil
166
+ # p = Person.new
167
+ # p.name = "Mark Bates"
168
+ # p.cache_self
169
+ # Person.get_from_cache("Mark Bates") # => "Mark Bates"
170
+ # p.uncache_self
171
+ # Person.get_from_cache("Mark Bates") # => nil
172
+ def uncache_self
173
+ cache_class.delete(self.cachetastic_key) unless self.cachetastic_key.nil?
174
+ end
175
+
176
+ # --------------------------
177
+
178
+ def self.included(klass) # :nodoc:
179
+ klass.send(:include, ClassAndInstanceMethods)
180
+ klass.extend(ClassOnlyMethods)
181
+ klass.extend(ClassAndInstanceMethods)
182
+ end
183
+
184
+ module ClassOnlyMethods
185
+ # Returns an object from the cache for a given key.
186
+ def get_from_cache(key, &block)
187
+ cache_class.get(key, &block)
188
+ end
189
+
190
+ # Deletes an object from the cache for a given key.
191
+ def delete_from_cache(key)
192
+ cache_class.delete(key)
193
+ end
194
+
195
+ # Sets an object into the cache for a given key.
196
+ def set_into_cache(key, value, expiry = 0)
197
+ cache_class.set(key, value, expiry)
198
+ end
199
+ end # ClassMethods
200
+
201
+ end # Cacheable
202
+ end # Cachetastic
@@ -0,0 +1,8 @@
1
+ require 'digest'
2
+ class String # :nodoc:
3
+
4
+ def hexdigest # :nodoc:
5
+ Digest::SHA1.hexdigest(self)
6
+ end
7
+
8
+ end
@@ -0,0 +1,49 @@
1
+ module Cachetastic # :nodoc:
2
+ # This class handles logging for the caches and their adapters.
3
+ # This class exists simply to supply the ability to write to
4
+ # multiple loggers simultaneously from a single call. It also
5
+ # creates a standardized message to write to those loggers.
6
+ #
7
+ # It is important that any logger type of class you decide to use
8
+ # reponds to the following methods:
9
+ # fatal(message)
10
+ # error(message)
11
+ # warn(message)
12
+ # info(message)
13
+ # debug(message)
14
+ class Logger
15
+
16
+ # An <tt>Array</tt> of 'real' loggers to write to.
17
+ attr_accessor :loggers
18
+
19
+ # The <tt>initialize</tt> method takes an <tt>Array</tt>
20
+ # of your favorite logger style classes to write to.
21
+ def initialize(*loggers)
22
+ @loggers = [loggers].flatten
23
+ end
24
+
25
+ LOG_LEVELS = [:fatal, :error, :warn, :info, :debug] # :nodoc:
26
+
27
+ LOG_LEVELS.each do |level|
28
+ define_method(level) do |*args|
29
+ lm = "[CACHE] [#{level.to_s.upcase}]\t#{Time.now.strftime("%m/%d/%y %H:%M:%S")}"
30
+ exs = []
31
+ args.each do |arg|
32
+ if arg.is_a?(Exception)
33
+ exs << arg
34
+ continue
35
+ end
36
+ lm << "\t" << arg.to_s
37
+ end
38
+ exs.each do |ex|
39
+ lm << "\n#{ex.message}\n" << ex.backtrace.join("\n")
40
+ end
41
+ # puts "lm: #{lm}"
42
+ self.loggers.each do |log|
43
+ log.send(level, lm)
44
+ end
45
+ end
46
+ end
47
+
48
+ end # Logger
49
+ end # Cachetastic
@@ -0,0 +1,22 @@
1
+ module Cachetastic # :nodoc:
2
+ class Cache
3
+
4
+ class StoreObject # :nodoc:
5
+ attr_accessor :expires_at
6
+ attr_accessor :key
7
+ attr_accessor :value
8
+
9
+ def initialize(key, value, expires_at)
10
+ self.key = key
11
+ self.value = value
12
+ self.expires_at = expires_at
13
+ end
14
+
15
+ def expired?
16
+ return Time.now > self.expires_at
17
+ end
18
+
19
+ end # StoreObject
20
+
21
+ end # Cache
22
+ end # Cachetastic
@@ -0,0 +1,20 @@
1
+ require 'configatron'
2
+ require 'logger'
3
+ require 'activesupport'
4
+ require 'fileutils'
5
+ require 'memcache'
6
+
7
+ Dir.glob(File.join(File.dirname(__FILE__), 'cachetastic', '**/*.rb')).each do |f|
8
+ require File.expand_path(f)
9
+ end
10
+
11
+ configatron.cachetastic.defaults.set_default(:marshal_method, :none)
12
+ configatron.cachetastic.defaults.set_default(:expiry_swing, 0)
13
+ configatron.cachetastic.defaults.set_default(:default_expiry, 86400)
14
+ configatron.cachetastic.defaults.set_default(:debug, true)
15
+ configatron.cachetastic.defaults.set_default(:adapter, Cachetastic::Adapters::LocalMemory)
16
+ log_path = File.join(FileUtils.pwd, 'log', 'cachetastic.log')
17
+ FileUtils.mkdir_p(File.dirname(log_path))
18
+ logger = ::Logger.new(log_path)
19
+ logger.level = ::Logger::DEBUG
20
+ configatron.cachetastic.defaults.set_default(:logger, logger)
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: markbates-cachetastic
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0.20090611142033
5
+ platform: ruby
6
+ authors:
7
+ - Mark Bates
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-11 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: configatron
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: memcache-client
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.7.4
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: activesupport
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.3.2
44
+ version:
45
+ description: A very simple, yet very powerful caching framework for Ruby.
46
+ email: mark@mackframework.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - README
53
+ - LICENSE
54
+ files:
55
+ - lib/cachetastic/adapters/base.rb
56
+ - lib/cachetastic/adapters/file.rb
57
+ - lib/cachetastic/adapters/local_memory.rb
58
+ - lib/cachetastic/adapters/memcached.rb
59
+ - lib/cachetastic/cache.rb
60
+ - lib/cachetastic/cacheable.rb
61
+ - lib/cachetastic/extensions/string.rb
62
+ - lib/cachetastic/logger.rb
63
+ - lib/cachetastic/store_object.rb
64
+ - lib/cachetastic.rb
65
+ - README
66
+ - LICENSE
67
+ has_rdoc: true
68
+ homepage: http://www.metabates.com
69
+ post_install_message:
70
+ rdoc_options: []
71
+
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ version:
86
+ requirements: []
87
+
88
+ rubyforge_project: magrathea
89
+ rubygems_version: 1.2.0
90
+ signing_key:
91
+ specification_version: 2
92
+ summary: A very simple, yet very powerful caching framework for Ruby.
93
+ test_files: []
94
+