instructure-redis-store 1.0.0.1.instructure1

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.
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
+