instructure-redis-store 1.0.0.1.instructure1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.travis.yml +7 -0
  2. data/CHANGELOG +311 -0
  3. data/Gemfile +34 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +239 -0
  6. data/Rakefile +60 -0
  7. data/VERSION +1 -0
  8. data/lib/action_controller/session/redis_session_store.rb +81 -0
  9. data/lib/active_support/cache/redis_store.rb +254 -0
  10. data/lib/cache/merb/redis_store.rb +79 -0
  11. data/lib/cache/sinatra/redis_store.rb +131 -0
  12. data/lib/i18n/backend/redis.rb +67 -0
  13. data/lib/rack/cache/redis_entitystore.rb +48 -0
  14. data/lib/rack/cache/redis_metastore.rb +40 -0
  15. data/lib/rack/session/merb.rb +32 -0
  16. data/lib/rack/session/redis.rb +88 -0
  17. data/lib/redis-store.rb +45 -0
  18. data/lib/redis/distributed_store.rb +39 -0
  19. data/lib/redis/factory.rb +46 -0
  20. data/lib/redis/store.rb +39 -0
  21. data/lib/redis/store/interface.rb +17 -0
  22. data/lib/redis/store/marshalling.rb +51 -0
  23. data/lib/redis/store/namespace.rb +62 -0
  24. data/lib/redis/store/ttl.rb +37 -0
  25. data/lib/redis/store/version.rb +12 -0
  26. data/spec/action_controller/session/redis_session_store_spec.rb +126 -0
  27. data/spec/active_support/cache/redis_store_spec.rb +426 -0
  28. data/spec/cache/merb/redis_store_spec.rb +143 -0
  29. data/spec/cache/sinatra/redis_store_spec.rb +192 -0
  30. data/spec/config/node-one.conf +417 -0
  31. data/spec/config/node-two.conf +417 -0
  32. data/spec/config/redis.conf +417 -0
  33. data/spec/i18n/backend/redis_spec.rb +72 -0
  34. data/spec/rack/cache/entitystore/pony.jpg +0 -0
  35. data/spec/rack/cache/entitystore/redis_spec.rb +124 -0
  36. data/spec/rack/cache/metastore/redis_spec.rb +259 -0
  37. data/spec/rack/session/redis_spec.rb +234 -0
  38. data/spec/redis/distributed_store_spec.rb +55 -0
  39. data/spec/redis/factory_spec.rb +110 -0
  40. data/spec/redis/store/interface_spec.rb +23 -0
  41. data/spec/redis/store/marshalling_spec.rb +119 -0
  42. data/spec/redis/store/namespace_spec.rb +76 -0
  43. data/spec/redis/store/version_spec.rb +7 -0
  44. data/spec/redis/store_spec.rb +13 -0
  45. data/spec/spec_helper.rb +43 -0
  46. data/tasks/redis.tasks.rb +235 -0
  47. metadata +249 -0
