trick_serial 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +128 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/cabar.yml +17 -0
- data/lib/trick_serial.rb +3 -0
- data/lib/trick_serial/serializer.rb +427 -0
- data/lib/trick_serial/serializer/cgi_session.rb +239 -0
- data/lib/trick_serial/serializer/rails.rb +88 -0
- data/lib/trick_serial/serializer/simple.rb +129 -0
- data/spec/spec_helper.rb +76 -0
- data/spec/trick_serial/cgi_session_spec.rb +169 -0
- data/spec/trick_serial/serializer_simple_spec.rb +268 -0
- data/spec/trick_serial/serializer_spec.rb +275 -0
- metadata +207 -0
data/spec/spec_helper.rb
ADDED
@@ -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
|