Pimki 1.3.092 → 1.4.092

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 (88) hide show
  1. data/README +145 -131
  2. data/README-PIMKI +15 -5
  3. data/app/controllers/wiki.rb +167 -54
  4. data/app/models/author.rb +3 -3
  5. data/app/models/chunks/chunk.rb +3 -3
  6. data/app/models/chunks/engines.rb +18 -21
  7. data/app/models/chunks/include.rb +29 -29
  8. data/app/models/chunks/literal.rb +20 -20
  9. data/app/models/chunks/match.rb +19 -19
  10. data/app/models/chunks/nowiki.rb +31 -31
  11. data/app/models/chunks/nowiki_test.rb +14 -14
  12. data/app/models/chunks/test.rb +18 -18
  13. data/app/models/chunks/todo.rb +44 -23
  14. data/app/models/chunks/uri.rb +97 -97
  15. data/app/models/chunks/uri_test.rb +92 -92
  16. data/app/models/chunks/wiki.rb +4 -4
  17. data/app/models/chunks/wiki_symbols.rb +22 -22
  18. data/app/models/chunks/wiki_test.rb +36 -36
  19. data/app/models/page.rb +39 -7
  20. data/app/models/page_lock.rb +23 -23
  21. data/app/models/page_set.rb +72 -72
  22. data/app/models/page_test.rb +75 -75
  23. data/app/models/revision.rb +1 -1
  24. data/app/models/revision_test.rb +251 -251
  25. data/app/models/web.rb +19 -6
  26. data/app/models/web_test.rb +52 -52
  27. data/app/models/wiki_content.rb +131 -119
  28. data/app/models/wiki_service.rb +31 -16
  29. data/app/models/wiki_service_test.rb +15 -15
  30. data/app/models/wiki_words.rb +1 -1
  31. data/app/models/wiki_words_test.rb +12 -12
  32. data/app/views/bottom.rhtml +3 -3
  33. data/app/views/markdown_help.rhtml +15 -15
  34. data/app/views/menu.rhtml +20 -20
  35. data/app/views/navigation.rhtml +26 -26
  36. data/app/views/rdoc_help.rhtml +15 -15
  37. data/app/views/static_style_sheet.rhtml +237 -237
  38. data/app/views/style.rhtml +178 -178
  39. data/app/views/textile_help.rhtml +27 -27
  40. data/app/views/top.rhtml +7 -2
  41. data/app/views/wiki/authors.rhtml +15 -15
  42. data/app/views/wiki/bliki.rhtml +101 -101
  43. data/app/views/wiki/bliki_edit.rhtml +3 -0
  44. data/app/views/wiki/bliki_new.rhtml +3 -0
  45. data/app/views/wiki/bliki_revision.rhtml +90 -90
  46. data/app/views/wiki/edit.rhtml +12 -3
  47. data/app/views/wiki/edit_menu.rhtml +64 -47
  48. data/app/views/wiki/edit_web.rhtml +65 -18
  49. data/app/views/wiki/export.rhtml +14 -14
  50. data/app/views/wiki/feeds.rhtml +10 -10
  51. data/app/views/wiki/list.rhtml +17 -15
  52. data/app/views/wiki/locked.rhtml +13 -13
  53. data/app/views/wiki/login.rhtml +10 -10
  54. data/app/views/wiki/mind.rhtml +0 -1
  55. data/app/views/wiki/new.rhtml +8 -3
  56. data/app/views/wiki/new_system.rhtml +77 -77
  57. data/app/views/wiki/new_web.rhtml +63 -63
  58. data/app/views/wiki/page.rhtml +88 -82
  59. data/app/views/wiki/print.rhtml +15 -15
  60. data/app/views/wiki/published.rhtml +2 -1
  61. data/app/views/wiki/recently_revised.rhtml +31 -31
  62. data/app/views/wiki/revision.rhtml +1 -7
  63. data/app/views/wiki/rollback.rhtml +31 -0
  64. data/app/views/wiki/rss_feed.rhtml +21 -21
  65. data/app/views/wiki/search.rhtml +48 -48
  66. data/app/views/wiki/tex.rhtml +22 -22
  67. data/app/views/wiki/tex_web.rhtml +34 -34
  68. data/app/views/wiki/todo.rhtml +90 -67
  69. data/app/views/wiki/web_list.rhtml +12 -12
  70. data/app/views/wiki_words_help.rhtml +1 -1
  71. data/favicon.png +0 -0
  72. data/libraries/action_controller_servlet.rb +17 -2
  73. data/libraries/bluecloth.rb +1127 -1127
  74. data/libraries/diff/diff.rb +474 -474
  75. data/libraries/diff/diff_test.rb +79 -79
  76. data/libraries/erb.rb +490 -490
  77. data/libraries/madeleine/automatic.rb +418 -357
  78. data/libraries/madeleine/clock.rb +94 -94
  79. data/libraries/madeleine/files.rb +19 -0
  80. data/libraries/madeleine/zmarshal.rb +60 -0
  81. data/libraries/madeleine_service.rb +14 -15
  82. data/libraries/rdocsupport.rb +155 -155
  83. data/libraries/redcloth_for_tex.rb +869 -869
  84. data/libraries/redcloth_for_tex_test.rb +40 -40
  85. data/libraries/view_helper.rb +32 -32
  86. data/libraries/web_controller_server.rb +96 -94
  87. data/pimki.rb +47 -6
  88. metadata +18 -4
