remix-stash 0.9.0 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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