adapter 0.5

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ Copyright (c) 2011, Zynga Inc.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
7
+ * Neither the name of Zynga Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
8
+
9
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,59 @@
1
+ = Adapter
2
+
3
+ A simple interface to anything.
4
+
5
+ == Defining an Adapter
6
+
7
+ An adapter requires 4 methods to work: read, write, delete and clear.
8
+
9
+ Adapter.define(:memory) do
10
+ def read(key)
11
+ decode(client[key_for(key)])
12
+ end
13
+
14
+ def write(key, value)
15
+ client[key_for(key)] = encode(value)
16
+ end
17
+
18
+ def delete(key)
19
+ client.delete(key_for(key))
20
+ end
21
+
22
+ def clear
23
+ client.clear
24
+ end
25
+ end
26
+
27
+ Note: in order for things to be most flexible, always wrap key with key_for(key) which will ensure that pretty much anything can be a key. Also, by default encode and decode are included and they Marshal.dump and Marshal.load. Feel free to override these if you prefer JSON serialization or something else.
28
+
29
+ Once you have defined an adapter, you can get a class of that adapter like this:
30
+
31
+ Adapter[:memory]
32
+
33
+ This returns a dynamic class with your adapting methods included and an initialize method that takes a client. This means you can get an instance of the adapter by using new and passing the client (in this instance a boring hash):
34
+
35
+ adapter = Adapter[:memory].new({}) # sets {} to client
36
+ adapter.write('foo', 'bar')
37
+ adapter.read('foo') # 'bar'
38
+ adapter.delete('foo')
39
+ adapter.fetch('foo', 'bar') # returns bar and sets foo to bar
40
+
41
+ get and [] are aliased to read. set and []= are aliased to write.
42
+
43
+ Note: You can also optionally provide a lock method. See the memcached and redis adapters for more on locking.
44
+
45
+ == Extending Adapters
46
+
47
+ Adapters can be defined using a block, a module, or a module and a block. This allows very flexibly overriding specific things in an adapter to create a new adapter, without having to redo all of the work. See examples/overriding_serialization.rb for an example of this.
48
+
49
+ == Note on Patches/Pull Requests
50
+
51
+ * Fork the project.
52
+ * Make your feature addition or bug fix.
53
+ * Add tests for it. This is important so we don't break it in a future version unintentionally.
54
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine, but bump version in a commit by itself so we can ignore when we pull)
55
+ * Send us a pull request. Bonus points for topic branches.
56
+
57
+ == Copyright
58
+
59
+ Copyright (c) 2010 New Toy. See LICENSE for details.
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
@@ -0,0 +1,58 @@
1
+ require 'adapter/asserts'
2
+ require 'adapter/defaults'
3
+ require 'adapter/exceptions'
4
+
5
+ module Adapter
6
+ extend Asserts
7
+ include Defaults
8
+
9
+ def self.definitions
10
+ @definitions ||= {}
11
+ end
12
+
13
+ def self.define(name, mod=nil, &block)
14
+ definition_module = Module.new
15
+ definition_module.send(:include, Defaults)
16
+ definition_module.send(:include, mod) unless mod.nil?
17
+ definition_module.send(:include, Module.new(&block)) if block_given?
18
+ assert_valid_module(definition_module)
19
+ adapters.delete(name.to_sym)
20
+ definitions[name.to_sym] = definition_module
21
+ end
22
+
23
+ def self.adapters
24
+ @adapters ||= {}
25
+ end
26
+
27
+ def self.[](name)
28
+ assert_valid_adapter(name)
29
+ adapters[name.to_sym] ||= get_adapter_instance(name)
30
+ end
31
+
32
+ private
33
+ def self.get_adapter_instance(name)
34
+ Class.new do
35
+ attr_reader :client, :options
36
+
37
+ def initialize(client, options={})
38
+ @client = client
39
+ @options = options
40
+ end
41
+
42
+ include Adapter.definitions[name.to_sym]
43
+
44
+ alias get read
45
+ alias set write
46
+
47
+ alias [] read
48
+ alias []= write
49
+
50
+ def eql?(other)
51
+ self.class.eql?(other.class) && client == other.client
52
+ end
53
+ alias == eql?
54
+
55
+ class_eval "def name; :#{name} end"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,21 @@
1
+ module Adapter
2
+ module Asserts
3
+ RequiredMethods = [:read, :write, :delete, :clear]
4
+
5
+ def assert_valid_module(mod)
6
+ assert_methods_defined(mod)
7
+ end
8
+
9
+ def assert_valid_adapter(name)
10
+ raise Undefined.new(name) unless definitions.key?(name.to_sym)
11
+ end
12
+
13
+ def assert_methods_defined(mod)
14
+ missing_methods = []
15
+ RequiredMethods.each do |meth|
16
+ missing_methods << meth unless mod.method_defined?(meth)
17
+ end
18
+ raise IncompleteAPI.new(missing_methods) unless missing_methods.empty?
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ module Adapter
2
+ module Defaults
3
+ def fetch(key, value=nil, &block)
4
+ read(key) || begin
5
+ value = yield(key) if value.nil? && block_given?
6
+ write(key, value)
7
+ value
8
+ end
9
+ end
10
+
11
+ def key?(key)
12
+ !read(key).nil?
13
+ end
14
+
15
+ def key_for(key)
16
+ if key.is_a?(String)
17
+ key
18
+ elsif key.is_a?(Symbol)
19
+ key.to_s
20
+ else
21
+ Marshal.dump(key)
22
+ end
23
+ end
24
+
25
+ def encode(value)
26
+ Marshal.dump(value)
27
+ end
28
+
29
+ def decode(value)
30
+ value && Marshal.load(value)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ module Adapter
2
+ class Error < StandardError; end
3
+
4
+ class Undefined < Error; end
5
+
6
+ class IncompleteAPI < Error
7
+ def initialize(methods)
8
+ super("Missing methods needed to complete API (#{methods.join(', ')})")
9
+ end
10
+ end
11
+
12
+ class LockTimeout < Error
13
+ def initialize(key, timeout)
14
+ super("Timeout on lock #{key} exceeded #{timeout} sec")
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ require 'adapter'
2
+
3
+ module Adapter
4
+ module Memory
5
+ def read(key)
6
+ decode(client[key_for(key)])
7
+ end
8
+
9
+ def write(key, value)
10
+ client[key_for(key)] = encode(value)
11
+ end
12
+
13
+ def delete(key)
14
+ read(key).tap { client.delete(key_for(key)) }
15
+ end
16
+
17
+ def clear
18
+ client.clear
19
+ end
20
+ end
21
+ end
22
+
23
+ Adapter.define(:memory, Adapter::Memory)
@@ -0,0 +1,3 @@
1
+ module Adapter
2
+ VERSION = '0.5'
3
+ end
@@ -0,0 +1,40 @@
1
+ require 'helper'
2
+
3
+ describe Adapter::Defaults do
4
+ let(:mod) do
5
+ Module.new.tap do |m|
6
+ m.extend(Adapter::Defaults)
7
+ end
8
+ end
9
+
10
+ describe "#key_for" do
11
+ it "returns value for string" do
12
+ mod.key_for('foo').should == 'foo'
13
+ end
14
+
15
+ it "returns string for symbol" do
16
+ mod.key_for(:foo).should == 'foo'
17
+ end
18
+
19
+ it "marshals anything not a string or symbol" do
20
+ mod.key_for({'testing' => 'this'}).should == %Q(\004\b{\006\"\ftesting\"\tthis)
21
+ end
22
+ end
23
+
24
+ describe "#encode" do
25
+ it "marshals value" do
26
+ mod.encode(nil).should == "\004\b0"
27
+ mod.encode({'testing' => 'this'}).should == %Q(\004\b{\006\"\ftesting\"\tthis)
28
+ end
29
+ end
30
+
31
+ describe "#decode" do
32
+ it "returns nil if nil" do
33
+ mod.decode(nil).should be_nil
34
+ end
35
+
36
+ it "returns marshal load if not nil" do
37
+ mod.decode(%Q(\004\b{\006\"\ftesting\"\tthis)).should == {'testing' => 'this'}
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ require 'helper'
2
+ require 'adapter/memory'
3
+
4
+ describe "Memory adapter" do
5
+ before do
6
+ @client = {}
7
+ @adapter = Adapter[:memory].new(@client)
8
+ @adapter.clear
9
+ end
10
+
11
+ let(:adapter) { @adapter }
12
+ let(:client) { @client }
13
+
14
+ it_should_behave_like 'a marshaled adapter'
15
+ end
@@ -0,0 +1,302 @@
1
+ require 'helper'
2
+
3
+ describe Adapter do
4
+ describe ".definitions" do
5
+ it "defaults to empty hash" do
6
+ Adapter.instance_variable_set("@definitions", nil)
7
+ Adapter.definitions.should == {}
8
+ end
9
+ end
10
+
11
+ describe ".define" do
12
+ describe "with string name" do
13
+ it "symbolizes string adapter names" do
14
+ Adapter.define('memory', valid_module)
15
+ Adapter.definitions.keys.should include(:memory)
16
+ end
17
+ end
18
+
19
+ describe "with module" do
20
+ before do
21
+ @mod = valid_module
22
+ Adapter.define(:memory, mod)
23
+ end
24
+ let(:mod) { @mod }
25
+
26
+ it "adds adapter to definitions" do
27
+ Adapter.definitions.should have_key(:memory)
28
+ Adapter.definitions[:memory].should be_instance_of(Module)
29
+ end
30
+
31
+ it "includes the defaults" do
32
+ Class.new do
33
+ include Adapter.definitions[:memory]
34
+ end.tap do |klass|
35
+ klass.new.respond_to?(:fetch).should be_true
36
+ klass.new.respond_to?(:key_for, true).should be_true
37
+ klass.new.respond_to?(:encode, true).should be_true
38
+ klass.new.respond_to?(:decode, true).should be_true
39
+ end
40
+ end
41
+
42
+ [:read, :write, :delete, :clear].each do |method_name|
43
+ it "raises error if #{method_name} is not defined in module" do
44
+ mod.send(:undef_method, method_name)
45
+
46
+ lambda do
47
+ Adapter.define(:memory, mod)
48
+ end.should raise_error(Adapter::IncompleteAPI, "Missing methods needed to complete API (#{method_name})")
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "with block" do
54
+ before do
55
+ Adapter.define(:memory) do
56
+ def read(key)
57
+ client[key]
58
+ end
59
+
60
+ def write(key, value)
61
+ client[key] = value
62
+ end
63
+
64
+ def delete(key)
65
+ client.delete(key)
66
+ end
67
+
68
+ def clear
69
+ client.clear
70
+ end
71
+ end
72
+ end
73
+
74
+ it "adds adapter to definitions" do
75
+ Adapter.definitions.should have_key(:memory)
76
+ end
77
+
78
+ it "modularizes the block" do
79
+ Adapter.definitions[:memory].should be_instance_of(Module)
80
+ end
81
+ end
82
+
83
+ describe "with module and block" do
84
+ before do
85
+ Adapter.define(:memory, valid_module) do
86
+ def clear
87
+ raise 'Not Implemented'
88
+ end
89
+ end
90
+ end
91
+
92
+ it "includes block after module" do
93
+ adapter = Adapter[:memory].new({})
94
+ adapter.write('foo', 'bar')
95
+ adapter.read('foo').should == 'bar'
96
+ lambda do
97
+ adapter.clear
98
+ end.should raise_error('Not Implemented')
99
+ end
100
+ end
101
+ end
102
+
103
+ describe "Overriding encode/decode" do
104
+ before do
105
+ Adapter.define(:memory_json, valid_module) do
106
+ def encode(value)
107
+ ActiveSupport::JSON.encode(value)
108
+ end
109
+
110
+ def decode(value)
111
+ ActiveSupport::JSON.decode(value)
112
+ end
113
+ end
114
+ end
115
+ let(:adapter) { Adapter[:memory_json].new({}) }
116
+
117
+ it "encodes correctly" do
118
+ hash = {'foo' => 'bar'}
119
+ adapter.write('foo', hash)
120
+ adapter.client['foo'].should == ActiveSupport::JSON.encode(hash)
121
+ end
122
+
123
+ it "decodes correctly" do
124
+ hash = {'foo' => 'bar'}
125
+ adapter.client['foo'] = ActiveSupport::JSON.encode(hash)
126
+ adapter.read('foo').should == hash
127
+ end
128
+ end
129
+
130
+ describe "Redefining an adapter" do
131
+ before do
132
+ Adapter.define(:memory, valid_module)
133
+ Adapter.define(:hash, valid_module)
134
+ @memoized_memory = Adapter[:memory]
135
+ @memoized_hash = Adapter[:hash]
136
+ Adapter.define(:memory, valid_module)
137
+ end
138
+
139
+ it "unmemoizes adapter by name" do
140
+ Adapter[:memory].should_not equal(@memoized_memory)
141
+ end
142
+
143
+ it "does not unmemoize other adapters" do
144
+ Adapter[:hash].should equal(@memoized_hash)
145
+ end
146
+ end
147
+
148
+ describe ".[]" do
149
+ before do
150
+ Adapter.define(:memory, valid_module)
151
+ end
152
+
153
+ it "returns adapter instance" do
154
+ adapter = Adapter[:memory].new({})
155
+ adapter.write('foo', 'bar')
156
+ adapter.read('foo').should == 'bar'
157
+ adapter.delete('foo')
158
+ adapter.read('foo').should be_nil
159
+ adapter.write('foo', 'bar')
160
+ adapter.clear
161
+ adapter.read('foo').should be_nil
162
+ end
163
+
164
+ it "raises error for undefined adapter" do
165
+ lambda do
166
+ Adapter[:non_existant]
167
+ end.should raise_error(Adapter::Undefined)
168
+ end
169
+
170
+ it "memoizes adapter by name" do
171
+ Adapter[:memory].should equal(Adapter[:memory])
172
+ end
173
+ end
174
+
175
+ describe "Adapter" do
176
+ before do
177
+ Adapter.define(:memory, valid_module)
178
+ @client = {}
179
+ @adapter = Adapter[:memory].new(@client)
180
+ end
181
+ let(:adapter) { @adapter }
182
+
183
+ describe "#initialize" do
184
+ it "works with options" do
185
+ Adapter.define(:memory, valid_module)
186
+ adapter = Adapter[:memory].new({}, :namespace => 'foo')
187
+ adapter.options[:namespace].should == 'foo'
188
+ end
189
+ end
190
+
191
+ describe "#name" do
192
+ it "returns adapter name" do
193
+ adapter.name.should be(:memory)
194
+ end
195
+ end
196
+
197
+ describe "#fetch" do
198
+ it "returns value if found" do
199
+ adapter.write('foo', 'bar')
200
+ adapter.fetch('foo', 'baz').should == 'bar'
201
+ end
202
+
203
+ it "returns value if not found" do
204
+ adapter.fetch('foo', 'baz').should == 'baz'
205
+ end
206
+
207
+ describe "with block" do
208
+ it "returns value if key found" do
209
+ adapter.write('foo', 'bar')
210
+ adapter.should_not_receive(:write)
211
+ adapter.fetch('foo') do
212
+ 'baz'
213
+ end.should == 'bar'
214
+ end
215
+
216
+ it "returns result of block if key not found and writes result to key" do
217
+ adapter.fetch('foo') do
218
+ 'baz'
219
+ end.should == 'baz'
220
+ adapter.fetch('foo').should == 'baz'
221
+ end
222
+
223
+ it "yields key to block" do
224
+ adapter.fetch('foo') do |key|
225
+ key
226
+ end.should == 'foo'
227
+ end
228
+ end
229
+ end
230
+
231
+ describe "#key?" do
232
+ it "returns true if key is set" do
233
+ adapter.write('foo', 'bar')
234
+ adapter.key?('foo').should be_true
235
+ end
236
+
237
+ it "returns false if key is not set" do
238
+ adapter.key?('foo').should be_false
239
+ end
240
+ end
241
+
242
+ describe "#[]" do
243
+ it "is aliased to read" do
244
+ adapter.write('foo', 'bar')
245
+ adapter['foo'].should == 'bar'
246
+ end
247
+ end
248
+
249
+ describe "#get" do
250
+ it "is aliased to read" do
251
+ adapter.write('foo', 'bar')
252
+ adapter.get('foo').should == 'bar'
253
+ end
254
+ end
255
+
256
+ describe "#[]=" do
257
+ it "is aliased to write" do
258
+ adapter.read('foo').should be_nil
259
+ adapter['foo'] = 'bar'
260
+ adapter.read('foo').should == 'bar'
261
+ end
262
+ end
263
+
264
+ describe "#[]=" do
265
+ it "is aliased to write" do
266
+ adapter.read('foo').should be_nil
267
+ adapter.set('foo', 'bar')
268
+ adapter.read('foo').should == 'bar'
269
+ end
270
+ end
271
+
272
+ describe "#eql?" do
273
+ it "returns true if same name and client" do
274
+ adapter.should eql(Adapter[:memory].new({}))
275
+ end
276
+
277
+ it "returns false if different name" do
278
+ Adapter.define(:hash, valid_module)
279
+ adapter.should_not eql(Adapter[:hash].new({}))
280
+ end
281
+
282
+ it "returns false if different client" do
283
+ adapter.should_not eql(Adapter[:memory].new(Object.new))
284
+ end
285
+ end
286
+
287
+ describe "#==" do
288
+ it "returns true if same name and client" do
289
+ adapter.should == Adapter[:memory].new({})
290
+ end
291
+
292
+ it "returns false if different name" do
293
+ Adapter.define(:hash, valid_module)
294
+ adapter.should_not == Adapter[:hash].new({})
295
+ end
296
+
297
+ it "returns false if different client" do
298
+ adapter.should_not == Adapter[:memory].new(Object.new)
299
+ end
300
+ end
301
+ end
302
+ end
@@ -0,0 +1,28 @@
1
+ $:.unshift(File.expand_path('../../lib', __FILE__))
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+
6
+ Bundler.require(:default, :development)
7
+
8
+ require 'pathname'
9
+ require 'logger'
10
+
11
+ root_path = Pathname(__FILE__).dirname.join('..').expand_path
12
+ lib_path = root_path.join('lib')
13
+ log_path = root_path.join('log')
14
+ log_path.mkpath
15
+
16
+ require 'support/an_adapter'
17
+ require 'support/marshal_adapter'
18
+ require 'support/json_adapter'
19
+ require 'support/module_helpers'
20
+
21
+ logger = Logger.new(log_path.join('test.log'))
22
+ LogBuddy.init(:logger => logger)
23
+
24
+ Rspec.configure do |c|
25
+ c.include(ModuleHelpers)
26
+ end
27
+
28
+ AdapterTestTypes = {"String" => ["key", "key2"]}
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --timeout
3
+ 20
@@ -0,0 +1,162 @@
1
+ shared_examples_for "an adapter" do
2
+ it "can read the client" do
3
+ adapter.client.should == client
4
+ end
5
+
6
+ AdapterTestTypes.each do |type, (key, key2)|
7
+ it "reads from keys that are #{type}s like a Hash" do
8
+ handle_failed_connections do
9
+ adapter[key].should == nil
10
+ end
11
+ end
12
+
13
+ it "writes String values to keys that are #{type}s like a Hash" do
14
+ handle_failed_connections do
15
+ adapter[key] = "value"
16
+ adapter[key].should == "value"
17
+ end
18
+ end
19
+
20
+ it "guarantees that a different String value is retrieved from the #{type} key" do
21
+ handle_failed_connections do
22
+ value = "value"
23
+ adapter[key] = value
24
+ adapter[key].should_not be_equal(value)
25
+ end
26
+ end
27
+
28
+ it "guarantees that a different Object value is retrieved from the #{type} key" do
29
+ handle_failed_connections do
30
+ value = {:foo => :bar}
31
+ adapter[key] = value
32
+ adapter[key].should_not be_equal(:foo => :bar)
33
+ end
34
+ end
35
+
36
+ it "returns false from key? if a #{type} key is not available" do
37
+ handle_failed_connections do
38
+ adapter.key?(key).should be_false
39
+ end
40
+ end
41
+
42
+ it "returns true from key? if a #{type} key is available" do
43
+ handle_failed_connections do
44
+ adapter[key] = "value"
45
+ adapter.key?(key).should be_true
46
+ end
47
+ end
48
+
49
+ it "removes and return an element with a #{type} key from the backing store via delete if it exists" do
50
+ handle_failed_connections do
51
+ adapter[key] = "value"
52
+ adapter.delete(key).should == "value"
53
+ adapter.key?(key).should be_false
54
+ end
55
+ end
56
+
57
+ it "returns nil from delete if an element for a #{type} key does not exist" do
58
+ handle_failed_connections do
59
+ adapter.delete(key).should be_nil
60
+ end
61
+ end
62
+
63
+ it "removes all #{type} keys from the store with clear" do
64
+ handle_failed_connections do
65
+ adapter[key] = "value"
66
+ adapter[key2] = "value2"
67
+ adapter.clear
68
+ adapter.key?(key).should_not be_true
69
+ adapter.key?(key2).should_not be_true
70
+ end
71
+ end
72
+
73
+ it "fetches a #{type} key with a default value with fetch, if the key is not available" do
74
+ handle_failed_connections do
75
+ adapter.fetch(key, "value").should == "value"
76
+ end
77
+ end
78
+
79
+ it "fetches a #{type} key with a block with fetch, if the key is not available" do
80
+ handle_failed_connections do
81
+ adapter.fetch(key) { |k| "value" }.should == "value"
82
+ end
83
+ end
84
+
85
+ it "does not run the block if the #{type} key is available" do
86
+ handle_failed_connections do
87
+ adapter[key] = "value"
88
+ unaltered = "unaltered"
89
+ adapter.fetch(key) { unaltered = "altered" }
90
+ unaltered.should == "unaltered"
91
+ end
92
+ end
93
+
94
+ it "fetches a #{type} key with a default value with fetch, if the key is available" do
95
+ handle_failed_connections do
96
+ adapter[key] = "value2"
97
+ adapter.fetch(key, "value").should == "value2"
98
+ end
99
+ end
100
+
101
+ it "writes #{key} values with #write" do
102
+ handle_failed_connections do
103
+ adapter.write(key, "value")
104
+ adapter[key].should == "value"
105
+ end
106
+ end
107
+ end
108
+
109
+ it "refuses to #[] from keys that cannot be marshalled" do
110
+ handle_failed_connections do
111
+ lambda do
112
+ adapter[Struct.new(:foo).new(:bar)]
113
+ end.should raise_error(TypeError)
114
+ end
115
+ end
116
+
117
+ it "refuses to fetch from keys that cannot be marshalled" do
118
+ handle_failed_connections do
119
+ lambda do
120
+ adapter.fetch(Struct.new(:foo).new(:bar), true)
121
+ end.should raise_error(TypeError)
122
+ end
123
+ end
124
+
125
+ it "refuses to #[]= to keys that cannot be marshalled" do
126
+ handle_failed_connections do
127
+ lambda do
128
+ adapter[Struct.new(:foo).new(:bar)] = "value"
129
+ end.should raise_error(TypeError)
130
+ end
131
+ end
132
+
133
+ it "refuses to store to keys that cannot be marshalled" do
134
+ handle_failed_connections do
135
+ lambda do
136
+ adapter.write Struct.new(:foo).new(:bar), "value"
137
+ end.should raise_error(TypeError)
138
+ end
139
+ end
140
+
141
+ it "refuses to check for key? if the key cannot be marshalled" do
142
+ handle_failed_connections do
143
+ lambda do
144
+ adapter.key? Struct.new(:foo).new(:bar)
145
+ end.should raise_error(TypeError)
146
+ end
147
+ end
148
+
149
+ it "refuses to delete a key if the key cannot be marshalled" do
150
+ handle_failed_connections do
151
+ lambda do
152
+ adapter.delete Struct.new(:foo).new(:bar)
153
+ end.should raise_error(TypeError)
154
+ end
155
+ end
156
+
157
+ it "specifies that it is writable via frozen?" do
158
+ handle_failed_connections do
159
+ adapter.should_not be_frozen
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,10 @@
1
+ shared_examples_for "a json adapter" do
2
+ it_should_behave_like 'an adapter'
3
+
4
+ AdapterTestTypes.each do |type, (key, key2)|
5
+ it "writes Object values to keys that are #{type}s like a Hash" do
6
+ adapter[key] = {:foo => :bar}
7
+ adapter[key].should == {'foo' => 'bar'}
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ shared_examples_for "a marshaled adapter" do
2
+ it_should_behave_like 'an adapter'
3
+
4
+ AdapterTestTypes.each do |type, (key, key2)|
5
+ it "writes Object values to keys that are #{type}s like a Hash" do
6
+ handle_failed_connections do
7
+ adapter[key] = {:foo => :bar}
8
+ adapter[key].should == {:foo => :bar}
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ module ModuleHelpers
2
+ def valid_module
3
+ Module.new do
4
+ def read(key)
5
+ decode(client[key_for(key)])
6
+ end
7
+
8
+ def write(key, value)
9
+ client[key_for(key)] = encode(value)
10
+ end
11
+
12
+ def delete(key)
13
+ client.delete(key_for(key))
14
+ end
15
+
16
+ def clear
17
+ client.clear
18
+ end
19
+ end
20
+ end
21
+
22
+ def handle_failed_connections
23
+ yield
24
+ rescue => e
25
+ puts e.inspect
26
+ puts e.message unless e.message.nil?
27
+ pending
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: adapter
3
+ version: !ruby/object:Gem::Version
4
+ hash: 1
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 5
9
+ version: "0.5"
10
+ platform: ruby
11
+ authors:
12
+ - John Nunemaker
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-25 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email:
23
+ - nunemaker@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - lib/adapter/asserts.rb
32
+ - lib/adapter/defaults.rb
33
+ - lib/adapter/exceptions.rb
34
+ - lib/adapter/memory.rb
35
+ - lib/adapter/version.rb
36
+ - lib/adapter.rb
37
+ - spec/adapter/defaults_spec.rb
38
+ - spec/adapter/memory_spec.rb
39
+ - spec/adapter_spec.rb
40
+ - spec/helper.rb
41
+ - spec/spec.opts
42
+ - spec/support/an_adapter.rb
43
+ - spec/support/json_adapter.rb
44
+ - spec/support/marshal_adapter.rb
45
+ - spec/support/module_helpers.rb
46
+ - LICENSE
47
+ - Rakefile
48
+ - README.rdoc
49
+ has_rdoc: true
50
+ homepage: http://github.com/newtoy/adapter
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.3.7
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: A simple interface to anything
83
+ test_files: []
84
+