trick_serial 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,239 @@
1
+ require 'trick_serial/serializer'
2
+
3
+ module TrickSerial
4
+ class Serializer
5
+ # Support for ::CGI::Session stores.
6
+ #
7
+ # Stores for use with CGI::Session and TrickSerial::Serializer::CgiSession::Store
8
+ # must implement #_data and #_data= to get access to the underlying Hash structure.
9
+ #
10
+ module CgiSession
11
+ def self.activate!
12
+ require 'cgi/session'
13
+ require 'cgi/session/pstore'
14
+
15
+ ::CGI::Session.send(:include, SessionSerializer)
16
+
17
+ if defined? ::CGI::Session::FileStore
18
+ ::CGI::Session::FileStore.send(:include, FileStoreSerializer)
19
+ end
20
+ if defined? ::CGI::Session::PStore
21
+ ::CGI::Session::PStore.send(:include, PStoreSerializer)
22
+ end
23
+ if defined? ::CGI::Session::MemCacheStore
24
+ ::CGI::Session::MemCacheStore.send(:include, MemCacheStoreSerializer)
25
+ end
26
+ if defined? ::CGI::Session::CassandraStore
27
+ ::CGI::Session::CassandraStore.send(:include, CassandraStoreSerializer)
28
+ end
29
+ end
30
+
31
+ # Defines a Session store Decorator that interjects TrickSerial::Serializer
32
+ # inside #restore, #update, etc.
33
+ #
34
+ # Example:
35
+ #
36
+ # cgi = CGI.new("html4")
37
+ # session = CGI::Session.new(
38
+ # 'database_manager' => TrickSerial::Serializer::CgiSession::Store, # The Decorator.
39
+ # 'TrickSerial.database_manager' => CGI::Session::PStore, # Actual store Class.
40
+ # # Options for PStore instance:
41
+ # 'tmpdir' => '/tmp/mysessions',
42
+ # 'session_key' => 'mykey',
43
+ # ...
44
+ # )
45
+ #
46
+ class Store
47
+ attr_accessor :logger, :logger_level
48
+
49
+ # Options:
50
+ #
51
+ # 'TrickSerial.database_manager': the actual session Store class (e.g.: CGI::Session::PStore).
52
+ # 'TrickSerial.dbman': an actual session store instance.
53
+ # 'TrickSerial.serializer': a clonable instance of TrickSerial::Serializer.
54
+ # 'TrickSerial.logger': a Log4r object.
55
+ # 'TrickSerial.logger_level': a Symbol for the logger level (e.g: :debug)
56
+ #
57
+ # The remaining options are passed to the actual Store specified by
58
+ # :'TrickSerial.database_manager'.
59
+ def initialize(session, option={})
60
+ @session = session
61
+ @option = option
62
+ @dbman_cls = option.delete('TrickSerial.database_manager') ||
63
+ (raise "#{self} options did not specify TrickSerial.database_manager: #{option.inspect}")
64
+ @dbman = option.delete('TrickSerial.dbman')
65
+ # option['new_session'] = true
66
+ @option['database_manager'] = @dbman_cls
67
+ @serializer = option.delete('TrickSerial.serializer')
68
+ @logger = option.delete('TrickSerial.logger')
69
+ @logger_level = option.delete('TrickSerial.logger_level') || :debug
70
+ _log { "creating #{self} for #{option.inspect}" }
71
+ end
72
+
73
+ def _dbman
74
+ @dbman ||=
75
+ begin
76
+ # Fool decorated Store.
77
+ save = @session.new_session
78
+ @session.new_session = true
79
+ # debugger
80
+ @dbman_cls.new(@session, @option)
81
+ ensure
82
+ @session.new_session = save
83
+ end
84
+ end
85
+
86
+ def _make_serializer
87
+ (@serializer || TrickSerial::Serializer.default).dup
88
+ end
89
+
90
+ def restore
91
+ _log { "#{self} restore" }
92
+ _dbman.restore
93
+ _dbman.decode_with_trick_serial_serializer! if _dbman.respond_to?(:decode_with_trick_serial_serializer!)
94
+ _dbman._data
95
+ end
96
+
97
+ def update
98
+ _log { "#{self} update" }
99
+ serializer = _make_serializer
100
+ data_save = _dbman._data
101
+ _dbman._data = serializer.encode(_dbman._data)
102
+ _dbman.encode_with_trick_serial_serializer! if _dbman.respond_to?(:encode_with_trick_serial_serializer!)
103
+ # debugger
104
+ _dbman.update
105
+ ensure
106
+ _dbman._data = data_save
107
+ end
108
+
109
+ def close
110
+ _log { "#{self} close" }
111
+ serializer = _make_serializer
112
+ data_save = _dbman._data
113
+ _dbman._data = serializer.encode(_dbman._data)
114
+ _dbman.encode_with_trick_serial_serializer! if _dbman.respond_to?(:encode_with_trick_serial_serializer!)
115
+ _dbman.close
116
+ ensure
117
+ _dbman._data = data_save
118
+ end
119
+
120
+ def delete
121
+ _log { "#{self} close" }
122
+ _dbman.delete
123
+ end
124
+
125
+ def _log msg = nil
126
+ msg ||= yield if block_given?
127
+ if msg && @logger
128
+ @logger.send(@logger_level, msg)
129
+ end
130
+ end
131
+ end
132
+
133
+ # Hacks to get access to Session.new_session.
134
+ module SessionSerializer
135
+ attr_writer :session_id, :new_session
136
+ end
137
+
138
+ # Defines common mixin for interjecting TrickSerial::Serializer before
139
+ # SessionStore#update saves its data.
140
+ module SessionStoreDataHook
141
+ def self.included target
142
+ super
143
+ target.extend(ModuleMethods)
144
+ end
145
+
146
+ module ModuleMethods
147
+ def included target
148
+ super
149
+ =begin
150
+ target.class_eval do
151
+ alias :restore_without_trick_serial_serializer :restore
152
+ alias :restore :restore_with_trick_serial_serializer
153
+ alias :update_without_trick_serial_serializer :update
154
+ alias :update :update_with_trick_serial_serializer
155
+ end
156
+ =end
157
+ end
158
+ end
159
+
160
+ =begin
161
+ def restore_with_trick_serial_serializer
162
+ restore_without_trick_serial_serializer
163
+ decode_with_trick_serial_serializer!
164
+ _data
165
+ end
166
+ =end
167
+
168
+ def encode_with_trick_serial_serializer!
169
+ end
170
+
171
+ def decode_with_trick_serial_serializer!
172
+ end
173
+
174
+ =begin
175
+ # Clones TrickSerial::Serializer.default.
176
+ # Encodes the session store's "data".
177
+ # Replaces the session store's data with the encoded data.
178
+ # Call original #update.
179
+ # Restores old session store's "data".
180
+ def update_with_trick_serial_serializer
181
+ serializer = TrickSerial::Serializer.default.dup
182
+ data_save = self._data
183
+ self._data = serializer.encode(self._data)
184
+ encode_with_trick_serial_serializer!
185
+ update_without_trick_serial_serializer
186
+ ensure
187
+ self._data = data_save
188
+ end
189
+ =end
190
+ end
191
+
192
+ # FileStore can only handle String => String data.
193
+ # Use Marshal and Base64 to further encode it.
194
+ module FileStoreSerializer
195
+ include SessionStoreDataHook
196
+ def self.included target
197
+ super
198
+ require 'base64'
199
+ end
200
+
201
+ def _data; @hash; end
202
+ def _data= x; @hash = x; end
203
+
204
+ PHONY_KEY = '_'.freeze
205
+
206
+ def encode_with_trick_serial_serializer!
207
+ # $stderr.puts "#{self} encode <= @hash=#{@hash.inspect}"
208
+ @hash &&= { PHONY_KEY => ::Base64.encode64(Marshal.dump(@hash)).chomp! }
209
+ # $stderr.puts "#{self} encode => @hash=#{@hash.inspect}"
210
+ end
211
+ def decode_with_trick_serial_serializer!
212
+ # $stderr.puts "#{self} decode <= @hash=#{@hash.inspect}"
213
+ @hash &&= (v = @hash[PHONY_KEY]) ? Marshal.load(::Base64.decode64(v)) : { }
214
+ # $stderr.puts "#{self} decode => @hash=#{@hash.inspect}"
215
+ end
216
+ end
217
+
218
+ module PStoreSerializer
219
+ include SessionStoreDataHook
220
+ def _data; @hash; end
221
+ def _data= x; @hash = x; end
222
+ end
223
+
224
+ module MemCacheStoreSerializer
225
+ include SessionStoreDataHook
226
+ def _data; @session_data; end
227
+ def _data=x; @session_data = x; end
228
+ end
229
+
230
+ module CassandraStoreSerializer
231
+ include SessionStoreDataHook
232
+ def _data; @session_data; end
233
+ def _data=x; @session_data = x; end
234
+ end
235
+ end
236
+
237
+ end
238
+ end
239
+
@@ -0,0 +1,88 @@
1
+ require 'trick_serial/serializer'
2
+
3
+ module TrickSerial
4
+ class Serializer
5
+ module Rails
6
+ def self.activate!
7
+ rails_version =
8
+ (Rails.version rescue nil) ||
9
+ (RAILS_VERSION rescue nil) || :unknown
10
+ case rails_version
11
+ when /^3\./
12
+ V3
13
+ when /^1\.2\./
14
+ V12
15
+ else
16
+ raise ArgumentError, "#{self}: Unknown Rails version: #{rails_version.inspect}"
17
+ end.activate!
18
+ end
19
+
20
+ # Rails 3 support.
21
+ module V3
22
+ def self.activate!
23
+ =begin
24
+ if defined? ::ActiveRecord::Session
25
+ ::ActiveRecord::Sesson.send(:include, ActiveRecordSessionSerializer)
26
+ end
27
+ =end
28
+ if defined? ::ActionDispatch::Session::MemCacheStore
29
+ ::ActionDispatch::Session::MemCacheStore.send(:include, SessionStoreSerializer)
30
+ end
31
+ end
32
+
33
+ module SessionStoreSerializer
34
+ def self.included target
35
+ super
36
+ target.class_eval do
37
+ alias :get_session_without_trick_serial_serializer :get_session
38
+ alias :get_session :get_session_with_trick_serial_serializer
39
+ alias :set_session_without_trick_serial_serializer :set_session
40
+ alias :set_session :get_session_with_trick_serial_serializer
41
+ end
42
+ end
43
+
44
+ def get_session_with_trick_serial_serializer env, sid
45
+ result = get_session_without_trick_serial_serializer env, sid
46
+ result
47
+ end
48
+
49
+ def set_session_with_trick_serial_serializer env, sid, session_data
50
+ serializer = (env[:'TrickSerial::Serializer.instance'] || TrickSerial::Serializer.default).dup
51
+ session_data = serializer.encode(session_data)
52
+ set_session_without_trick_serial_serializer env, sid, session_data
53
+ result
54
+ end
55
+ end
56
+
57
+ module ActiveRecordSessionSerializer
58
+ def self.included target
59
+ super
60
+ target.class_eval do
61
+ alias :marshal_data_without_trick_serial_serializer! :marshal_data!
62
+ alias :marshal_data! :marshal_data_with_trick_serial_serializer!
63
+ end
64
+ end
65
+
66
+ def marshal_data_with_trick_serial_serializer!
67
+ save_data = @data
68
+ if loaded?
69
+ serializer = TrickSerial::Serializer.default.dup
70
+ @data = serializer.encode(@data)
71
+ end
72
+ marshal_data_without_trick_serial_serializer!
73
+ ensure
74
+ @data = save_data
75
+ end
76
+ end
77
+ end
78
+
79
+ # Rails 1.2 support.
80
+ module V12
81
+ def self.activate!
82
+ require 'trick_serial/serializer/cgi_session'
83
+ TrickSerial::Serializer::CgiSession.activate!
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,129 @@
1
+
2
+ require 'trick_serial/serializer'
3
+
4
+ module TrickSerial
5
+ class Serializer
6
+ # Simple, non-swizzling coder.
7
+ # This requires encode and decode operations.
8
+ # Array and Hash are not extended to support swizzling.
9
+ # Ivar swizzling is not used.
10
+ class Simple < self
11
+ def _encode! x
12
+ # pp [ :_encode!, x.class, x ]
13
+
14
+ case x
15
+ when ObjectProxy
16
+ x
17
+
18
+ when Array
19
+ if o = @visited[x.object_id]
20
+ return o.first
21
+ end
22
+ extended = false
23
+ o = x
24
+ x = x.dup if @copy
25
+ @visited[o.object_id] = [ x, o ]
26
+ x.map! do | v |
27
+ _encode! v
28
+ end
29
+
30
+ when Hash
31
+ if o = @visited[x.object_id]
32
+ return o.first
33
+ end
34
+ extended = false
35
+ o = x
36
+ x = x.dup if @copy
37
+ @visited[o.object_id] = [ x, o ]
38
+ x.keys.to_a.each do | k |
39
+ x[k] = _encode!(x[k])
40
+ end
41
+
42
+ when *@proxyable
43
+ if proxy = @object_to_proxy_map[x.object_id]
44
+ return proxy.first
45
+ end
46
+ # debugger
47
+ o = x
48
+ proxy_cls = nil
49
+ if class_option = self._class_option(x)
50
+ proxy_cls = class_option[:proxy_class]
51
+ # Deeply encode instance vars?
52
+ if ivs = class_option[:instance_vars]
53
+ ivs = x.instance_variables if ivs == true
54
+ x = x.dup if @copy
55
+ @object_to_proxy_map[o.object_id] = [ x, o ]
56
+ ivs.each do | ivar |
57
+ v = x.instance_variable_get(ivar)
58
+ v = _encode!(v)
59
+ x.instance_variable_set(ivar, v)
60
+ end
61
+ end
62
+ end
63
+
64
+ x = _make_proxy o, x, proxy_cls
65
+ end
66
+
67
+ x
68
+ end # def
69
+
70
+ def _decode! x
71
+ case x
72
+ when ObjectProxy
73
+ x = x.object
74
+
75
+ when Array
76
+ if o = @visited[x.object_id]
77
+ return o.first
78
+ end
79
+ extended = false
80
+ o = x
81
+ x = x.dup if @copy
82
+ @visited[o.object_id] = [ x, o ]
83
+ x.map! do | v |
84
+ _decode! v
85
+ end
86
+
87
+ when Hash
88
+ if o = @visited[x.object_id]
89
+ return o.first
90
+ end
91
+ extended = false
92
+ o = x
93
+ x = x.dup if @copy
94
+ @visited[o.object_id] = [ x, o ]
95
+ x.keys.to_a.each do | k |
96
+ x[k] = _decode!(x[k])
97
+ end
98
+
99
+ when *@proxyable
100
+ if proxy = @object_to_proxy_map[x.object_id]
101
+ return proxy.first
102
+ end
103
+ # debugger
104
+ o = x
105
+ if class_option = _class_option(x)
106
+ # Deeply encode instance vars?
107
+ if ivs = class_option[:instance_vars]
108
+ ivs = x.instance_variables if ivs == true
109
+ x = x.dup if @copy
110
+ @object_to_proxy_map[o.object_id] = [ x, o ]
111
+ ivs.each do | ivar |
112
+ v = x.instance_variable_get(ivar)
113
+ # $stderr.puts "\n#{x.class} #{x.object_id} ivar #{ivar} #{v.inspect}" if @debug
114
+ v = _decode!(v)
115
+ x.instance_variable_set(ivar, v)
116
+ end
117
+ end
118
+ end
119
+ @object_to_proxy_map[o.object_id] ||= [ x, o ]
120
+ end # case
121
+
122
+ x
123
+ end # def
124
+
125
+ end # class
126
+ end # class
127
+ end # module
128
+
129
+