@@ -1,357 +1,418 @@
1
- require 'yaml'
2
-
3
- module Madeleine
4
-
5
- # Automatic commands for Madeleine
6
- #
7
- # Author:: Stephen Sykes <ruby@stephensykes.com>
8
- # Copyright:: Copyright (C) 2003-2004
9
- # Version:: 0.4
10
- #
11
- # This module provides a way of automatically generating command objects for madeleine to
12
- # store. It works by making a proxy object for all objects of any classes in which it is included.
13
- # Method calls to these objects are intercepted, and stored as a command before being
14
- # passed on to the real receiver. The command objects remember which object the command was
15
- # destined for by using a pair of internal ids that are contained in each of the proxy objects.
16
- #
17
- # There is also a mechanism for specifying which methods not to intercept calls to by using
18
- # automatic_read_only, and its opposite automatic_read_write.
19
- #
20
- # Should you require it, the snapshots can be stored as yaml, just pass YAML as the second
21
- # argument to AutomaticSnapshotMadeleine.new. The system will read either Marshal or YAML at
22
- # startup.
23
- #
24
- # This module is designed to work correctly in the case there are multiple madeleine systems in use by
25
- # a single program, and is also safe to use with threads.
26
- #
27
- # Usage:
28
- #
29
- # require 'madeleine'
30
- # require 'madeleine/automatic'
31
- #
32
- # class A
33
- # include Madeleine::Automatic::Interceptor
34
- # attr_reader :foo
35
- # automatic_read_only :foo
36
- # def initialize(param1, ...)
37
- # ...
38
- # end
39
- # def some_method(paramA, ...)
40
- # ...
41
- # end
42
- # automatic_read_only
43
- # def bigfoo
44
- # foo.upcase
45
- # end
46
- # end
47
- #
48
- # mad = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
49
- #
50
- # mad.system.some_method(paramA, ...) # logged as a command by madeleine
51
- # print mad.foo # not logged
52
- # print mad.bigfoo # not logged
53
- # mad.take_snapshot
54
- #
55
-
56
- module Automatic
57
- #
58
- # This module should be included (at the top) in any classes that are to be persisted.
59
- # It will intercept method calls and make sure they are converted into commands that are logged by Madeleine.
60
- # It does this by returning a Prox object that is a proxy for the real object.
61
- #
62
- # It also handles automatic_read_only and automatic_read_write, allowing user specification of which methods
63
- # should be made into commands
64
- #
65
- module Interceptor
66
- #
67
- # When included, redefine new so that we can return a Prox object instead, and define methods to handle
68
- # keeping track of which methods are read only
69
- #
70
- def self.included(klass)
71
- class << klass #:nodoc:
72
- alias_method :_old_new, :new
73
- @@auto_read_only_flag = false
74
- @@read_only_methods = []
75
-
76
- def new(*args, &block)
77
- Prox.new(_old_new(*args, &block))
78
- end
79
- #
80
- # Called when a method added - remember symbol if read only
81
- #
82
- def method_added(symbol)
83
- @@read_only_methods << symbol if @@auto_read_only_flag
84
- end
85
- #
86
- # Set the read only flag, or add read only methods
87
- #
88
- def automatic_read_only(*list)
89
- if (list == [])
90
- @@auto_read_only_flag = true
91
- else
92
- list.each {|s| @@read_only_methods << s}
93
- end
94
- end
95
- #
96
- # Clear the read only flag, or remove read only methods
97
- #
98
- def automatic_read_write(*list)
99
- if (list == [])
100
- @@auto_read_only_flag = false
101
- else
102
- list.each {|s| @@read_only_methods.delete(s)}
103
- end
104
- end
105
-
106
- end
107
- end
108
- #
109
- # Return the list of read only methods so Prox#method_missing can find what to and what not to make into a command
110
- #
111
- def read_only_methods
112
- @@read_only_methods
113
- end
114
- end
115
-
116
- #
117
- # A Command object is automatically created for each method call to an object within the system that comes from without.
118
- # These objects are recorded in the log by Madeleine.
119
- #
120
- # Note: The command also records which system it belongs to. This is used in a recovery situation.
121
- # If a command contains a sysid that doesn't match the system sent to us, then we change that
122
- # system's id to the one in the command. This makes a system adopt the correct id as soon as a
123
- # command for it is executed. This is the case when restoring a system for which there is no snapshot.
124
- #
125
- class Command
126
- def initialize(symbol, myid, sysid, *args)
127
- @symbol = symbol
128
- @myid = myid
129
- @sysid = sysid
130
- @args = args
131
- end
132
- #
133
- # Called by madeleine when the command is done either first time, or when restoring the log
134
- #
135
- def execute(system)
136
- AutomaticSnapshotMadeleine.register_sysid(@sysid) if (system.sysid != @sysid)
137
- Thread.current[:system].myid2ref(@myid).thing.send(@symbol, *@args)
138
- end
139
- end
140
- #
141
- # This is a little class to pass to SnapshotMadeleine. This is used for snapshots only.
142
- # It acts as the marshaller, and just passes marshalling requests on to the user specified
143
- # marshaller. This defaults to Marshal, but could be YAML or another.
144
- # After we have done a restore, the ObjectSpace is searched for instances of Prox to
145
- # add new objects to the list in AutomaticSnapshotMadeleine
146
- #
147
- class Automatic_marshaller
148
- def Automatic_marshaller.load(io)
149
- # restored_obj = Thread.current[:system].marshaller.load(arg)
150
- restored_obj = Deserialize.load(io, Thread.current[:system].marshaller)
151
- ObjectSpace.each_object(Prox) {|o| Thread.current[:system].restore(o) if (o.sysid == restored_obj.sysid)}
152
- restored_obj
153
- end
154
- def Automatic_marshaller.dump(obj, stream = nil)
155
- Thread.current[:system].marshaller.dump(obj, stream)
156
- end
157
- end
158
- #
159
- # A Prox object is generated and returned by Interceptor each time a system object is created.
160
- #
161
- class Prox
162
- attr_accessor :thing, :myid, :sysid
163
-
164
- def initialize(thing)
165
- if (thing)
166
- raise "App object created outside of app" unless Thread.current[:system]
167
- @sysid = Thread.current[:system].sysid
168
- @myid = Thread.current[:system].add(self)
169
- @thing = thing
170
- end
171
- end
172
- #
173
- # This automatically makes and executes a new Command if a method is called from
174
- # outside the system.
175
- #
176
- def method_missing(symbol, *args, &block)
177
- # print "Sending #{symbol} to #{@thing.to_s}, myid=#{@myid}, sysid=#{@sysid}\n"
178
- raise NoMethodError, "Undefined method" unless @thing.respond_to?(symbol)
179
- if (Thread.current[:system] || @thing.read_only_methods.include?(symbol))
180
- @thing.send(symbol, *args, &block)
181
- else
182
- raise "Cannot make command with block" if block_given?
183
- Thread.current[:system] = AutomaticSnapshotMadeleine.systems[@sysid]
184
- begin
185
- result = Thread.current[:system].execute_command(Command.new(symbol, @myid, @sysid, *args))
186
- ensure
187
- Thread.current[:system] = false
188
- end
189
- result
190
- end
191
- end
192
- #
193
- # Custom marshalling - this adds the internal id (myid) and the system id to a marshall
194
- # of the object we are the proxy for.
195
- # We take care to not marshal the same object twice, so circular references will work.
196
- # We ignore Thread.current[:system].marshaller here - this is only called by Marshal, and
197
- # marshal is always used for Command objects
198
- #
199
- def _dump(depth)
200
- if (Thread.current[:snapshot_memory])
201
- if (Thread.current[:snapshot_memory][self])
202
- [@myid.to_s, @sysid].pack("A8A30")
203
- else
204
- Thread.current[:snapshot_memory][self] = true
205
- [@myid.to_s, @sysid].pack("A8A30") + Marshal.dump(@thing, depth)
206
- end
207
- else
208
- [@myid.to_s, @sysid].pack("A8A30")
209
- end
210
- end
211
- #
212
- # Custom marshalling for Marshal - restore a Prox object.
213
- #
214
- def Prox._load(str)
215
- x = Prox.new(nil)
216
- a = str.unpack("A8A30a*")
217
- x.myid = a[0].to_i
218
- x.sysid = a[1]
219
- x = Thread.current[:system].restore(x)
220
- x.thing = Marshal.load(a[2]) if (a[2] > "")
221
- x
222
- end
223
-
224
- end
225
-
226
- #
227
- # The AutomaticSnapshotMadeleine class contains an instance of the persister
228
- # (default is SnapshotMadeleine) and provides additional automatic functionality.
229
- #
230
- # The class keeps a record of all the systems that currently exist.
231
- # Each instance of the class keeps a record of Prox objects in that system by internal id (myid).
232
- #
233
- # We also add functionality to take_snapshot in order to set things up so that the custom Prox object
234
- # marshalling will work correctly.
235
- #
236
- class AutomaticSnapshotMadeleine
237
- attr_accessor :sysid, :marshaller
238
- attr_reader :list, :marshaller
239
-
240
- def initialize(directory_name, marshaller=Marshal, persister=SnapshotMadeleine, &new_system_block)
241
- @sysid ||= Time.now.to_f.to_s + Thread.current.object_id.to_s # Gererate a new sysid
242
- @myid_count = 0 # This sysid will be used only if new
243
- @list = {} # object is taken by madeleine
244
- Thread.current[:system] = self # during system startup system should not create commands
245
- AutomaticSnapshotMadeleine.register_sysid(@sysid) # this sysid may be overridden
246
- @marshaller = marshaller # until attrb
247
- begin
248
- @persister = persister.new(directory_name, Automatic_marshaller, &new_system_block)
249
- AutomaticSnapshotMadeleine.register_sysid(@sysid) # needed if there were no commands
250
- ensure
251
- Thread.current[:system] = false
252
- end
253
- end
254
- #
255
- # Add a proxy object to the list, return the myid for that object
256
- #
257
- def add(proxo)
258
- @list[@myid_count += 1] = proxo.object_id
259
- @myid_count
260
- end
261
- #
262
- # Restore a marshalled proxy object to list - myid_count is increased as required.
263
- # If the object already exists in the system then the existing object must be used.
264
- #
265
- def restore(proxo)
266
- if (@list[proxo.myid])# && proxo.sysid == myid2ref(proxo.myid).sysid)
267
- proxo = myid2ref(proxo.myid)
268
- else
269
- @list[proxo.myid] = proxo.object_id
270
- @myid_count = proxo.myid if (@myid_count < proxo.myid)
271
- end
272
- @sysid = proxo.sysid # to be sure to have the correct sysid
273
- proxo
274
- end
275
- #
276
- # Returns a reference to the object indicated by the internal id supplied.
277
- #
278
- def myid2ref(myid)
279
- raise "Internal id #{myid} not found" unless objid = @list[myid]
280
- ObjectSpace._id2ref(objid)
281
- end
282
- #
283
- # Take a snapshot of the system.
284
- #
285
- def take_snapshot
286
- begin
287
- Thread.current[:system] = self
288
- Thread.current[:snapshot_memory] = {}
289
- @persister.take_snapshot
290
- ensure
291
- Thread.current[:snapshot_memory] = nil
292
- Thread.current[:system] = false
293
- end
294
- end
295
- #
296
- # Sets the real sid for this thread's system - called during startup or from a command.
297
- #
298
- def AutomaticSnapshotMadeleine.register_sysid(sid)
299
- Thread.critical = true
300
- @@systems ||= {} # holds systems by sysid
301
- @@systems[sid] = Thread.current[:system]
302
- Thread.critical = false
303
- @@systems[sid].sysid = sid
304
- @@systems[sid].list.delete_if {|k,v| # set all the prox objects that already exist to have the right sysid
305
- begin
306
- ObjectSpace._id2ref(v).sysid = sid
307
- false
308
- rescue RangeError
309
- true # Id was to a GC'd object, delete it
310
- end
311
- }
312
- end
313
- #
314
- # Returns the hash containing the systems.
315
- #
316
- def AutomaticSnapshotMadeleine.systems
317
- @@systems
318
- end
319
- #
320
- # Pass on any other calls to the persister
321
- #
322
- def method_missing(symbol, *args, &block)
323
- @persister.send(symbol, *args, &block)
324
- end
325
- end
326
-
327
-
328
- class Deserialize
329
- #
330
- # Detect marshal format, and return deserialized object using the right marshaller
331
- # If detection didn't work, use the marshaller given in the optional 2nd argument
332
- #
333
- def Deserialize.load(io, marshaller=Marshal)
334
- c = io.getc
335
- c1 = io.getc
336
- io.rewind
337
- if (c == Marshal::MAJOR_VERSION && c1 <= Marshal::MINOR_VERSION)
338
- Marshal.load(io)
339
- else
340
- while (s = io.gets)
341
- break if (s !~ /^\s*#/ && s !~ /^\s*$/) # ignore blank and comment lines
342
- end
343
- io.rewind
344
- if (s && s =~ /^\s*---/) # "---" is the yaml header
345
- YAML.load(io)
346
- else
347
- marshaller.load(io)
348
- end
349
- end
350
- end
351
-
352
- end
353
-
354
- end
355
- end
356
-
357
- AutomaticSnapshotMadeleine = Madeleine::Automatic::AutomaticSnapshotMadeleine
1
+ require 'yaml'
2
+ require 'madeleine/zmarshal'
3
+ require 'soap/marshal'
4
+
5
+ module Madeleine
6
+
7
+ # Automatic commands for Madeleine
8
+ #
9
+ # Author:: Stephen Sykes <sds@stephensykes.com>
10
+ # Copyright:: Copyright (C) 2003-2004
11
+ # Version:: 0.41
12
+ #
13
+ # This module provides a way of automatically generating command objects for madeleine to
14
+ # store. It works by making a proxy object for all objects of any classes in which it is included.
15
+ # Method calls to these objects are intercepted, and stored as a command before being
16
+ # passed on to the real receiver. The command objects remember which object the command was
17
+ # destined for by using a pair of internal ids that are contained in each of the proxy objects.
18
+ #
19
+ # There is also a mechanism for specifying which methods not to intercept calls to by using
20
+ # automatic_read_only, and its opposite automatic_read_write.
21
+ #
22
+ # Should you require it, the snapshots can be stored as yaml, and can be compressed. Just pass
23
+ # the marshaller you want to use as the second argument to AutomaticSnapshotMadeleine.new.
24
+ # If the passed marshaller did not successfully deserialize the latest snapshot, the system
25
+ # will try to automatically detect and read either Marshal, YAML, SOAP, or their corresponding
26
+ # compressed versions.
27
+ #
28
+ # This module is designed to work correctly in the case there are multiple madeleine systems in use by
29
+ # a single program, and is also safe to use with threads.
30
+ #
31
+ # Usage:
32
+ #
33
+ # require 'madeleine'
34
+ # require 'madeleine/automatic'
35
+ #
36
+ # class A
37
+ # include Madeleine::Automatic::Interceptor
38
+ # attr_reader :foo
39
+ # automatic_read_only :foo
40
+ # def initialize(param1, ...)
41
+ # ...
42
+ # end
43
+ # def some_method(paramA, ...)
44
+ # ...
45
+ # end
46
+ # automatic_read_only
47
+ # def bigfoo
48
+ # foo.upcase
49
+ # end
50
+ # end
51
+ #
52
+ # mad = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
53
+ #
54
+ # mad.system.some_method(paramA, ...) # logged as a command by madeleine
55
+ # print mad.foo # not logged
56
+ # print mad.bigfoo # not logged
57
+ # mad.take_snapshot
58
+ #
59
+
60
+ module Automatic
61
+ #
62
+ # This module should be included (at the top) in any classes that are to be persisted.
63
+ # It will intercept method calls and make sure they are converted into commands that are logged by Madeleine.
64
+ # It does this by returning a Prox object that is a proxy for the real object.
65
+ #
66
+ # It also handles automatic_read_only and automatic_read_write, allowing user specification of which methods
67
+ # should be made into commands
68
+ #
69
+ module Interceptor
70
+ #
71
+ # When included, redefine new so that we can return a Prox object instead, and define methods to handle
72
+ # keeping track of which methods are read only
73
+ #
74
+ def self.included(klass)
75
+ class <<klass
76
+ alias_method :_old_new, :new
77
+
78
+ def new(*args, &block)
79
+ Prox.new(_old_new(*args, &block))
80
+ end
81
+ #
82
+ # Called when a method added - remember symbol if read only
83
+ # This is a good place to add in any superclass's read only methods also
84
+ #
85
+ def method_added(symbol)
86
+ self.instance_eval {
87
+ @read_only_methods ||= []
88
+ @auto_read_only_flag ||= false
89
+ @read_only_methods << symbol if @auto_read_only_flag
90
+ c = self
91
+ while (c = c.superclass)
92
+ if (c.instance_eval {instance_variables.include? "@read_only_methods"})
93
+ @read_only_methods |= c.instance_eval {@read_only_methods}
94
+ end
95
+ end
96
+ }
97
+ end
98
+ #
99
+ # Set the read only flag, or add read only methods
100
+ #
101
+ def automatic_read_only(*list)
102
+ if (list == [])
103
+ self.instance_eval {@auto_read_only_flag = true}
104
+ else
105
+ list.each {|s| self.instance_eval {@read_only_methods ||= []; @read_only_methods << s}}
106
+ end
107
+ end
108
+ #
109
+ # Clear the read only flag, or remove read only methods
110
+ #
111
+ def automatic_read_write(*list)
112
+ if (list == [])
113
+ self.instance_eval {@auto_read_only_flag = false}
114
+ else
115
+ list.each {|s| self.instance_eval {@read_only_methods ||= []; @read_only_methods.delete(s)}}
116
+ end
117
+ end
118
+
119
+ end
120
+ end
121
+ #
122
+ # Return the list of read only methods so Automatic_proxy#method_missing can find what to and what not to make into a command
123
+ #
124
+ def read_only_methods
125
+ self.class.instance_eval {@read_only_methods}
126
+ end
127
+ end
128
+
129
+ #
130
+ # A Command object is automatically created for each method call to an object within the system that comes from without.
131
+ # These objects are recorded in the log by Madeleine.
132
+ #
133
+ class Command
134
+ def initialize(symbol, myid, *args)
135
+ @symbol = symbol
136
+ @myid = myid
137
+ @args = args
138
+ end
139
+ #
140
+ # Called by madeleine when the command is done either first time, or when restoring the log
141
+ #
142
+ def execute(system)
143
+ Thread.current[:system].myid2ref(@myid).thing.send(@symbol, *@args)
144
+ end
145
+ end
146
+ #
147
+ # This is a little class to pass to SnapshotMadeleine. This is used for snapshots only.
148
+ # It acts as the marshaller, and just passes marshalling requests on to the user specified
149
+ # marshaller. This defaults to Marshal, but could be YAML or another.
150
+ # After we have done a restore, the ObjectSpace is searched for instances of Prox to
151
+ # add new objects to the list in AutomaticSnapshotMadeleine
152
+ #
153
+ class Automatic_marshaller #:nodoc:
154
+ def Automatic_marshaller.load(io)
155
+ restored_obj = Deserialize.load(io, Thread.current[:system].marshaller)
156
+ ObjectSpace.each_object(Prox) {|o| Thread.current[:system].restore(o) if (o.sysid == restored_obj.sysid)}
157
+ restored_obj
158
+ end
159
+ def Automatic_marshaller.dump(obj, io = nil)
160
+ Thread.current[:system].marshaller.dump(obj, io)
161
+ end
162
+ end
163
+ #
164
+ # A Prox object is generated and returned by Interceptor each time a system object is created.
165
+ #
166
+ class Prox #:nodoc:
167
+ attr_accessor :thing, :myid, :sysid
168
+
169
+ def initialize(thing)
170
+ if (thing)
171
+ raise "App object created outside of app" unless Thread.current[:system]
172
+ @sysid = Thread.current[:system].sysid
173
+ @myid = Thread.current[:system].add(self)
174
+ @thing = thing
175
+ end
176
+ end
177
+ #
178
+ # This automatically makes and executes a new Command if a method is called from
179
+ # outside the system.
180
+ #
181
+ def method_missing(symbol, *args, &block)
182
+ # print "Sending #{symbol} to #{@thing.to_s}, myid=#{@myid}, sysid=#{@sysid}\n"
183
+ raise NoMethodError, "Undefined method" unless @thing.respond_to?(symbol)
184
+ if (Thread.current[:system])
185
+ @thing.send(symbol, *args, &block)
186
+ else
187
+ raise "Cannot make command with block" if block_given?
188
+ Thread.current[:system] = AutomaticSnapshotMadeleine.systems[@sysid]
189
+ begin
190
+ if (@thing.read_only_methods.include?(symbol))
191
+ result = Thread.current[:system].execute_query(Command.new(symbol, @myid, *args))
192
+ else
193
+ result = Thread.current[:system].execute_command(Command.new(symbol, @myid, *args))
194
+ end
195
+ ensure
196
+ Thread.current[:system] = false
197
+ end
198
+ result
199
+ end
200
+ end
201
+ #
202
+ # Custom marshalling - this adds the internal id (myid) and the system id to a marshal
203
+ # of the object we are the proxy for.
204
+ # We take care to not marshal the same object twice, so circular references will work.
205
+ # We ignore Thread.current[:system].marshaller here - this is only called by Marshal, and
206
+ # marshal is always used for Command objects
207
+ #
208
+ def _dump(depth)
209
+ if (Thread.current[:snapshot_memory])
210
+ if (Thread.current[:snapshot_memory][self])
211
+ [@myid.to_s, @sysid].pack("A8A30")
212
+ else
213
+ Thread.current[:snapshot_memory][self] = true
214
+ [@myid.to_s, @sysid].pack("A8A30") + Marshal.dump(@thing, depth)
215
+ end
216
+ else
217
+ [@myid.to_s, @sysid].pack("A8A30") # never marshal a prox object in a command, just ref
218
+ end
219
+ end
220
+
221
+ #
222
+ # Custom marshalling for Marshal - restore a Prox object.
223
+ #
224
+ def Prox._load(str)
225
+ x = Prox.new(nil)
226
+ a = str.unpack("A8A30a*")
227
+ x.myid = a[0].to_i
228
+ x.sysid = a[1]
229
+ x = Thread.current[:system].restore(x)
230
+ x.thing = Marshal.load(a[2]) if (a[2] > "")
231
+ x
232
+ end
233
+
234
+ end
235
+
236
+ #
237
+ # The AutomaticSnapshotMadeleine class contains an instance of the persister
238
+ # (default is SnapshotMadeleine) and provides additional automatic functionality.
239
+ #
240
+ # The class is instantiated the same way as SnapshotMadeleine:
241
+ # madeleine_sys = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
242
+ # The second initialisation parameter is the persister. Supported persisters are:
243
+ #
244
+ # * Marshal (default)
245
+ # * YAML
246
+ # * SOAP::Marshal
247
+ # * Madeleine::ZMarshal.new(Marshal)
248
+ # * Madeleine::ZMarshal.new(YAML)
249
+ # * Madeleine::ZMarshal.new(SOAP::Marshal)
250
+ #
251
+ # The class keeps a record of all the systems that currently exist.
252
+ # Each instance of the class keeps a record of Prox objects in that system by internal id (myid).
253
+ #
254
+ # We also add functionality to take_snapshot in order to set things up so that the custom Prox object
255
+ # marshalling will work correctly.
256
+ #
257
+ class AutomaticSnapshotMadeleine
258
+ attr_accessor :marshaller
259
+ attr_reader :list, :sysid
260
+
261
+ def initialize(directory_name, marshaller=Marshal, persister=SnapshotMadeleine, &new_system_block)
262
+ @sysid ||= Time.now.to_f.to_s + Thread.current.object_id.to_s # Gererate a new sysid
263
+ @myid_count = 0
264
+ @list = {}
265
+ Thread.current[:system] = self # during system startup system should not create commands
266
+ Thread.critical = true
267
+ @@systems ||= {} # holds systems by sysid
268
+ @@systems[@sysid] = self
269
+ Thread.critical = false
270
+ @marshaller = marshaller # until attrb
271
+ begin
272
+ @persister = persister.new(directory_name, Automatic_marshaller, &new_system_block)
273
+ @list.delete_if {|k,v| # set all the prox objects that now exist to have the right sysid
274
+ begin
275
+ ObjectSpace._id2ref(v).sysid = @sysid
276
+ false
277
+ rescue RangeError
278
+ true # Id was to a GC'd object, delete it
279
+ end
280
+ }
281
+ ensure
282
+ Thread.current[:system] = false
283
+ end
284
+ end
285
+ #
286
+ # Add a proxy object to the list, return the myid for that object
287
+ #
288
+ def add(proxo)
289
+ @list[@myid_count += 1] = proxo.object_id
290
+ @myid_count
291
+ end
292
+ #
293
+ # Restore a marshalled proxy object to list - myid_count is increased as required.
294
+ # If the object already exists in the system then the existing object must be used.
295
+ #
296
+ def restore(proxo)
297
+ if (@list[proxo.myid])
298
+ proxo = myid2ref(proxo.myid)
299
+ else
300
+ @list[proxo.myid] = proxo.object_id
301
+ @myid_count = proxo.myid if (@myid_count < proxo.myid)
302
+ end
303
+ proxo
304
+ end
305
+ #
306
+ # Returns a reference to the object indicated by the internal id supplied.
307
+ #
308
+ def myid2ref(myid)
309
+ raise "Internal id #{myid} not found" unless objid = @list[myid]
310
+ ObjectSpace._id2ref(objid)
311
+ end
312
+ #
313
+ # Take a snapshot of the system.
314
+ #
315
+ def take_snapshot
316
+ begin
317
+ Thread.current[:system] = self
318
+ Thread.current[:snapshot_memory] = {}
319
+ @persister.take_snapshot
320
+ ensure
321
+ Thread.current[:snapshot_memory] = nil
322
+ Thread.current[:system] = false
323
+ end
324
+ end
325
+ #
326
+ # Returns the hash containing the systems.
327
+ #
328
+ def AutomaticSnapshotMadeleine.systems
329
+ @@systems
330
+ end
331
+ #
332
+ # Close method changes the sysid for Prox objects so they can't be mistaken for real ones in a new
333
+ # system before GC gets them
334
+ #
335
+ def close
336
+ begin
337
+ @list.each_key {|k| myid2ref(k).sysid = nil}
338
+ rescue RangeError
339
+ # do nothing
340
+ end
341
+ @persister.close
342
+ end
343
+
344
+ #
345
+ # Pass on any other calls to the persister
346
+ #
347
+ def method_missing(symbol, *args, &block)
348
+ @persister.send(symbol, *args, &block)
349
+ end
350
+ end
351
+
352
+
353
+ module Deserialize #:nodoc:
354
+ #
355
+ # Detect format of an io stream. Leave it rewound.
356
+ #
357
+ def Deserialize.detect(io)
358
+ c = io.getc
359
+ c1 = io.getc
360
+ io.rewind
361
+ if (c == Marshal::MAJOR_VERSION && c1 <= Marshal::MINOR_VERSION)
362
+ Marshal
363
+ elsif (c == 31 && c1 == 139) # gzip magic numbers
364
+ ZMarshal
365
+ else
366
+ while (s = io.gets)
367
+ break if (s !~ /^\s*$/) # ignore blank lines
368
+ end
369
+ io.rewind
370
+ if (s && s =~ /^\s*<\?[xX][mM][lL]/) # "<?xml" begins an xml serialization
371
+ SOAP::Marshal
372
+ else
373
+ while (s = io.gets)
374
+ break if (s !~ /^\s*#/ && s !~ /^\s*$/) # ignore blank and comment lines
375
+ end
376
+ io.rewind
377
+ if (s && s =~ /^\s*---/) # "---" is the yaml header
378
+ YAML
379
+ else
380
+ nil # failed to detect
381
+ end
382
+ end
383
+ end
384
+ end
385
+ #
386
+ # Try to deserialize object. If there was an error, try to detect marshal format,
387
+ # and return deserialized object using the right marshaller
388
+ # If detection didn't work, raise up the exception
389
+ #
390
+ def Deserialize.load(io, marshaller=Marshal)
391
+ begin
392
+ marshaller.load(io)
393
+ rescue Exception => e
394
+ io.rewind
395
+ detected_marshaller = detect(io)
396
+ if (detected_marshaller == ZMarshal)
397
+ zio = Zlib::GzipReader.new(io)
398
+ detected_zmarshaller = detect(zio)
399
+ zio.finish
400
+ io.rewind
401
+ if (detected_zmarshaller)
402
+ ZMarshal.new(detected_zmarshaller).load(io)
403
+ else
404
+ raise e
405
+ end
406
+ elsif (detected_marshaller)
407
+ detected_marshaller.load(io)
408
+ else
409
+ raise e
410
+ end
411
+ end
412
+ end
413
+ end
414
+
415
+ end
416
+ end
417
+
418
+ AutomaticSnapshotMadeleine = Madeleine::Automatic::AutomaticSnapshotMadeleine