trick_serial 0.2.0

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,76 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'trick_serial'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ =begin
11
+ if $DEBUG || ENV['DEBUG']
12
+ require 'rubygems'
13
+ gem 'ruby-debug'
14
+ require 'ruby-debug'
15
+ end
16
+ =end
17
+
18
+ RSpec.configure do |config|
19
+
20
+ end
21
+
22
+ ######################################################################
23
+ # Common test helpers
24
+ #
25
+
26
+ module TrickSerial
27
+ class Serializer
28
+ module Test
29
+ class A
30
+ attr_accessor :x, :y
31
+ end
32
+ class B
33
+ attr_accessor :x, :y
34
+ end
35
+ class PhonyActiveRecord
36
+ attr_accessor :id
37
+
38
+ @@find_map = { }
39
+ def self.find_map
40
+ @@find_map
41
+ end
42
+
43
+ @@id ||= 0
44
+ def initialize
45
+ @id = (@@id += 1)
46
+ end
47
+ def self.find(id)
48
+ obj = new
49
+ obj.id = id
50
+ # $stderr.puts " #{self}.find(#{id.inspect}) => #{obj.inspect}"
51
+ # (@@find_map[id] ||= 0) += 1 # Shouldn't Ruby parse this?
52
+ @@find_map[id] ||= 0
53
+ @@find_map[id] += 1
54
+ obj
55
+ end
56
+
57
+ def to_s
58
+ super.sub!(/>$/, " id=#{@id.inspect}>")
59
+ end
60
+ end
61
+
62
+ class Model < PhonyActiveRecord
63
+ attr_accessor :state
64
+ def initialize x = nil
65
+ super()
66
+ @state = x
67
+ end
68
+
69
+ def to_s
70
+ super.sub!(/>$/, " @state=#{@state.inspect}>")
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
@@ -0,0 +1,169 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ # Test Target:
4
+ require 'trick_serial/serializer/cgi_session'
5
+
6
+ # Test Helpers:
7
+ require 'cgi'
8
+ require 'cgi/session'
9
+ require 'cgi/session/pstore'
10
+ require 'fileutils'
11
+ require 'stringio'
12
+
13
+ $have_mem_cache_store = false
14
+ begin
15
+ require 'action_controller/session/mem_cache_store'
16
+ require 'trick_serial/serializer/cgi_session'
17
+ $have_mem_cache_store = true
18
+ rescue Exception => err
19
+ $stderr.puts "#{__FILE__}: #{err.inspect}\n #{err.backtrace * "\n "}"
20
+ end
21
+
22
+
23
+ TrickSerial::Serializer::CgiSession.activate!
24
+
25
+ describe "TrickSerial::Serializer::Cgi::Session" do
26
+
27
+ before(:each) do
28
+ @s = TrickSerial::Serializer.new
29
+ @s.class_option_map = {
30
+ TrickSerial::Serializer::Test::PhonyActiveRecord =>
31
+ { :proxy_class => TrickSerial::Serializer::ActiveRecordProxy, },
32
+ }
33
+
34
+ TrickSerial::Serializer::Test::PhonyActiveRecord.find_map.clear
35
+
36
+ @m = TrickSerial::Serializer::Test::Model.new(123)
37
+ @m_unsaved = TrickSerial::Serializer::Test::Model.new(:unsaved)
38
+ @m_unsaved.id = nil
39
+
40
+ TrickSerial::Serializer.default = @s
41
+ end
42
+
43
+ after(:each) do
44
+ TrickSerial::Serializer.default = nil
45
+ end
46
+
47
+
48
+ attr_accessor :cgi, :session
49
+
50
+ def create_session! options
51
+ @cgi = CGI.new("html4")
52
+ def @cgi.env_table; @env_table ||= { }; end
53
+ def @cgi.stdinput; @stdinput ||= StringIO.new; end
54
+
55
+ @cgi.stdinput << @header if @header
56
+
57
+ yield if block_given?
58
+
59
+ options = {
60
+ 'TrickSerial.database_manager' => @store,
61
+ 'database_manager' => TrickSerial::Serializer::CgiSession::Store,
62
+ 'tmpdir' => @tmpdir,
63
+ 'session_id' => 'abc123',
64
+ 'session_key' => '_test',
65
+ 'session_expires' => Time.now + 30 + 30,
66
+ 'prefix' => '_test',
67
+ }.merge(options)
68
+ @session = CGI::Session.new(cgi, options)
69
+ end
70
+
71
+ def test_store! store, options = { }
72
+ @tmpdir = "/tmp/#{File.basename(__FILE__)}-#{$$}"
73
+ FileUtils.mkdir_p(@tmpdir)
74
+
75
+ @store = store
76
+
77
+ create_session! options
78
+
79
+ @session['a'] = 1
80
+ @session['b'] = :b
81
+ @session['c'] = "aksjdfsd"
82
+ @session['m'] = @m
83
+ @session['m2'] = @m
84
+ @session['m_unsaved'] = @m_unsaved
85
+ @session['ary'] = [ 0, 1, @m, 3, 4, @m ]
86
+
87
+ @session.update
88
+ @session.close
89
+
90
+ create_session! options
91
+
92
+ fm = TrickSerial::Serializer::Test::PhonyActiveRecord.find_map
93
+ fm.clear
94
+ fm[@m.id].should == nil
95
+
96
+ @session['a'].should == 1
97
+ @session['b'].should == :b
98
+ @session['c'].should == "aksjdfsd"
99
+
100
+ fm[@m.id].should == nil
101
+
102
+ @session['m'].class.should == @m.class
103
+ @session['m'].object_id.should_not == @m.object_id
104
+ @session['m'].id.should == @m.id
105
+
106
+ fm[@m.id].should == 1
107
+
108
+ @session['m2'].object_id.should == @session['m'].object_id
109
+
110
+ fm[@m.id].should == 1
111
+
112
+ @session['ary'].class.should == Array
113
+
114
+ fm[@m.id].should == 1
115
+ fm.keys.size.should == 1
116
+
117
+ ensure
118
+ File.unlink(*Dir["#{@tmpdir}/*"])
119
+ FileUtils.rmdir(@tmpdir)
120
+ end
121
+
122
+ # FileStore can only handle String => String data.
123
+ it "should handle CGI::Session::FileStore" do
124
+ test_store! CGI::Session::FileStore
125
+ end
126
+
127
+ =begin
128
+ # MemoryStore is completely transparent
129
+ it "should handle CGI::Session::MemoryStore" do
130
+ test_store! CGI::Session::MemoryStore
131
+ end
132
+ =end
133
+
134
+ it "should handle CGI::Session::PStore" do
135
+ test_store! CGI::Session::PStore
136
+ end
137
+
138
+ if $have_mem_cache_store
139
+ it "should handle CGI::Session::MemCacheStore" do
140
+ begin
141
+ memcache_pid = nil
142
+ memcache_port = 45328
143
+ memcache_host = '127.0.0.1'
144
+ memcache_args = [ "memcached",
145
+ "-p", memcache_port,
146
+ "-l", memcache_host,
147
+ ]
148
+ memcache_args << "-vvv" if $DEBUG
149
+
150
+ cache = ::MemCache.new(memcache_host)
151
+ cache.servers = "#{memcache_host}:#{memcache_port}"
152
+ session_opts = {
153
+ 'cache' => cache,
154
+ }
155
+ memcache_pid = Process.fork do
156
+ memcache_args.map!{|e| e.to_s}
157
+ $stderr.puts "#{__FILE__}: starting memcache #{memcache_args.inspect}" if $DEBUG
158
+ Process.exec(*memcache_args)
159
+ end
160
+ sleep 1
161
+ test_store! CGI::Session::MemCacheStore, session_opts
162
+ ensure
163
+ sleep 1
164
+ Process.kill(9, memcache_pid) if memcache_pid
165
+ end
166
+ end
167
+ end
168
+ end
169
+
@@ -0,0 +1,268 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ require 'trick_serial/serializer/simple'
4
+
5
+ ######################################################################
6
+
7
+ describe "TrickSerial::Serializer::Simple" do
8
+ def t
9
+ TrickSerial::Serializer::Test
10
+ end
11
+ before(:each) do
12
+ @s = TrickSerial::Serializer::Simple.new
13
+ @s.class_option_map = {
14
+ t::PhonyActiveRecord => { :proxy_class => TrickSerial::Serializer::ActiveRecordProxy, },
15
+ t::A => { :instance_vars => true },
16
+ t::B => { :instance_vars => [ "@x" ] },
17
+ }
18
+ TrickSerial::Serializer::Test::PhonyActiveRecord.find_map.clear
19
+
20
+ @m = TrickSerial::Serializer::Test::Model.new(123)
21
+ @m2 = TrickSerial::Serializer::Test::Model.new(456)
22
+ @m_unsaved = TrickSerial::Serializer::Test::Model.new(:unsaved)
23
+ @m_unsaved.id = nil
24
+ @h = {
25
+ :a => 1,
26
+ 'b' => 2,
27
+ :obj => Object.new,
28
+ :m => @m,
29
+ :a => [ 0, 1, @m, 3, 4, @m ],
30
+ :m_unsaved => @m_unsaved,
31
+ }
32
+ @h[:a2] = @h[:a]
33
+ @h[:h2] = @h
34
+ end
35
+
36
+ it "should handle #encode!" do
37
+ @h[:m].should == @m
38
+ @h[:a][2].should == @m
39
+
40
+ result = @s.encode!(@h)
41
+
42
+ result.object_id.should == @h.object_id
43
+ end
44
+
45
+ it "should handle #decode!" do
46
+ @h[:m].should == @m
47
+ @h[:a][2].should == @m
48
+
49
+ result = @s.decode!(@h)
50
+
51
+ result.object_id.should == @h.object_id
52
+ end
53
+
54
+ it "should honor #enabled?" do
55
+ enabled = true
56
+ @s.enabled = lambda { | | enabled }
57
+ result = @s.encode(@h)
58
+ result.object_id.should_not == @h.object_id
59
+
60
+ enabled = false
61
+ result = @s.encode(@h)
62
+ result.object_id.should == @h.object_id
63
+ end
64
+
65
+ it "should proxy unsaved models" do
66
+ @o = @s.encode!(@h)
67
+ @o = @s.decode!(@o)
68
+ @o[:m].object_id.should_not == @m.object_id
69
+ @o[:m].class.should == @m.class
70
+ @o[:m].id.should == @m.id
71
+ end
72
+
73
+ it "should not proxy unsaved models" do
74
+ @o = @s.encode!(@h)
75
+ @o = @s.decode!(@o)
76
+ @o[:m_unsaved].object_id.should == @m_unsaved.object_id
77
+ end
78
+
79
+ it "should handle encode/decode through Hash#[]" do
80
+ @s.encode!(@h)
81
+ @s.decode!(@h)
82
+ p = @h[:m]
83
+ # pp [ :p=, p ]
84
+ p.class.should == @m.class
85
+ end
86
+
87
+ it "should handle encode/decode through Hash#values" do
88
+ @s.encode!(@h)
89
+ p = @h.values
90
+ p.select{|m| @m.class == m.class}.size.should == 1 # one of them is not proxyable.
91
+
92
+ @s.decode!(@h)
93
+ p = @h.values
94
+ p.select{|m| @m.class == m.class}.size.should == 2
95
+ end
96
+
97
+ it "should handle encode/decode through Array#[]" do
98
+ @s.encode!(@h)
99
+ p = @h[:a][2]
100
+ p.class.should_not == @m.class
101
+
102
+ @s.decode!(@h)
103
+ p = @h[:a][2]
104
+ p.class.should == @m.class
105
+ end
106
+
107
+ it "should handle encode/decode through Array#each" do
108
+ @s.encode!(@h)
109
+ p = @h[:m]
110
+ a = [ ]
111
+ @h[:a].each do | e |
112
+ a << e
113
+ end
114
+ a.should == [ 0, 1, p, 3, 4, p ]
115
+
116
+ @s.decode!(@h)
117
+ p = @h[:m]
118
+ a = [ ]
119
+ @h[:a].each do | e |
120
+ a << e
121
+ end
122
+ a.should == [ 0, 1, p, 3, 4, p ]
123
+ end
124
+
125
+ it "should handle encode/decode through Array#map" do
126
+ @s.encode!(@h)
127
+ @s.decode!(@h)
128
+ p = @h[:m]
129
+ a = @h[:a].each { | e | e }
130
+ a.should == [ 0, 1, p, 3, 4, p ]
131
+ end
132
+
133
+ it "should handle encode/decode through Array#map!" do
134
+ @s.encode!(@h)
135
+ @s.decode!(@h)
136
+ p = @h[:m]
137
+ a = @h[:a].map! { | e | 1; e }
138
+ a.should == [ 0, 1, p, 3, 4, p ]
139
+ end
140
+
141
+ it "should handle encode/decode through Array#select" do
142
+ @s.encode!(@h)
143
+ @s.decode!(@h)
144
+ p = @h[:m]
145
+ a = @h[:a].select { | e | @m.class == e.class }
146
+ a.should == [ p, p ]
147
+ end
148
+
149
+ it "should handle encode/decode through Array#find" do
150
+ @s.encode!(@h)
151
+ @s.decode!(@h)
152
+ p = @h[:m]
153
+ a = @h[:a].find { | e | @m.class == e.class }
154
+ a.should == p
155
+ end
156
+
157
+ it "should lazily traverse proxies" do
158
+ fm = TrickSerial::Serializer::Test::PhonyActiveRecord.find_map
159
+
160
+ fm[@m.id].should == nil
161
+
162
+ @s.encode!(@h)
163
+ @h = Marshal.load(Marshal.dump(@h))
164
+
165
+ fm[@m.id].should == nil
166
+
167
+ @s.decode!(@h)
168
+ fm[@m.id].should == 1
169
+
170
+ p = @h[:m]
171
+
172
+ fm[@m.id].should == 1
173
+
174
+ @h[:m].object_id.should == p.object_id
175
+ @h[:a][2].object_id.should == p.object_id
176
+ @h[:a][5].object_id.should == p.object_id
177
+
178
+ fm[@m.id].should == 1
179
+ fm.keys.size.should == 1
180
+ end
181
+
182
+ it "should preserve object identity" do
183
+ @s.encode!(@h)
184
+ @s.decode!(@h)
185
+
186
+ p = @h[:m]
187
+
188
+ @h[:m].object_id.should == p.object_id
189
+ @h[:a2].object_id.should == @h[:a].object_id
190
+ @h[:a][2].object_id.should == p.object_id
191
+ @h[:a][5].object_id.should == p.object_id
192
+ end
193
+
194
+ it "should produce a serializable result" do
195
+ @s.encode!(@h)
196
+
197
+ @o = Marshal.load(Marshal.dump(@h))
198
+ @o = @s.decode!(@o)
199
+
200
+ p = @o[:m]
201
+ p.class.should == @m.class
202
+
203
+ @o[:m].object_id.should == p.object_id
204
+ @o[:a][2].object_id.should == p.object_id
205
+ @o[:a][5].object_id.should == p.object_id
206
+ @o[:h2].object_id.should == @o.object_id
207
+ end
208
+
209
+ it "should copy core collections" do
210
+ @o = @s.encode(@h)
211
+ @o = @s.decode(@o)
212
+
213
+ @o.object_id.should_not == @h.object_id
214
+ @o[:a].object_id.should_not == @h[:a].object_id
215
+ @o[:a2].object_id.should == @o[:a].object_id
216
+
217
+ @o[:m].object_id.should_not == @h[:m].object_id
218
+ @o[:h2].object_id.should == @o.object_id
219
+ end
220
+
221
+ it "should honor :instance_vars option" do
222
+ obj = t::A.new
223
+ obj.x = @m
224
+ obj.y = @m
225
+ obj = @s.encode!(obj)
226
+ e = Marshal.load(str = Marshal.dump(obj))
227
+
228
+ e.class.should == t::A
229
+ e.instance_variable_get("@x").class.should == TrickSerial::Serializer::ActiveRecordProxy
230
+ e.instance_variable_get("@y").class.should == TrickSerial::Serializer::ActiveRecordProxy
231
+
232
+ @s.verbose = @s.debug = true
233
+ obj = @s.decode!(e)
234
+ # $stderr.puts "marshal = #{str.inspect}"
235
+
236
+ e.class.should == t::A
237
+ e.instance_variable_get("@x").class.should == TrickSerial::Serializer::Test::Model
238
+ e.instance_variable_get("@y").class.should == TrickSerial::Serializer::Test::Model
239
+ e.x.object_id.should == e.y.object_id
240
+
241
+ obj = t::B.new
242
+ obj.x = @m # should be encoded
243
+ obj.y = @m # should not be encoded
244
+ @s.verbose = @s.debug = false
245
+ obj = @s.encode!(obj)
246
+ e = Marshal.load(str = Marshal.dump(obj))
247
+
248
+ e.class.should == t::B
249
+ e.instance_variable_get("@x").class.should == TrickSerial::Serializer::ActiveRecordProxy
250
+ e.instance_variable_get("@y").class.should == TrickSerial::Serializer::Test::Model
251
+
252
+ @s.verbose = @s.debug = true
253
+ obj = @s.decode!(e)
254
+ # $stderr.puts "marshal = #{str.inspect}"
255
+
256
+ e.class.should == t::B
257
+ e.instance_variable_get("@x").class.should == TrickSerial::Serializer::Test::Model
258
+ e.x.class.should == @m.class
259
+ e.instance_variable_get("@y").class.should == TrickSerial::Serializer::Test::Model
260
+ e.y.class.should == @m.class
261
+ e.x.id.should == @m.id
262
+ e.y.id.should == @m.id
263
+ e.x.object_id.should_not == @m.object_id
264
+ e.x.object_id.should_not == e.y.object_id
265
+ e.y.object_id.should_not == @m.object_id
266
+ end
267
+
268
+ end