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