Module: InstantCache
- Defined in:
- lib/instantcache.rb,
lib/instantcache/exceptions.rb
Overview
–
Copyright © 2011 Ken Coar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
++
Defined Under Namespace
Classes: Blob, ConnexionLost, Counter, CounterIntegerOnly, Destroyed, Exception, IncompatibleType, IncompleteException, LockInconsistency, NoCache, SharedOnly
Constant Summary collapse
- Version =
The base Versionomy representation of the package version.
Versionomy.parse('0.1.1')
- VERSION =
The package version-as-a-string.
Version.to_s.freeze
- SHARED =
Label informing accessor declarations that the variable is to be shared. This changes how some things are done (like default memcached cell names).
:SHARED- PRIVATE =
Marks a variable as deliberately private and unshared. It can still be accessed through memcached calls if you know how, but it isn’t made easy – it’s supposed to be private, after all.
:PRIVATE- Setup =
String constant used to set up most of the background magic common to all of our types of cached variables.
TODO: Resolve what to do if the instance variable is zapped
One of the fortunate side-effects of all of the methods calling this first is that if the instance variable gets zapped somehow, the next access to it through of of our methods will create a new Blob or Counter object and put it into the instance variable before proceeding.
One of the UNfortunate side effects of that is that if the object that was lost was locked, it cannot be unlocked through the normal paths – only the blob object itself is supposed to lock and unlock itself. It can be worked around, but that’s for another day.
If we decide against instantiating a new object, the ConnexionLost exception is ready to be pressed into service.
<<-'EOT' # :nodoc: def _initialise_%s unless (self.instance_variables.include?('@%s') \ && @%s.kind_of?(InstantCache::Blob)) mvar = InstantCache::%s.new cellname = self.class.name + ':' cellname << self.object_id.to_s cellname << ':@%s' shared = %s owner = ObjectSpace._id2ref(self.object_id) mvar.instance_eval(%%Q{ def name return '%s' end def shared? return #{shared.inspect} end def private? return (! self.shared?) end def owner return ObjectSpace._id2ref(#{self.object_id}) end}) @%s = mvar ObjectSpace.define_finalizer(owner, Proc.new { mvar.unlock }) unless (shared) mvar.reset finaliser = Proc.new { InstantCache.cache.delete(mvar.name) InstantCache.cache.delete(mvar.send(:lock_name)) } ObjectSpace.define_finalizer(owner, finaliser) end return true end return false end private(:_initialise_%s) def %s_lock self.__send__(:_initialise_%s) return @%s.lock end def %s_unlock self.__send__(:_initialise_%s) return @%s.unlock end def %s_expiry self.__send__(:_initialise_%s) return @%s.__send__(:expiry) end def %s_expiry=(val=0) self.__send__(:_initialise_%s) return @%s.__send__(:expiry=, val) end def %s_reset self.__send__(:_initialise_%s) return @%s.__send__(:reset) end def %s_destroy! self.__send__(:_initialise_%s) return @%s.__send__(:destr- Reader =
String to define a read accessor for the given cache variable.
<<-'EOT' # :nodoc: def %s self.__send__(:_initialise_%s) return @%s.- Writer =
As above, except this is a storage (write) accessor, and is optional.
<<-'EOT' # :nodoc: def %s=(*args) self.__send__(:_initialise_%s) return @%s.set(*ar- EigenReader =
Actual code to create a read accessor for a cell.
Proc.new { |*args,&block| # :nodoc: shared = true if ([ :SHARED, :PRIVATE ].include?(args[0])) shared = (args.shift == :SHARED) end args.each do |ivar| ivar_s = ivar.to_s if (block) if (shared) name = block.call(ivar) else raise SharedOnly.new(ivar.to_sym.inspect) end end name ||= '#{cellname}' subslist = (([ ivar_s ] * 3) + [ 'Blob', ivar_s, shared.inspect, name] + ([ ivar_s ] * 20)) class_eval(Setup % subslist) class_eval(Reader % subslist[7, 3]) end nil }
- EigenAccessor =
Code for a write accessor.
Proc.new { |*args,&block| # :nodoc: shared = true if ([ :SHARED, :PRIVATE ].include?(args[0])) shared = (args.shift == :SHARED) end args.each do |ivar| ivar_s = ivar.to_s if (block) if (shared) name = block.call(ivar) else raise SharedOnly.new(ivar.to_sym.inspect) end end name ||= '#{cellname}' subslist = (([ ivar_s ] * 3) + [ 'Blob', ivar_s, shared.inspect, name] + ([ ivar_s ] * 20)) class_eval(Setup % subslist) class_eval(Reader % subslist[7, 3]) class_eval(Writer % subslist[7, 3]) end nil }
- EigenCounter =
And the code for a counter (read and write access).
Proc.new { |*args,&block| # :nodoc: shared = true if ([ :SHARED, :PRIVATE ].include?(args[0])) shared = (args.shift == :SHARED) end args.each do |ivar| ivar_s = ivar.to_s if (block) if (shared) name = block.call(ivar) else raise SharedOnly.new(ivar.to_sym.inspect) end end name ||= '#{cellname}' subslist = (([ ivar_s ] * 3) + [ 'Counter', ivar_s, shared.inspect, name] + ([ ivar_s ] * 20)) class_eval(Setup % subslist) subslist.delete_at(6) subslist.delete_at(5) subslist.delete_at(3) class_eval(Reader % subslist[7, 3]) class_eval(Writer % subslist[7, 3]) class_eval(Counter % subslist[0, 10]) end nil }
Class Attribute Summary collapse
-
.cache_object ⇒ Object
The memcached instance is currently a class-wide value.
Class Method Summary collapse
-
.cache ⇒ Object
Description Wrapper for the @cache_object class variable.
-
.enwrap(target) ⇒ Object
Description Add singleton wrapper methods to a copy of the cached value.
-
.included(base_klass) ⇒ Object
Description This class method is invoked when the module is mixed into a class; the argument is the class object involved.
-
.unwrap(target) ⇒ Object
Description Removes any singleton methods added by the #enwrap class method.
Instance Method Summary collapse
-
#name ⇒ Object
Description This should be overridden by inheritors; it’s used to form the name of the memcached cell.
Class Attribute Details
.cache_object ⇒ Object
The memcached instance is currently a class-wide value.
101 102 103 |
# File 'lib/instantcache.rb', line 101 def cache_object @cache_object end |
Class Method Details
.cache ⇒ Object
Description
Wrapper for the @cache_object class variable.
:call-seq: InstantCache.cache.method
Arguments
None.
Exceptions
InstantCache::NoCache-
Cache object unset or misset.
116 117 118 119 120 |
# File 'lib/instantcache.rb', line 116 def cache # :nodoc mco = (InstantCache.cache_object ||= nil) return mco if (mco.kind_of?(MemCache)) raise NoCache end |
.enwrap(target) ⇒ Object
Description
Add singleton wrapper methods to a copy of the cached value.
:call-seq: InstantCache.enwrap(cacheval) => nil
Arguments
- cacheval
-
Variable containing value fetched from memcache.
Exceptions
InstantCache::Destroyed-
Cache value instance has been destroyed and is no longer usable. The value in the cache is unaffected.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/instantcache.rb', line 137 def enwrap(target) # # Shamelessly cadged from delegator.rb # eigenklass = eval('class << target ; self ; end') preserved = ::Kernel.public_instance_methods(false) preserved -= [ 'to_s', 'to_a', 'inspect', '==', '=~', '===' ] swbd = {} target.instance_variable_set(:@_instantcache_method_map, swbd) target.instance_variable_set(:@_instantcache_datatype, target.class) for t in self.class.ancestors preserved |= t.public_instance_methods(false) preserved |= t.private_instance_methods(false) preserved |= t.protected_instance_methods(false) end preserved << 'singleton_method_added' target.methods.each do |method| next if (preserved.include?(method)) swbd[method] = target.method(method.to_sym) target.instance_eval(<<-EOS) def #{method}(*args, &block) iniself = self.clone result = @_instantcache_method_map['#{method}'].call(*args, &block) if (self != iniself) # # Store the changed entity # newklass = self.class iniklass = iniself.instance_variable_get(:@_instantcache_datatype) unless (self.kind_of?(iniklass)) begin raise InstantCache::IncompatibleType.new(newklass.name, iniklass.name, 'TBS') rescue InstantCache::IncompatibleType if ($@) [email protected]_if { |s| %r"\A#{Regexp.quote(__FILE__)}:\d+:in `" =~ s } end raise end end owner = self.instance_variable_get(:@_instantcache_owner) owner.set(self) end return result end EOS end return nil end |
.included(base_klass) ⇒ Object
Description
This class method is invoked when the module is mixed into a class; the argument is the class object involved.
Arguments
- base_klass
-
Class object of the class into which the module is being mixed.
Exceptions
None.
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 |
# File 'lib/instantcache.rb', line 1238 def included(base_klass) base_eigenklass = base_klass.class_eval('class << self ; self ; end') base_eigenklass.__send__(:define_method, :memcached_reader, EigenReader) base_eigenklass.__send__(:define_method, :memcached_accessor, EigenAccessor) base_eigenklass.__send__(:define_method, :memcached_counter, EigenCounter) return nil end |
.unwrap(target) ⇒ Object
Description
Removes any singleton methods added by the #enwrap class method. If the argument doesn’t have any (e.g., isn’t a value that was previously fetched), this is a no-op.
:call-seq: InstantCache.unwrap(target) => nil
Arguments
- target
-
Variable containing value previously fetched from memcache.
Exceptions
None.
206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/instantcache.rb', line 206 def unwrap(target) remap = target.instance_variable_get(:@_instantcache_method_map) return nil unless (remap.kind_of?(Hash)) remap.keys.each do |method| begin eval("class << target ; remove_method(:#{method}) ; end") rescue end end target.instance_variable_set(:@_instantcache_method_map, nil) target.instance_variable_set(:@_instantcache_owner, nil) return nil end |
Instance Method Details
#name ⇒ Object
Description
This should be overridden by inheritors; it’s used to form the name of the memcached cell.
Arguments
None.
Exceptions
None.
1267 1268 1269 |
# File 'lib/instantcache.rb', line 1267 def name return "Unnamed-#{self.class.name}-object" end |