instantcache 0.1.0a1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -1
- data/README.txt +47 -10
- data/instantcache.gemspec +10 -14
- data/lib/instantcache.rb +50 -33
- data/lib/instantcache/exceptions.rb +37 -0
- metadata +13 -17
data/History.txt
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
-
=== 0.
|
1
|
+
=== 0.1.1 2011-09-09
|
2
|
+
|
3
|
+
* 1 major enhancement:
|
4
|
+
* Added check for the cache object having been set up.
|
5
|
+
- Minor enhancements
|
6
|
+
- Much documentation updating.
|
7
|
+
|
8
|
+
=== 0.1.0a1 2011-09-08
|
2
9
|
|
3
10
|
* 1 major enhancement:
|
4
11
|
* Initial release
|
data/README.txt
CHANGED
@@ -1,28 +1,65 @@
|
|
1
1
|
= InstantCache - Variables in memcached
|
2
2
|
|
3
|
-
* http://
|
3
|
+
* http://instantcache.rubyforge.org/
|
4
|
+
|
5
|
+
* github: http://github.com/RoUS/rubygem-instantcache
|
6
|
+
* rubyforge: http://rubyforge.org/scm/?group_id=10054
|
4
7
|
|
5
8
|
== DESCRIPTION:
|
6
9
|
|
7
|
-
rubygem-instantcache provides the InstantCache module, a mixin
|
10
|
+
*rubygem-instantcache* provides the InstantCache module, a mixin
|
8
11
|
which has accessors that allow you to declare 'instance variables'
|
9
12
|
that are actually stored in a memcached cluster rather than
|
10
13
|
local memory.
|
11
14
|
|
15
|
+
== GLOSSARY:
|
16
|
+
|
17
|
+
* <b><i>blob</i></b>, <b><i>blob object</i></b>, or <b><i>blob instance</i></b>: An instrumented InstantCache::Blob or InstantCache::Counter object that has been instantiated and stored in an <i>instance variable</i>.
|
18
|
+
* <b><i>cached value</i></b>: The value actually stored in the memcached cluster.
|
19
|
+
* <b><i>cache</i></b> (or <b><i>cached</i></b>) <b><i>variable</i></b>: an instance variable declared with one of the <tt>memcached_<i>xxx</i></tt> declarators and holding a <i>blob instance</i>.
|
20
|
+
* <b><i>cell</i></b> or <b><i>cache cell</i></b>: The named location in the memcached cluster where a <i>cached value</i> is stored.
|
21
|
+
* <b><i>instance variable</i></b>: This has its canonical meaning: a named cell local to an object. <i>Blob objects</i> are stored in <i>instance variables</i> to form <i>cached variables</i>.
|
22
|
+
|
12
23
|
== FEATURES:
|
13
24
|
|
14
|
-
* Provides
|
15
|
-
*
|
16
|
-
*
|
17
|
-
*
|
25
|
+
* Provides <tt>memcached_reader</tt>, <tt>memcached_accessor</tt>, and <tt>memcached_counter</tt> accessor declarations.
|
26
|
+
* Cached variables declared with <tt>memcached_counter</tt> are restricted to integer values, and are incrementable/decrementable atomically.
|
27
|
+
* Cached variables may be shared, using cells in memcache named by the user, or may be private, with opaque names unique to each instance.
|
28
|
+
* Cached variables may be locked and unlocked for atomic operations.
|
29
|
+
* If an instance variable is inadvertently overwritten, a new connexion to the cached value will be created (a new blob object will be instantiated) the next time one of InstantCache's methods is used to access it.
|
30
|
+
|
31
|
+
== CAVEATS AND WARNINGS
|
18
32
|
|
19
|
-
|
33
|
+
* InstantCache works by storing a specially-instrumented object in the actual instance variable. References from within the class to an instance variable declared as a cached variable should use the "<tt>self.</tt>" prefix (<i>e.g.</i>, <tt>self.ivar</tt>) or the blob object's methods directly (<i>e.g.</i>, <tt>@ivar.get</tt> or <tt>@ivar.set(17)</tt>).
|
34
|
+
* Direct assignment to an instance variable (<i>e.g.</i>, <tt>@ivar = 17</tt>) will lose the instrumented object and result in a new one being created.
|
20
35
|
|
21
|
-
|
36
|
+
== KNOWN PROBLEMS:
|
37
|
+
|
38
|
+
* If an cached variable is locked, and is then re-instantiated/reconnected, the variable will <b>remain</b> locked and cannot be unlocked.
|
39
|
+
* Multiple references to a cached variable can get out of sync, leading to confusion if they are updated independently. That is,
|
40
|
+
class Foo
|
41
|
+
include InstantCache
|
42
|
+
memcached_accessor(:ivar)
|
43
|
+
end
|
44
|
+
|
45
|
+
f = Foo.new
|
46
|
+
f.ivar = [1,2,3]
|
47
|
+
copy = f.ivar
|
48
|
+
f.ivar = 17
|
49
|
+
copy
|
50
|
+
=> [1,2,3]
|
51
|
+
f.ivar
|
52
|
+
=> 17
|
53
|
+
copy << 4
|
54
|
+
=> [1,2,3,4]
|
55
|
+
f.ivar
|
56
|
+
=> [1,2,3,4]
|
22
57
|
|
23
58
|
== SYNOPSIS:
|
24
59
|
|
60
|
+
require 'memcache'
|
25
61
|
require 'instantcache'
|
62
|
+
|
26
63
|
class Foo
|
27
64
|
include InstantCache
|
28
65
|
memcached_counter(:c1) # defaults to 0 if not already existant
|
@@ -56,8 +93,8 @@ local memory.
|
|
56
93
|
|
57
94
|
== REQUIREMENTS:
|
58
95
|
|
59
|
-
* Gem
|
60
|
-
* Gem
|
96
|
+
* Gem <tt>{versionomy}[https://rubygems.org/gems/versionomy]</tt>
|
97
|
+
* Gem <tt>{memcache-client}[https://rubygems.org/gems/memcache-client]</tt>
|
61
98
|
|
62
99
|
== INSTALL:
|
63
100
|
|
data/instantcache.gemspec
CHANGED
@@ -1,29 +1,25 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
|
-
s.name =
|
5
|
-
s.version = "0.1.
|
4
|
+
s.name = "instantcache"
|
5
|
+
s.version = "0.1.1"
|
6
6
|
|
7
|
-
s.required_rubygems_version = Gem::Requirement.new("
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.1") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Ken Coar"]
|
9
|
-
s.date =
|
10
|
-
s.description =
|
11
|
-
which has accessors that allow you to declare 'instance variables'
|
12
|
-
that are actually stored in a memcached cluster rather than
|
13
|
-
local memory.}
|
9
|
+
s.date = "2011-09-09"
|
10
|
+
s.description = "rubygem-instantcache provides the InstantCache module, a mixin\nwhich has accessors that allow you to declare 'instance variables'\nthat are actually stored in a memcached cluster rather than\nlocal memory."
|
14
11
|
s.email = ["coar@rubyforge.org"]
|
15
12
|
s.extra_rdoc_files = ["History.txt", "LICENCE.txt", "Manifest.txt", "README.txt"]
|
16
|
-
s.files = ["History.txt", "LICENCE.txt", "Manifest.txt", "README.txt", "Rakefile", "instantcache.gemspec", "lib/instantcache.rb", "lib/instantcache/exceptions.rb", "script/console", "script/destroy", "script/generate", "test/test_helper.rb", "test/test_instantcache.rb", "test/test_sharing_complex.rb", "test/test_sharing_simple.rb"]
|
17
|
-
s.homepage =
|
13
|
+
s.files = ["History.txt", "LICENCE.txt", "Manifest.txt", "README.txt", "Rakefile", "instantcache.gemspec", "lib/instantcache.rb", "lib/instantcache/exceptions.rb", "script/console", "script/destroy", "script/generate", "test/test_helper.rb", "test/test_instantcache.rb", "test/test_sharing_complex.rb", "test/test_sharing_simple.rb", ".gemtest"]
|
14
|
+
s.homepage = "http://instantcache.rubyforge.org/"
|
18
15
|
s.rdoc_options = ["--main", "README.txt"]
|
19
16
|
s.require_paths = ["lib"]
|
20
|
-
s.rubyforge_project =
|
21
|
-
s.rubygems_version =
|
22
|
-
s.summary =
|
17
|
+
s.rubyforge_project = "instantcache"
|
18
|
+
s.rubygems_version = "1.8.10"
|
19
|
+
s.summary = "rubygem-instantcache provides the InstantCache module, a mixin which has accessors that allow you to declare 'instance variables' that are actually stored in a memcached cluster rather than local memory."
|
23
20
|
s.test_files = ["test/test_instantcache.rb", "test/test_helper.rb", "test/test_sharing_simple.rb", "test/test_sharing_complex.rb"]
|
24
21
|
|
25
22
|
if s.respond_to? :specification_version then
|
26
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
27
23
|
s.specification_version = 3
|
28
24
|
|
29
25
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
data/lib/instantcache.rb
CHANGED
@@ -73,7 +73,7 @@ module InstantCache
|
|
73
73
|
#
|
74
74
|
# The base Versionomy representation of the package version.
|
75
75
|
#
|
76
|
-
Version = Versionomy.parse('0.1.
|
76
|
+
Version = Versionomy.parse('0.1.1')
|
77
77
|
|
78
78
|
#
|
79
79
|
# The package version-as-a-string.
|
@@ -102,6 +102,27 @@ module InstantCache
|
|
102
102
|
|
103
103
|
#
|
104
104
|
# === Description
|
105
|
+
# Wrapper for the @cache_object class variable.
|
106
|
+
#
|
107
|
+
# :call-seq:
|
108
|
+
# InstantCache.cache.<i>method</i>
|
109
|
+
#
|
110
|
+
# === Arguments
|
111
|
+
# <i>None.</i>
|
112
|
+
#
|
113
|
+
# === Exceptions
|
114
|
+
# [<tt>InstantCache::NoCache</tt>] Cache object unset or misset.
|
115
|
+
#
|
116
|
+
def cache # :nodoc
|
117
|
+
mco = (InstantCache.cache_object ||= nil)
|
118
|
+
return mco if (mco.kind_of?(MemCache))
|
119
|
+
raise NoCache
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# === Description
|
124
|
+
# Add singleton wrapper methods to a copy of the cached value.
|
125
|
+
#
|
105
126
|
# :call-seq:
|
106
127
|
# InstantCache.enwrap(<i>cacheval</i>) => nil
|
107
128
|
#
|
@@ -451,7 +472,7 @@ module InstantCache
|
|
451
472
|
#
|
452
473
|
# TODO: This can mess with subclassing; need better way to find the cache
|
453
474
|
#
|
454
|
-
InstantCache.
|
475
|
+
InstantCache.cache.set(self.name, rval, self.expiry, self.rawmode)
|
455
476
|
return rval
|
456
477
|
end
|
457
478
|
|
@@ -570,7 +591,7 @@ module InstantCache
|
|
570
591
|
#
|
571
592
|
# TODO: Another instance of poor-man's-cache-location; see #reset
|
572
593
|
#
|
573
|
-
sts = InstantCache.
|
594
|
+
sts = InstantCache.cache.add(self.lock_name, @identity)
|
574
595
|
@locked_by_us = (sts.to_s =~ %r!^STORED!) ? true : false
|
575
596
|
return @locked_by_us
|
576
597
|
end
|
@@ -610,7 +631,7 @@ module InstantCache
|
|
610
631
|
#
|
611
632
|
# TODO: Another instance of poor-man's-cache-location; see #reset
|
612
633
|
#
|
613
|
-
sts = InstantCache.
|
634
|
+
sts = InstantCache.cache.get(self.lock_name) || false
|
614
635
|
if (@locked_by_us && (sts != @identity))
|
615
636
|
#
|
616
637
|
# If we show we have the lock, but the lock cell doesn't exist
|
@@ -626,7 +647,7 @@ module InstantCache
|
|
626
647
|
#
|
627
648
|
# TODO: Another instance of poor-man's-cache-location; see #reset
|
628
649
|
#
|
629
|
-
sts = InstantCache.
|
650
|
+
sts = InstantCache.cache.delete(self.lock_name)
|
630
651
|
if (sts !~ %r!^DELETED!)
|
631
652
|
e = LockInconsistency.new(self.lock_name,
|
632
653
|
'/DELETED/',
|
@@ -656,7 +677,7 @@ module InstantCache
|
|
656
677
|
#
|
657
678
|
# TODO: Another instance of poor-man's-cache-location; see #reset
|
658
679
|
#
|
659
|
-
value = InstantCache.
|
680
|
+
value = InstantCache.cache.get(self.name, self.rawmode)
|
660
681
|
begin
|
661
682
|
#
|
662
683
|
# Make a copy of the thing we fetched out of the cache.
|
@@ -717,8 +738,8 @@ module InstantCache
|
|
717
738
|
#
|
718
739
|
# We use both memcache#add and memcache#set for completeness.
|
719
740
|
#
|
720
|
-
InstantCache.
|
721
|
-
InstantCache.
|
741
|
+
InstantCache.cache.add(self.name, val, self.expiry, self.rawmode)
|
742
|
+
InstantCache.cache.set(self.name, val, self.expiry, self.rawmode)
|
722
743
|
#
|
723
744
|
# Return the value as fetched through our accessor; this ensures
|
724
745
|
# the proper annotation.
|
@@ -746,27 +767,6 @@ module InstantCache
|
|
746
767
|
return self.get.__send__(:to_s, *args)
|
747
768
|
end
|
748
769
|
|
749
|
-
=begin
|
750
|
-
#
|
751
|
-
# === Description
|
752
|
-
# :call-seq:
|
753
|
-
# === Arguments
|
754
|
-
# === Exceptions
|
755
|
-
# <i>None.</i>
|
756
|
-
#
|
757
|
-
def method_missing(meth, *args)
|
758
|
-
methsym = meth.to_sym
|
759
|
-
return self.__send__(methsym, *args) if (self.respond_to?(methsym))
|
760
|
-
curval = self.get
|
761
|
-
lastval = curval.clone
|
762
|
-
opresult = curval.__send__(methsym, *args)
|
763
|
-
if (curval != lastval)
|
764
|
-
self.set(curval)
|
765
|
-
end
|
766
|
-
return opresult
|
767
|
-
end
|
768
|
-
=end
|
769
|
-
|
770
770
|
end # End of class Blob
|
771
771
|
|
772
772
|
#
|
@@ -965,7 +965,7 @@ module InstantCache
|
|
965
965
|
#
|
966
966
|
# TODO: Another instance of poor-man's-cache-location; see #reset
|
967
967
|
#
|
968
|
-
return InstantCache.
|
968
|
+
return InstantCache.cache.incr(self.name, amt)
|
969
969
|
end
|
970
970
|
alias_method(:incr, :increment)
|
971
971
|
|
@@ -999,7 +999,7 @@ module InstantCache
|
|
999
999
|
#
|
1000
1000
|
# TODO: Another instance of poor-man's-cache-location; see #reset
|
1001
1001
|
#
|
1002
|
-
return InstantCache.
|
1002
|
+
return InstantCache.cache.decr(self.name, amt)
|
1003
1003
|
end
|
1004
1004
|
alias_method(:decr, :decrement)
|
1005
1005
|
|
@@ -1018,6 +1018,23 @@ module InstantCache
|
|
1018
1018
|
# String constant used to set up most of the background magic
|
1019
1019
|
# common to all of our types of cached variables.
|
1020
1020
|
#
|
1021
|
+
# TODO: Resolve what to do if the instance variable is zapped
|
1022
|
+
#
|
1023
|
+
# One of the fortunate side-effects of all of the methods calling
|
1024
|
+
# this first is that if the instance variable gets zapped somehow,
|
1025
|
+
# the next access to it through of of our methods will create a
|
1026
|
+
# new Blob or Counter object and put it into the instance variable
|
1027
|
+
# before proceeding.
|
1028
|
+
#
|
1029
|
+
# One of the UNfortunate side effects of *that* is that if the
|
1030
|
+
# object that was lost was locked, it cannot be unlocked through
|
1031
|
+
# the normal paths -- only the blob object itself is supposed to
|
1032
|
+
# lock and unlock itself. It can be worked around, but that's for
|
1033
|
+
# another day.
|
1034
|
+
#
|
1035
|
+
# If we decide against instantiating a new object, the ConnexionLost
|
1036
|
+
# exception is ready to be pressed into service.
|
1037
|
+
#
|
1021
1038
|
Setup =<<-'EOT' # :nodoc:
|
1022
1039
|
def _initialise_%s
|
1023
1040
|
unless (self.instance_variables.include?('@%s') \
|
@@ -1046,8 +1063,8 @@ module InstantCache
|
|
1046
1063
|
unless (shared)
|
1047
1064
|
mvar.reset
|
1048
1065
|
finaliser = Proc.new {
|
1049
|
-
InstantCache.
|
1050
|
-
InstantCache.
|
1066
|
+
InstantCache.cache.delete(mvar.name)
|
1067
|
+
InstantCache.cache.delete(mvar.send(:lock_name))
|
1051
1068
|
}
|
1052
1069
|
ObjectSpace.define_finalizer(owner, finaliser)
|
1053
1070
|
end
|
@@ -137,6 +137,24 @@ module InstantCache
|
|
137
137
|
]
|
138
138
|
end # End of class IncompleteException
|
139
139
|
|
140
|
+
#
|
141
|
+
# Obviously this package is dependent upon memcache being available
|
142
|
+
# and configured. Gritch otherwise.
|
143
|
+
#
|
144
|
+
class NoCache < InstantCache::Exception
|
145
|
+
#
|
146
|
+
# ==== <tt>raise NoCache</tt>
|
147
|
+
# => InstantCache::NoCache: InstantCache.cache_object not initialised
|
148
|
+
#
|
149
|
+
# ==== <tt>raise Destroyed.new('<i>arg</i>')</tt>
|
150
|
+
# => InstantCache::NoCache: InstantCache.cache_object not initialised for "arg"
|
151
|
+
#
|
152
|
+
MessageFormat = [
|
153
|
+
'InstantCache.cache_object not initialised',
|
154
|
+
'InstantCache.cache_object not initialised for "%s"',
|
155
|
+
]
|
156
|
+
end # End of class NoCache
|
157
|
+
|
140
158
|
#
|
141
159
|
# Once a variable has been hit by the 'destroy!' method, it
|
142
160
|
# becomes inaccessible to the instance.
|
@@ -155,6 +173,25 @@ module InstantCache
|
|
155
173
|
]
|
156
174
|
end # End of class Destroyed
|
157
175
|
|
176
|
+
#
|
177
|
+
# If the instance variable is somehow overwritten, the methods that
|
178
|
+
# try to treat it as a Blob or Counter will fail. This exception
|
179
|
+
# tries to make that failure less mysterious.
|
180
|
+
#
|
181
|
+
class ConnexionLost < InstantCache::Exception
|
182
|
+
#
|
183
|
+
# ==== <tt>raise ConnexionLost</tt>
|
184
|
+
# => InstantCache::ConnexionLost: instance variable no longer connected to cache
|
185
|
+
#
|
186
|
+
# ==== <tt>raise Destroyed.new('<i>arg</i>')</tt>
|
187
|
+
# => InstantCache::ConnexionLost: instance variable "@arg" no longer connected to cache
|
188
|
+
#
|
189
|
+
MessageFormat = [
|
190
|
+
'instance variable no longer connected to cache',
|
191
|
+
'instance variable "@%s" no longer connected to cache',
|
192
|
+
]
|
193
|
+
end # End of class ConnexionLost
|
194
|
+
|
158
195
|
#
|
159
196
|
# Our record of the locked status of a cell differs from the information
|
160
197
|
# stored in the memcache about it. This Is Not Good.
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: instantcache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 25
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ken Coar
|
@@ -15,8 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-09-
|
19
|
-
default_executable:
|
18
|
+
date: 2011-09-09 00:00:00 Z
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
22
21
|
name: versionomy
|
@@ -66,7 +65,7 @@ dependencies:
|
|
66
65
|
type: :development
|
67
66
|
version_requirements: *id003
|
68
67
|
description: |-
|
69
|
-
rubygem-instantcache provides the InstantCache module, a mixin
|
68
|
+
*rubygem-instantcache* provides the InstantCache module, a mixin
|
70
69
|
which has accessors that allow you to declare 'instance variables'
|
71
70
|
that are actually stored in a memcached cluster rather than
|
72
71
|
local memory.
|
@@ -98,8 +97,7 @@ files:
|
|
98
97
|
- test/test_sharing_complex.rb
|
99
98
|
- test/test_sharing_simple.rb
|
100
99
|
- .gemtest
|
101
|
-
|
102
|
-
homepage: http://github.com/RoUS/rubygem-instantcache
|
100
|
+
homepage: http://instantcache.rubyforge.org/
|
103
101
|
licenses: []
|
104
102
|
|
105
103
|
post_install_message:
|
@@ -120,21 +118,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
119
|
none: false
|
122
120
|
requirements:
|
123
|
-
- - "
|
121
|
+
- - ">="
|
124
122
|
- !ruby/object:Gem::Version
|
125
|
-
hash:
|
123
|
+
hash: 3
|
126
124
|
segments:
|
127
|
-
-
|
128
|
-
|
129
|
-
- 1
|
130
|
-
version: 1.3.1
|
125
|
+
- 0
|
126
|
+
version: "0"
|
131
127
|
requirements: []
|
132
128
|
|
133
129
|
rubyforge_project: instantcache
|
134
|
-
rubygems_version: 1.
|
130
|
+
rubygems_version: 1.8.10
|
135
131
|
signing_key:
|
136
132
|
specification_version: 3
|
137
|
-
summary: rubygem-instantcache provides the InstantCache module, a mixin which has accessors that allow you to declare 'instance variables' that are actually stored in a memcached cluster rather than local memory.
|
133
|
+
summary: "*rubygem-instantcache* provides the InstantCache module, a mixin which has accessors that allow you to declare 'instance variables' that are actually stored in a memcached cluster rather than local memory."
|
138
134
|
test_files:
|
139
135
|
- test/test_instantcache.rb
|
140
136
|
- test/test_helper.rb
|