remix-stash 0.9.0 → 0.9.6

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.
@@ -0,0 +1,256 @@
1
+ module Remix; end
2
+
3
+ class Remix::Stash
4
+ require 'remix/stash/extension'
5
+ require 'remix/stash/cluster'
6
+ require 'remix/stash/protocol'
7
+
8
+ attr_accessor :name
9
+
10
+ @@instances = {}
11
+ @@clusters = {:default => Cluster.new(%w[localhost:11211])}
12
+
13
+ def self.cluster(name)
14
+ @@clusters[name]
15
+ end
16
+
17
+ def self.cycle_action
18
+ @@instances.each {|name, stash|
19
+ stash.cycle if stash.default[:coherency] == :action}
20
+ end
21
+
22
+ def self.define_cluster(clusters)
23
+ clusters.each do |k,v|
24
+ @@clusters[k] = Cluster === v ? v : Cluster.new(v)
25
+ end
26
+ end
27
+
28
+ def self.new(name)
29
+ @@instances[name] ||= super
30
+ end
31
+
32
+ def initialize(name)
33
+ @name = name
34
+ @scope = nil
35
+ if name == :root
36
+ @local = @opts = {:coherency => :action, :ttl => 0, :cluster => :default}
37
+ else
38
+ @local = {}
39
+ @opts = stash.default.dup
40
+ end
41
+ end
42
+
43
+ def add(*keys)
44
+ opts = default_opts(keys)
45
+ value = keys.pop
46
+ key = canonical_key(keys, opts)
47
+ cluster(opts).select(key) {|io| Protocol.add(io, key, value, opts[:ttl])}
48
+ end
49
+
50
+ def clear(*keys)
51
+ opts = default_opts(keys)
52
+ if keys.empty?
53
+ if @name == :root
54
+ cluster(opts).each {|io| Protocol.flush(io)}
55
+ else
56
+ vk = vector_key
57
+ cluster(opts).select(vk) {|io|
58
+ unless Protocol.incr(io, vk, 1)
59
+ Protocol.add(io, vk, '0')
60
+ Protocol.incr(io, vk, 1)
61
+ end
62
+ }
63
+ end
64
+ cycle
65
+ else
66
+ # remove a specific key
67
+ key = canonical_key(keys, opts)
68
+ cluster(opts).select(key) {|io| Protocol.delete(io, key)}
69
+ end
70
+ end
71
+
72
+ def clear_scope
73
+ @scope = nil
74
+ end
75
+
76
+ def cycle
77
+ @vector = nil
78
+ end
79
+
80
+ def decr(*keys)
81
+ opts = default_opts(keys)
82
+ step = keys.pop
83
+ key = canonical_key(keys, opts)
84
+ cluster(opts).select(key) {|io| Protocol.decr(io, key, step)}
85
+ end
86
+
87
+ def default(opts = nil)
88
+ if opts
89
+ if opts.has_key? :coherency
90
+ [:dynamic, :action, :transaction].include?(opts[:coherency]) or raise ArgumentError,
91
+ "Invalid coherency setting used (#{opts[:coherency].inspect})"
92
+ end
93
+ @local.merge!(opts)
94
+ @opts.merge!(opts)
95
+ if @name == :root
96
+ @@instances.each do |name, stash|
97
+ stash.update_options unless name == :root
98
+ end
99
+ end
100
+ end
101
+ @opts
102
+ end
103
+
104
+ def delete(*keys)
105
+ opts = default_opts(keys)
106
+ key = canonical_key(keys, opts)
107
+ cluster(opts).select(key) {|io| Protocol.delete(io, key)}
108
+ end
109
+
110
+ def eval(*keys)
111
+ opts = default_opts(keys)
112
+ key = canonical_key(keys, opts)
113
+ cluster(opts).select(key) {|io|
114
+ value = Protocol.get(io, key)
115
+ if value
116
+ Marshal.load(value)
117
+ else
118
+ value = yield(*keys)
119
+ Protocol.set(io, key, dump_value(value), opts[:ttl])
120
+ value
121
+ end
122
+ }
123
+ end
124
+
125
+ def gate(*keys)
126
+ opts = default_opts(keys)
127
+ key = canonical_key(keys, opts)
128
+ cluster(opts).select(key) {|io|
129
+ if Protocol.get(io, key)
130
+ yield(*keys)
131
+ true
132
+ else
133
+ false
134
+ end
135
+ }
136
+ end
137
+
138
+ def get(*keys)
139
+ opts = default_opts(keys)
140
+ key = canonical_key(keys, opts)
141
+ cluster(opts).select(key) {|io| load_value(Protocol.get(io, key))}
142
+ end
143
+ alias [] get
144
+
145
+ def incr(*keys)
146
+ opts = default_opts(keys)
147
+ step = keys.pop
148
+ key = canonical_key(keys, opts)
149
+ cluster(opts).select(key) {|io| Protocol.incr(io, key, step)}
150
+ end
151
+
152
+ def read(*keys)
153
+ opts = default_opts(keys)
154
+ key = canonical_key(keys, opts)
155
+ cluster(opts).select(key) {|io| Protocol.get(io, key)}
156
+ end
157
+
158
+ def release
159
+ @@instances.delete(@name)
160
+ end
161
+
162
+ def scope(&b)
163
+ @scope = b
164
+ self
165
+ end
166
+
167
+ def set(*keys)
168
+ opts = default_opts(keys)
169
+ value = keys.pop
170
+ key = canonical_key(keys, opts)
171
+ cluster(opts).select(key) {|io| Protocol.set(io, key, dump_value(value), opts[:ttl])}
172
+ end
173
+ alias []= set
174
+
175
+ def transaction
176
+ yield self
177
+ ensure
178
+ cycle
179
+ end
180
+
181
+ def write(*keys)
182
+ opts = default_opts(keys)
183
+ value = keys.pop
184
+ key = canonical_key(keys, opts)
185
+ cluster(opts).select(key) {|io| Protocol.set(io, key, value, opts[:ttl])}
186
+ end
187
+
188
+ protected
189
+
190
+ def update_options
191
+ @opts = stash.default.merge(@local)
192
+ end
193
+
194
+ private
195
+
196
+ KEY_SEPARATOR = '/'
197
+ def canonical_key(keys, opts)
198
+ v = vector(opts)
199
+ if @scope
200
+ "#{implicit_scope}#{keys.join(KEY_SEPARATOR)}#{vector(opts)}"
201
+ elsif v
202
+ keys.join(KEY_SEPARATOR) << v
203
+ else
204
+ keys.join(KEY_SEPARATOR)
205
+ end
206
+ end
207
+
208
+ def cluster(opts = {})
209
+ @@clusters[opts[:cluster]]
210
+ end
211
+
212
+ def default_opts(params)
213
+ params.last.is_a?(Hash) ? default.merge(params.pop) : default
214
+ end
215
+
216
+ def dump_value(value)
217
+ Marshal.dump(value)
218
+ end
219
+
220
+ def implicit_scope
221
+ @scope.call(self) if @scope
222
+ end
223
+
224
+ def load_value(data)
225
+ Marshal.load(data) if data
226
+ rescue TypeError, ArgumentError
227
+ logger = default_opts[:logger]
228
+ logger && logger.error("[stash] Unable to load marshal stream: #{data.inspect}")
229
+ nil
230
+ end
231
+
232
+ def vector(opts)
233
+ return if @name == :root
234
+ return @vector if @vector && opts[:coherency] != :dynamic
235
+ vk = vector_key
236
+ cluster(opts).select(vk) do |io|
237
+ @vector = Protocol.get(io, vk)
238
+ unless @vector
239
+ Protocol.add(io, vk, '0')
240
+ @vector = Protocol.get(io, vk)
241
+ end
242
+ @vector = "@#@name:#@vector"
243
+ end
244
+ end
245
+
246
+ def vector_key
247
+ "#@name#{implicit_scope}_vector"
248
+ end
249
+
250
+ class ProtocolError < RuntimeError; end
251
+ class ClusterError < RuntimeError; end
252
+
253
+ end
254
+
255
+ class Object; include Remix::Stash::Extension end
256
+ module Remix; extend Remix::Stash::Extension end
@@ -0,0 +1,67 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{remix-stash}
8
+ s.version = "0.9.6"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brian Mitchell"]
12
+ s.date = %q{2009-09-22}
13
+ s.email = %q{binary42@gmail.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "LICENSE",
21
+ "README.markdown",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "benchmarks/get_set.rb",
25
+ "benchmarks/payload.rb",
26
+ "examples/eval.rb",
27
+ "examples/gate.rb",
28
+ "examples/getset.rb",
29
+ "examples/scope.rb",
30
+ "examples/stash.rb",
31
+ "harness.rb",
32
+ "lib/remix/stash.rb",
33
+ "lib/remix/stash/cluster.rb",
34
+ "lib/remix/stash/extension.rb",
35
+ "lib/remix/stash/protocol.rb",
36
+ "remix-stash.gemspec",
37
+ "spec/extension_spec.rb",
38
+ "spec/spec.rb",
39
+ "spec/stash_spec.rb"
40
+ ]
41
+ s.has_rdoc = true
42
+ s.homepage = %q{http://github.com/binary42/remix-stash}
43
+ s.rdoc_options = ["--charset=UTF-8"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = %q{1.3.1}
46
+ s.summary = %q{Remix your memcache}
47
+ s.test_files = [
48
+ "spec/extension_spec.rb",
49
+ "spec/spec.rb",
50
+ "spec/stash_spec.rb",
51
+ "examples/eval.rb",
52
+ "examples/gate.rb",
53
+ "examples/getset.rb",
54
+ "examples/scope.rb",
55
+ "examples/stash.rb"
56
+ ]
57
+
58
+ if s.respond_to? :specification_version then
59
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
60
+ s.specification_version = 2
61
+
62
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
63
+ else
64
+ end
65
+ else
66
+ end
67
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/spec'
2
+
3
+ class ExtensionSpec < Spec
4
+
5
+ context '#stash' do
6
+
7
+ should 'return a stash object with the correct name' do
8
+ s = stash(:a)
9
+ assert_instance_of Stash, s
10
+ assert_equal :a, s.name
11
+ end
12
+
13
+ should 'return the same object when given the same name' do
14
+ assert_equal stash(:b), stash(:b)
15
+ assert_not_equal stash(:a), stash(:b)
16
+ end
17
+
18
+ should 'allow access to a default root stash' do
19
+ assert_equal stash, stash(:root)
20
+ assert_equal :root, stash.name
21
+ end
22
+
23
+ end
24
+
25
+ context 'modules' do
26
+
27
+ should 'be mixed into Object' do
28
+ assert Object.respond_to?(:stash)
29
+ end
30
+
31
+ should 'be mixed into Remix' do
32
+ assert Remix.respond_to?(:stash)
33
+ end
34
+
35
+ end
36
+
37
+ end
data/spec/spec.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH << File.dirname(__FILE__) + '/../lib'
6
+ require 'remix/stash'
7
+
8
+ Spec = Test::Unit::TestCase
9
+
10
+ include Remix