instantcache 0.1.0a1 → 0.1.1
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/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
|