Module: InstantCache
- Defined in:
- lib/instantcache.rb,
lib/instantcache/exceptions.rb
Overview
–
Copyright
++
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.
" def _initialise_%s\nunless (self.instance_variables.include?('@%s') \\\n && @%s.kind_of?(InstantCache::Blob))\n mvar = InstantCache::%s.new\n cellname = self.class.name + ':'\n cellname << self.object_id.to_s\n cellname << ':@%s'\n shared = %s\n owner = ObjectSpace._id2ref(self.object_id)\n mvar.instance_eval(%%Q{\n def name\n return '%s'\n end\n def shared?\n return \#{shared.inspect}\n end\n def private?\n return (! self.shared?)\n end\n def owner\n return ObjectSpace._id2ref(\#{self.object_id})\n end})\n @%s = mvar\n ObjectSpace.define_finalizer(owner, Proc.new { mvar.unlock })\n unless (shared)\n mvar.reset\n finaliser = Proc.new {\n InstantCache.cache.delete(mvar.name)\n InstantCache.cache.delete(mvar.send(:lock_name))\n }\n ObjectSpace.define_finalizer(owner, finaliser)\n end\n return true\nend\nreturn false\n end\n private(:_initialise_%s)\n def %s_lock\nself.__send__(:_initialise_%s)\nreturn @%s.lock\n end\n def %s_unlock\nself.__send__(:_initialise_%s)\nreturn @%s.unlock\n end\n def %s_expiry\nself.__send__(:_initialise_%s)\nreturn @%s.__send__(:expiry)\n end\n def %s_expiry=(val=0)\nself.__send__(:_initialise_%s)\nreturn @%s.__send__(:expiry=, val)\n end\n def %s_reset\nself.__send__(:_initialise_%s)\nreturn @%s.__send__(:reset)\n end\n def %s_destroy!\nself.__send__(:_initialise_%s)\nreturn @%s.__send__(:destr\n" # :nodoc:
- Reader =
String to define a read accessor for the given cache variable.
" def %s\nself.__send__(:_initialise_%s)\nreturn @%s.\n" # :nodoc:
- Writer =
As above, except this is a storage (write) accessor, and is optional.
" def %s=(*args)\nself.__send__(:_initialise_%s)\nreturn @%s.set(*ar\n" # :nodoc:
- 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(" def \#{method}(*args, &block)\n iniself = self.clone\n result = @_instantcache_method_map['\#{method}'].call(*args, &block)\n if (self != iniself)\n #\n # Store the changed entity\n #\n newklass = self.class\n iniklass = iniself.instance_variable_get(:@_instantcache_datatype)\n unless (self.kind_of?(iniklass))\n begin\n raise InstantCache::IncompatibleType.new(newklass.name,\n iniklass.name,\n 'TBS')\n rescue InstantCache::IncompatibleType\n if ($@)\n [email protected]_if { |s|\n %r\"\\A\#{Regexp.quote(__FILE__)}:\\d+:in `\" =~ s\n }\n end\n raise\n end\n end\n owner = self.instance_variable_get(:@_instantcache_owner)\n owner.set(self)\n end\n return result\n end\n EOS\n end\n return nil\nend\n") |
.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 |