Pimki 1.3.092 → 1.4.092

Sign up to get free protection for your applications and to get access to all the features.
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