@@ -0,0 +1,234 @@
1
+ require 'spec_helper'
2
+
3
+ module Rack
4
+ module Session
5
+ describe "Rack::Session::Redis" do
6
+ before(:each) do
7
+ @session_key = Rack::Session::Redis::DEFAULT_OPTIONS[:key]
8
+ @session_match = /#{@session_key}=[0-9a-fA-F]+;/
9
+ @incrementor = lambda do |env|
10
+ env["rack.session"]["counter"] ||= 0
11
+ env["rack.session"]["counter"] += 1
12
+ Rack::Response.new(env["rack.session"].inspect).to_a
13
+ end
14
+ @drop_session = proc do |env|
15
+ env['rack.session.options'][:drop] = true
16
+ @incrementor.call(env)
17
+ end
18
+ @renew_session = proc do |env|
19
+ env['rack.session.options'][:renew] = true
20
+ @incrementor.call(env)
21
+ end
22
+ @defer_session = proc do |env|
23
+ env['rack.session.options'][:defer] = true
24
+ @incrementor.call(env)
25
+ end
26
+ end
27
+
28
+ it "should specify connection params" do
29
+ pool = Rack::Session::Redis.new(@incrementor, :redis_server => "redis://127.0.0.1:6380/1/theplaylist").pool
30
+ pool.should be_kind_of(::Redis::Store)
31
+ pool.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 1 with namespace theplaylist"
32
+
33
+ pool = Rack::Session::Redis.new(@incrementor, :redis_server => ["redis://127.0.0.1:6379", "redis://127.0.0.1:6380"]).pool
34
+ pool.should be_kind_of(::Redis::DistributedStore)
35
+ end
36
+
37
+ it "creates a new cookie" do
38
+ pool = Rack::Session::Redis.new(@incrementor)
39
+ res = Rack::MockRequest.new(pool).get("/")
40
+ res["Set-Cookie"].should match(/#{@session_key}=/)
41
+ res.body.should == '{"counter"=>1}'
42
+ end
43
+
44
+ it "determines session from a cookie" do
45
+ pool = Rack::Session::Redis.new(@incrementor)
46
+ req = Rack::MockRequest.new(pool)
47
+ res = req.get("/")
48
+ cookie = res["Set-Cookie"]
49
+ req.get("/", "HTTP_COOKIE" => cookie).body.should == '{"counter"=>2}'
50
+ req.get("/", "HTTP_COOKIE" => cookie).body.should == '{"counter"=>3}'
51
+ end
52
+
53
+ it "survives nonexistant cookies" do
54
+ bad_cookie = "rack.session=blarghfasel"
55
+ pool = Rack::Session::Redis.new(@incrementor)
56
+ res = Rack::MockRequest.new(pool).
57
+ get("/", "HTTP_COOKIE" => bad_cookie)
58
+ res.body.should == '{"counter"=>1}'
59
+ cookie = res["Set-Cookie"][@session_match]
60
+ cookie.should_not match(/#{bad_cookie}/)
61
+ end
62
+
63
+ it "should maintain freshness" do
64
+ pool = Rack::Session::Redis.new(@incrementor, :expire_after => 3)
65
+ res = Rack::MockRequest.new(pool).get('/')
66
+ res.body.should include('"counter"=>1')
67
+ cookie = res["Set-Cookie"]
68
+ res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
69
+ res["Set-Cookie"].should == cookie
70
+ res.body.should include('"counter"=>2')
71
+ puts 'Sleeping to expire session' if $DEBUG
72
+ sleep 4
73
+ res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
74
+ res["Set-Cookie"].should_not == cookie
75
+ res.body.should include('"counter"=>1')
76
+ end
77
+
78
+ it "deletes cookies with :drop option" do
79
+ pool = Rack::Session::Redis.new(@incrementor)
80
+ req = Rack::MockRequest.new(pool)
81
+ drop = Rack::Utils::Context.new(pool, @drop_session)
82
+ dreq = Rack::MockRequest.new(drop)
83
+
84
+ res0 = req.get("/")
85
+ session = (cookie = res0["Set-Cookie"])[@session_match]
86
+ res0.body.should == '{"counter"=>1}'
87
+
88
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
89
+ res1["Set-Cookie"][@session_match].should == session
90
+ res1.body.should == '{"counter"=>2}'
91
+
92
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
93
+ res2["Set-Cookie"].should be_nil
94
+ res2.body.should == '{"counter"=>3}'
95
+
96
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
97
+ res3["Set-Cookie"][@session_match].should_not == session
98
+ res3.body.should == '{"counter"=>1}'
99
+ end
100
+
101
+ it "provides new session id with :renew option" do
102
+ pool = Rack::Session::Redis.new(@incrementor)
103
+ req = Rack::MockRequest.new(pool)
104
+ renew = Rack::Utils::Context.new(pool, @renew_session)
105
+ rreq = Rack::MockRequest.new(renew)
106
+
107
+ res0 = req.get("/")
108
+ session = (cookie = res0["Set-Cookie"])[@session_match]
109
+ res0.body.should == '{"counter"=>1}'
110
+
111
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
112
+ res1["Set-Cookie"][@session_match].should == session
113
+ res1.body.should == '{"counter"=>2}'
114
+
115
+ res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
116
+ new_cookie = res2["Set-Cookie"]
117
+ new_session = new_cookie[@session_match]
118
+ new_session.should_not == session
119
+ res2.body.should == '{"counter"=>3}'
120
+
121
+ res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
122
+ res3["Set-Cookie"][@session_match].should == new_session
123
+ res3.body.should == '{"counter"=>4}'
124
+ end
125
+
126
+ specify "omits cookie with :defer option" do
127
+ pool = Rack::Session::Redis.new(@incrementor)
128
+ req = Rack::MockRequest.new(pool)
129
+ defer = Rack::Utils::Context.new(pool, @defer_session)
130
+ dreq = Rack::MockRequest.new(defer)
131
+
132
+ res0 = req.get("/")
133
+ session = (cookie = res0["Set-Cookie"])[@session_match]
134
+ res0.body.should == '{"counter"=>1}'
135
+
136
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
137
+ res1["Set-Cookie"][@session_match].should == session
138
+ res1.body.should == '{"counter"=>2}'
139
+
140
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
141
+ res2["Set-Cookie"].should be_nil
142
+ res2.body.should == '{"counter"=>3}'
143
+
144
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
145
+ res3["Set-Cookie"][@session_match].should == session
146
+ res3.body.should == '{"counter"=>4}'
147
+ end
148
+
149
+ # anyone know how to do this better?
150
+ specify "multithread: should cleanly merge sessions" do
151
+ next unless $DEBUG
152
+ warn 'Running multithread test for Session::Redis'
153
+ pool = Rack::Session::Redis.new(@incrementor)
154
+ req = Rack::MockRequest.new(pool)
155
+
156
+ res = req.get('/')
157
+ res.body.should == '{"counter"=>1}'
158
+ cookie = res["Set-Cookie"]
159
+ sess_id = cookie[/#{pool.key}=([^,;]+)/,1]
160
+
161
+ delta_incrementor = lambda do |env|
162
+ # emulate disconjoinment of threading
163
+ env['rack.session'] = env['rack.session'].dup
164
+ Thread.stop
165
+ env['rack.session'][(Time.now.usec*rand).to_i] = true
166
+ @incrementor.call(env)
167
+ end
168
+ tses = Rack::Utils::Context.new pool, delta_incrementor
169
+ treq = Rack::MockRequest.new(tses)
170
+ tnum = rand(7).to_i+5
171
+ r = Array.new(tnum) do
172
+ Thread.new(treq) do |run|
173
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
174
+ end
175
+ end.reverse.map{|t| t.run.join.value }
176
+ r.each do |res|
177
+ res['Set-Cookie'].should == cookie
178
+ res.body.should include('"counter"=>2')
179
+ end
180
+
181
+ session = pool.pool.get(sess_id)
182
+ session.size.should == tnum+1 # counter
183
+ session['counter'].should == 2 # meeeh
184
+
185
+ tnum = rand(7).to_i+5
186
+ r = Array.new(tnum) do |i|
187
+ delta_time = proc do |env|
188
+ env['rack.session'][i] = Time.now
189
+ Thread.stop
190
+ env['rack.session'] = env['rack.session'].dup
191
+ env['rack.session'][i] -= Time.now
192
+ @incrementor.call(env)
193
+ end
194
+ app = Rack::Utils::Context.new pool, time_delta
195
+ req = Rack::MockRequest.new app
196
+ Thread.new(req) do |run|
197
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
198
+ end
199
+ end.reverse.map{|t| t.run.join.value }
200
+ r.each do |res|
201
+ res['Set-Cookie'].should == cookie
202
+ res.body.should include('"counter"=>3')
203
+ end
204
+
205
+ session = pool.pool.get(sess_id)
206
+ session.size.should == tnum+1
207
+ session['counter'].should == 3
208
+
209
+ drop_counter = proc do |env|
210
+ env['rack.session'].del 'counter'
211
+ env['rack.session']['foo'] = 'bar'
212
+ [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
213
+ end
214
+ tses = Rack::Utils::Context.new pool, drop_counter
215
+ treq = Rack::MockRequest.new(tses)
216
+ tnum = rand(7).to_i+5
217
+ r = Array.new(tnum) do
218
+ Thread.new(treq) do |run|
219
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
220
+ end
221
+ end.reverse.map{|t| t.run.join.value }
222
+ r.each do |res|
223
+ res['Set-Cookie'].should == cookie
224
+ res.body.should include('"foo"=>"bar"')
225
+ end
226
+
227
+ session = pool.pool.get(sess_id)
228
+ session.size.should == r.size+1
229
+ session['counter'].should be_nil
230
+ session['foo'].should == 'bar'
231
+ end
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Redis::DistributedStore" do
4
+ before(:each) do
5
+ @dmr = Redis::DistributedStore.new [
6
+ {:host => "localhost", :port => "6380", :db => 0},
7
+ {:host => "localhost", :port => "6381", :db => 0}
8
+ ]
9
+ @rabbit = OpenStruct.new :name => "bunny"
10
+ @white_rabbit = OpenStruct.new :color => "white"
11
+ @dmr.set "rabbit", @rabbit
12
+ end
13
+
14
+ after(:all) do
15
+ @dmr.ring.nodes.each { |server| server.flushdb }
16
+ end
17
+
18
+ it "should accept connection params" do
19
+ dmr = Redis::DistributedStore.new [ :host => "localhost", :port => "6380", :db => "1" ]
20
+ dmr.ring.nodes.size == 1
21
+ mr = dmr.ring.nodes.first
22
+ mr.to_s.should == "Redis Client connected to localhost:6380 against DB 1"
23
+ end
24
+
25
+ it "should force reconnection" do
26
+ @dmr.nodes.each do |node|
27
+ node.should_receive(:reconnect)
28
+ end
29
+
30
+ @dmr.reconnect
31
+ end
32
+
33
+ it "should set an object" do
34
+ @dmr.set "rabbit", @white_rabbit
35
+ @dmr.get("rabbit").should == @white_rabbit
36
+ end
37
+
38
+ it "should get an object" do
39
+ @dmr.get("rabbit").should == @rabbit
40
+ end
41
+
42
+ describe "namespace" do
43
+ before :each do
44
+ @dmr = Redis::DistributedStore.new [
45
+ {:host => "localhost", :port => "6380", :db => 0},
46
+ {:host => "localhost", :port => "6381", :db => 0}
47
+ ], :namespace => "theplaylist"
48
+ end
49
+
50
+ it "should use namespaced key" do
51
+ @dmr.should_receive(:node_for).with("theplaylist:rabbit").and_return @dmr.nodes.first
52
+ @dmr.get "rabbit"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Redis::Factory" do
4
+ describe ".create" do
5
+ context "when not given any arguments" do
6
+ it "should instantiate a Redis::Store store" do
7
+ store = Redis::Factory.create
8
+ store.should be_kind_of(Redis::Store)
9
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
10
+ end
11
+ end
12
+
13
+ context "when given a Hash" do
14
+ it "should allow to specify host" do
15
+ store = Redis::Factory.create :host => "localhost"
16
+ store.to_s.should == "Redis Client connected to localhost:6379 against DB 0"
17
+ end
18
+
19
+ it "should allow to specify port" do
20
+ store = Redis::Factory.create :host => "localhost", :port => 6380
21
+ store.to_s.should == "Redis Client connected to localhost:6380 against DB 0"
22
+ end
23
+
24
+ it "should allow to specify db" do
25
+ store = Redis::Factory.create :host => "localhost", :port => 6380, :db => 13
26
+ store.to_s.should == "Redis Client connected to localhost:6380 against DB 13"
27
+ end
28
+
29
+ it "should allow to specify namespace" do
30
+ store = Redis::Factory.create :namespace => "theplaylist"
31
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0 with namespace theplaylist"
32
+ end
33
+
34
+ it "should allow to specify key_prefix as namespace" do
35
+ store = Redis::Factory.create :key_prefix => "theplaylist"
36
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0 with namespace theplaylist"
37
+ end
38
+
39
+ it "should allow to specify marshalling" do
40
+ store = Redis::Factory.create :marshalling => false
41
+ store.instance_variable_get(:@marshalling).should be_false
42
+ end
43
+
44
+ it "should allow to specify password" do
45
+ store = Redis::Factory.create :password => "secret"
46
+ store.instance_variable_get(:@client).password.should == "secret"
47
+ end
48
+
49
+ it "should instantiate a Redis::DistributedStore store" do
50
+ store = Redis::Factory.create(
51
+ {:host => "localhost", :port => 6379},
52
+ {:host => "localhost", :port => 6380}
53
+ )
54
+ store.should be_kind_of(Redis::DistributedStore)
55
+ store.nodes.map {|node| node.to_s}.should == [
56
+ "Redis Client connected to localhost:6379 against DB 0",
57
+ "Redis Client connected to localhost:6380 against DB 0",
58
+ ]
59
+ end
60
+ end
61
+
62
+ context "when given a String" do
63
+ it "should allow to specify host" do
64
+ store = Redis::Factory.create "redis://127.0.0.1"
65
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0"
66
+ end
67
+
68
+ it "should allow to specify port" do
69
+ store = Redis::Factory.create "redis://127.0.0.1:6380"
70
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 0"
71
+ end
72
+
73
+ it "should allow to specify db" do
74
+ store = Redis::Factory.create "redis://127.0.0.1:6380/13"
75
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6380 against DB 13"
76
+ end
77
+
78
+ it "should allow to specify namespace" do
79
+ store = Redis::Factory.create "redis://127.0.0.1:6379/0/theplaylist"
80
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0 with namespace theplaylist"
81
+ end
82
+
83
+ it "should allow to specify scheme" do
84
+ store = Redis::Factory.create "redis://127.0.0.1:6379/0/theplaylist"
85
+ store.to_s.should == "Redis Client connected to 127.0.0.1:6379 against DB 0 with namespace theplaylist"
86
+ end
87
+
88
+ it "should allow to specify password" do
89
+ store = Redis::Factory.create "redis://:secret@127.0.0.1:6379/0/theplaylist"
90
+ store.instance_variable_get(:@client).password.should == "secret"
91
+ end
92
+
93
+ it "should allow to specify password without scheme" do
94
+ suppress_warnings do
95
+ store = Redis::Factory.create ":secret@127.0.0.1:6379/0/theplaylist"
96
+ store.instance_variable_get(:@client).password.should == "secret"
97
+ end
98
+ end
99
+
100
+ it "should instantiate a Redis::DistributedStore store" do
101
+ store = Redis::Factory.create "redis://127.0.0.1:6379", "redis://127.0.0.1:6380"
102
+ store.should be_kind_of(Redis::DistributedStore)
103
+ store.nodes.map {|node| node.to_s}.should == [
104
+ "Redis Client connected to 127.0.0.1:6379 against DB 0",
105
+ "Redis Client connected to 127.0.0.1:6380 against DB 0",
106
+ ]
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ class InterfacedRedis < Redis
4
+ include Redis::Store::Interface
5
+ end
6
+
7
+ describe "Redis::Store::Interface" do
8
+ before :each do
9
+ @r = InterfacedRedis.new
10
+ end
11
+
12
+ it "should get an element" do
13
+ lambda { @r.get("key", :option => true) }.should_not raise_error
14
+ end
15
+
16
+ it "should set an element" do
17
+ lambda { @r.set("key", "value", :option => true) }.should_not raise_error
18
+ end
19
+
20
+ it "should setnx an element" do
21
+ lambda { @r.setnx("key", "value", :option => true) }.should_not raise_error
22
+ end
23
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Redis::Marshalling" do
4
+ before(:each) do
5
+ @store = Redis::Store.new :marshalling => true
6
+ @rabbit = OpenStruct.new :name => "bunny"
7
+ @white_rabbit = OpenStruct.new :color => "white"
8
+ @store.set "rabbit", @rabbit
9
+ @store.del "rabbit2"
10
+ end
11
+
12
+ after :each do
13
+ @store.quit
14
+ end
15
+
16
+ it "should unmarshal an object on get" do
17
+ @store.get("rabbit").should === @rabbit
18
+ end
19
+
20
+ it "should marshal object on set" do
21
+ @store.set "rabbit", @white_rabbit
22
+ @store.get("rabbit").should === @white_rabbit
23
+ end
24
+
25
+ if RUBY_VERSION.match /1\.9/
26
+ it "should not unmarshal object on get if raw option is true" do
27
+ @store.get("rabbit", :raw => true).should == "\x04\bU:\x0FOpenStruct{\x06:\tnameI\"\nbunny\x06:\x06EF"
28
+ end
29
+ else
30
+ it "should not unmarshal object on get if raw option is true" do
31
+ @store.get("rabbit", :raw => true).should == "\004\bU:\017OpenStruct{\006:\tname\"\nbunny"
32
+ end
33
+ end
34
+
35
+ it "should not marshal object on set if raw option is true" do
36
+ @store.set "rabbit", @white_rabbit, :raw => true
37
+ @store.get("rabbit", :raw => true).should == %(#<OpenStruct color="white">)
38
+ end
39
+
40
+ it "should not unmarshal object if getting an empty string" do
41
+ @store.set "empty_string", ""
42
+ lambda { @store.get("empty_string").should == "" }.should_not raise_error
43
+ end
44
+
45
+ it "should not set an object if already exist" do
46
+ @store.setnx "rabbit", @white_rabbit
47
+ @store.get("rabbit").should === @rabbit
48
+ end
49
+
50
+ it "should marshal object on set_unless_exists" do
51
+ @store.setnx "rabbit2", @white_rabbit
52
+ @store.get("rabbit2").should === @white_rabbit
53
+ end
54
+
55
+ it "should not marshal object on set_unless_exists if raw option is true" do
56
+ @store.setnx "rabbit2", @white_rabbit, :raw => true
57
+ @store.get("rabbit2", :raw => true).should == %(#<OpenStruct color="white">)
58
+ end
59
+
60
+ it "should unmarshal object(s) on multi get" do
61
+ @store.set "rabbit2", @white_rabbit
62
+ rabbit, rabbit2 = @store.mget "rabbit", "rabbit2"
63
+ rabbit.should == @rabbit
64
+ rabbit2.should == @white_rabbit
65
+ end
66
+
67
+ if RUBY_VERSION.match /1\.9/
68
+ it "should not unmarshal object(s) on multi get if raw option is true" do
69
+ @store.set "rabbit2", @white_rabbit
70
+ rabbit, rabbit2 = @store.mget "rabbit", "rabbit2", :raw => true
71
+ rabbit.should == "\x04\bU:\x0FOpenStruct{\x06:\tnameI\"\nbunny\x06:\x06EF"
72
+ rabbit2.should == "\x04\bU:\x0FOpenStruct{\x06:\ncolorI\"\nwhite\x06:\x06EF"
73
+ end
74
+ else
75
+ it "should not unmarshal object(s) on multi get if raw option is true" do
76
+ @store.set "rabbit2", @white_rabbit
77
+ rabbit, rabbit2 = @store.mget "rabbit", "rabbit2", :raw => true
78
+ rabbit.should == "\004\bU:\017OpenStruct{\006:\tname\"\nbunny"
79
+ rabbit2.should == "\004\bU:\017OpenStruct{\006:\ncolor\"\nwhite"
80
+ end
81
+ end
82
+
83
+ describe "binary safety" do
84
+ it "should marshal objects" do
85
+ utf8_key = [51339].pack("U*")
86
+ ascii_rabbit = OpenStruct.new(:name => [128].pack("C*"))
87
+
88
+ @store.set(utf8_key, ascii_rabbit)
89
+ @store.get(utf8_key).should === ascii_rabbit
90
+ end
91
+
92
+ it "should get and set raw values" do
93
+ utf8_key = [51339].pack("U*")
94
+ ascii_string = [128].pack("C*")
95
+
96
+ @store.set(utf8_key, ascii_string, :raw => true)
97
+ @store.get(utf8_key, :raw => true).bytes.to_a === ascii_string.bytes.to_a
98
+ end
99
+
100
+ it "should marshal objects on setnx" do
101
+ utf8_key = [51339].pack("U*")
102
+ ascii_rabbit = OpenStruct.new(:name => [128].pack("C*"))
103
+
104
+ @store.del(utf8_key)
105
+ @store.setnx(utf8_key, ascii_rabbit)
106
+ @store.get(utf8_key).should === ascii_rabbit
107
+ end
108
+
109
+ it "should get and set raw values on setnx" do
110
+ utf8_key = [51339].pack("U*")
111
+ ascii_string = [128].pack("C*")
112
+
113
+ @store.del(utf8_key)
114
+ @store.setnx(utf8_key, ascii_string, :raw => true)
115
+ @store.get(utf8_key, :raw => true).bytes.to_a === ascii_string.bytes.to_a
116
+ end
117
+ end if defined?(Encoding)
118
+ end
119
+