madeleine 0.7.3 → 0.8.0.pre

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.
@@ -1,3 +1,9 @@
1
+ * Ruby 1.9 compatible (requires at least 1.8.7)
2
+ * Dropped the 'automatic' feature (Madeleine::Automatic etc)
3
+ * Dropped support for SOAP marshalling
4
+ * Removed deprecated classes:
5
+ - ClockedSnapshotMadeleine
6
+ - TimeOptimizingLogger
1
7
 
2
8
  Madeleine 0.7.3 (June 11, 2006):
3
9
 
data/README CHANGED
@@ -3,23 +3,11 @@ Madeleine is a Ruby implementation of Object Prevalence: Transparent
3
3
  persistence of business objects using command logging and complete
4
4
  system snapshots.
5
5
 
6
- <http://madeleine.rubyforge.org/>
6
+ https://github.com/ghostganz/madeleine
7
7
 
8
8
  Madeleine's design is based on Prevayler, the original Java
9
9
  prevalence layer.
10
10
 
11
- Learn more about object prevalence at <http://www.prevayler.org/>.
12
-
13
-
14
- Installation:
15
-
16
- Typical installation procedure is:
17
- $ ruby install.rb config
18
- $ ruby install.rb setup
19
- # ruby install.rb install (may require root privilege)
20
- Try 'ruby install.rb --help' for detailed usage.
21
-
22
- [From the documentation of Minero Aoki's 'install.rb']
23
11
 
24
12
  Usage:
25
13
 
@@ -40,23 +28,12 @@ Usage:
40
28
 
41
29
  Requirements:
42
30
 
43
- * Ruby 1.8.1 or later
44
-
45
- Additionaly, some of the sample code also uses ruby/tk.
46
-
47
-
48
- Known problems:
49
-
50
- * Won't run in some Windows-ports of Ruby due to missing
51
- fsync() call.
31
+ * Ruby 1.8.7 or later
52
32
 
53
33
  Contact:
54
34
 
55
35
  Homepage:
56
- <http://madeleine.rubyforge.org/>
57
-
58
- Questions, bug reports, patches, complaints? Use the mailing list:
59
- <http://rubyforge.org/mailman/listinfo/madeleine-devel>
36
+ https://github.com/ghostganz/madeleine
60
37
 
61
38
  License:
62
39
 
@@ -69,8 +46,8 @@ Credits:
69
46
 
70
47
  With the help of patches, testing and feedback from:
71
48
 
72
- Steve Conover, David Heinemeier Hansson, Johan Lind, H�kan R�berg,
73
- IIMA Susumu, Martin Tampe and Jon Tirs�n
49
+ Steve Conover, David Heinemeier Hansson, Johan Lind, Håkan Råberg,
50
+ IIMA Susumu, Martin Tampe and Jon Tirsén
74
51
 
75
52
  Thanks to Klaus Wuestefeld and the Prevayler developers for the
76
53
  model of this software; to Minero Aoki for the installer; to Matz and
@@ -1,8 +1,9 @@
1
+ # -*- coding: utf-8 -*-
1
2
  # Batched writes for Madeleine
2
3
  #
3
- # Copyright(c) H�kan R�berg 2003
4
+ # Copyright(c) Håkan Råberg 2003
5
+ #
4
6
  #
5
- #
6
7
  # This is an experimental implementation of batched log writes to mininize
7
8
  # calls to fsync. It uses a Shared/Exclusive-Lock, implemented in sync.rb,
8
9
  # which is included in Ruby 1.8.
File without changes
@@ -1,6 +1,7 @@
1
1
  #!/usr/local/bin/ruby -w
2
+ # -*- coding: utf-8 -*-
2
3
  #
3
- # Copyright(c) 2003 H�kan R�berg
4
+ # Copyright(c) 2003 Håkan Råberg
4
5
  #
5
6
  # Some components taken from test_persistence.rb
6
7
  # Copyright(c) 2003 Anders Bengtsson
@@ -14,7 +15,7 @@ require 'madeleine/clock'
14
15
 
15
16
 
16
17
  module Madeleine::Batch
17
- class BatchedSnapshotMadeleineTest < Test::Unit::TestCase
18
+ class BatchedSnapshotMadeleineTest < MiniTest::Unit::TestCase
18
19
 
19
20
  class ArraySystem < Array
20
21
  include Madeleine::Clock::ClockedSystem
@@ -121,7 +122,7 @@ module Madeleine::Batch
121
122
  end
122
123
  end
123
124
 
124
- class BatchedLogTest < Test::Unit::TestCase
125
+ class BatchedLogTest < MiniTest::Unit::TestCase
125
126
 
126
127
  class MockMadeleine
127
128
  def initialize(logger)
@@ -236,10 +237,10 @@ def add_batched_tests(suite)
236
237
  end
237
238
 
238
239
  if __FILE__ == $0
239
- suite = Test::Unit::TestSuite.new("BatchedLogTest")
240
+ suite = MiniTest::Unit::TestSuite.new("BatchedLogTest")
240
241
  add_batched_tests(suite)
241
242
 
242
243
  require 'test/unit/ui/console/testrunner'
243
244
  Thread.abort_on_exception = true
244
- Test::Unit::UI::Console::TestRunner.run(suite)
245
+ MiniTest::Unit::UI::Console::TestRunner.run(suite)
245
246
  end
File without changes
File without changes
@@ -9,7 +9,7 @@
9
9
  # require 'madeleine'
10
10
  #
11
11
  # madeleine = SnapshotMadeleine.new("my_example_storage") {
12
- # SomeExampleApplication.new()
12
+ # SomeExampleApplication.new
13
13
  # }
14
14
  #
15
15
  # madeleine.execute_command(command)
@@ -22,8 +22,9 @@ module Madeleine
22
22
  require 'fileutils'
23
23
  require 'madeleine/files'
24
24
  require 'madeleine/sanity'
25
+ require 'madeleine/version'
25
26
 
26
- MADELEINE_VERSION = "0.7.3"
27
+ MADELEINE_VERSION = Madeleine::VERSION
27
28
 
28
29
  class SnapshotMadeleine
29
30
 
@@ -33,7 +34,7 @@ module Madeleine
33
34
  # then be restored from the command logs.
34
35
  #
35
36
  # You can provide your own snapshot marshaller, for instance using
36
- # YAML or SOAP, instead of Ruby's built-in marshaller. The
37
+ # YAML, instead of Ruby's built-in marshaller. The
37
38
  # <tt>snapshot_marshaller</tt> must respond to
38
39
  # <tt>load(stream)</tt> and <tt>dump(object, stream)</tt>. You
39
40
  # must use the same marshaller every time for a system.
@@ -87,11 +88,11 @@ module Madeleine
87
88
  # * <tt>command</tt> - The command to execute on the system.
88
89
  def execute_command(command)
89
90
  verify_command_sane(command)
90
- @lock.synchronize {
91
+ @lock.synchronize do
91
92
  raise MadeleineClosedException if @closed
92
93
  @logger.store(command)
93
94
  @executer.execute(command)
94
- }
95
+ end
95
96
  end
96
97
 
97
98
  # Execute a query on the prevalent system.
@@ -102,9 +103,9 @@ module Madeleine
102
103
  #
103
104
  # * <tt>query</tt> - The query command to execute
104
105
  def execute_query(query)
105
- @lock.synchronize_shared {
106
+ @lock.synchronize_shared do
106
107
  @executer.execute(query)
107
- }
108
+ end
108
109
  end
109
110
 
110
111
  # Take a snapshot of the current system.
@@ -123,11 +124,11 @@ module Madeleine
123
124
  # end
124
125
  # }
125
126
  def take_snapshot
126
- @lock.synchronize {
127
+ @lock.synchronize do
127
128
  @logger.close
128
129
  @snapshotter.take(@system)
129
130
  @logger.reset
130
- }
131
+ end
131
132
  end
132
133
 
133
134
  # Close the system.
@@ -135,10 +136,10 @@ module Madeleine
135
136
  # The log file is closed and no new commands can be received
136
137
  # by this Madeleine.
137
138
  def close
138
- @lock.synchronize {
139
+ @lock.synchronize do
139
140
  @logger.close
140
141
  @closed = true
141
- }
142
+ end
142
143
  end
143
144
 
144
145
  private
@@ -213,9 +214,9 @@ module Madeleine
213
214
  id = SnapshotFile.highest_id(@directory_name)
214
215
  if id > 0
215
216
  snapshot_file = SnapshotFile.new(@directory_name, id).name
216
- open(snapshot_file, "rb") {|snapshot|
217
+ open(snapshot_file, "rb") do |snapshot|
217
218
  system = @marshaller.load(snapshot)
218
- }
219
+ end
219
220
  else
220
221
  system = new_system_block.call
221
222
  end
@@ -223,19 +224,19 @@ module Madeleine
223
224
  end
224
225
 
225
226
  def recover_logs(executer)
226
- executer.recovery {
227
- CommandLog.log_file_names(@directory_name, FileService.new).each {|file_name|
228
- open(@directory_name + File::SEPARATOR + file_name, "rb") {|log|
227
+ executer.recovery do
228
+ CommandLog.log_file_names(@directory_name, FileService.new).each do |file_name|
229
+ open(@directory_name + File::SEPARATOR + file_name, "rb") do |log|
229
230
  recover_log(executer, log)
230
- }
231
- }
232
- }
231
+ end
232
+ end
233
+ end
233
234
  end
234
235
 
235
236
  private
236
237
 
237
238
  def recover_log(executer, log)
238
- while ! log.eof?
239
+ until log.eof?
239
240
  command = Marshal.load(log)
240
241
  executer.execute(command)
241
242
  end
@@ -264,9 +265,10 @@ module Madeleine
264
265
  result = file_service.dir_entries(directory_name).select {|name|
265
266
  name =~ /^\d{#{FILE_COUNTER_SIZE}}\.command_log$/
266
267
  }
267
- result.each {|name| name.untaint }
268
- result.sort!
269
- result
268
+ result.each do |name|
269
+ name.untaint
270
+ end
271
+ result.sort
270
272
  end
271
273
 
272
274
  def initialize(path, file_service)
@@ -287,13 +289,13 @@ module Madeleine
287
289
 
288
290
  def self.highest_log(directory_name, file_service)
289
291
  highest = 0
290
- log_file_names(directory_name, file_service).each {|file_name|
292
+ log_file_names(directory_name, file_service).each do |file_name|
291
293
  match = /^(\d{#{FILE_COUNTER_SIZE}})/.match(file_name)
292
294
  n = match[1].to_i
293
295
  if n > highest
294
296
  highest = n
295
297
  end
296
- }
298
+ end
297
299
  highest
298
300
  end
299
301
  end
@@ -315,7 +317,7 @@ module Madeleine
315
317
  end
316
318
 
317
319
  def ensure_directory_exists
318
- if ! File.exist?(@directory_name)
320
+ unless File.exist?(@directory_name)
319
321
  FileUtils.mkpath(@directory_name)
320
322
  end
321
323
  end
@@ -353,10 +355,11 @@ module Madeleine
353
355
  private
354
356
 
355
357
  def delete_log_files
356
- Dir.glob(@directory_name + File::SEPARATOR + "*.command_log").each {|name|
358
+ names = Dir.glob(@directory_name + File::SEPARATOR + "*.command_log")
359
+ names.each do |name|
357
360
  name.untaint
358
361
  File.delete(name)
359
- }
362
+ end
360
363
  end
361
364
 
362
365
  def open_new_log
@@ -370,14 +373,14 @@ module Madeleine
370
373
  return 0 unless File.exist?(directory_name)
371
374
  suffix = "snapshot"
372
375
  highest = 0
373
- Dir.foreach(directory_name) {|file_name|
376
+ Dir.foreach(directory_name) do |file_name|
374
377
  match = /^(\d{#{FILE_COUNTER_SIZE}}\.#{suffix}$)/.match(file_name)
375
378
  next unless match
376
379
  n = match[1].to_i
377
380
  if n > highest
378
381
  highest = n
379
382
  end
380
- }
383
+ end
381
384
  highest
382
385
  end
383
386
 
@@ -399,12 +402,12 @@ module Madeleine
399
402
  def take(system)
400
403
  numbered_file = SnapshotFile.next(@directory_name)
401
404
  name = numbered_file.name
402
- open(name + '.tmp', 'wb') {|snapshot|
405
+ open("#{name}.tmp", 'wb') do |snapshot|
403
406
  @marshaller.dump(system, snapshot)
404
407
  snapshot.flush
405
408
  snapshot.fsync
406
- }
407
- File.rename(name + '.tmp', name)
409
+ end
410
+ File.rename("#{name}.tmp", name)
408
411
  end
409
412
  end
410
413
 
@@ -423,4 +426,3 @@ module Madeleine
423
426
  end
424
427
 
425
428
  SnapshotMadeleine = Madeleine::SnapshotMadeleine
426
-
@@ -7,10 +7,6 @@ require 'madeleine'
7
7
  module Madeleine
8
8
  module Clock
9
9
 
10
- # Deprecated. Use SnapshotMadeleine instead.
11
- class ClockedSnapshotMadeleine < ::Madeleine::SnapshotMadeleine # :nodoc:
12
- end
13
-
14
10
  # Let your system extend this module if you need to access the
15
11
  # machine time. Used together with a TimeActor that keeps
16
12
  # the clock current.
@@ -53,12 +49,12 @@ module Madeleine
53
49
  @madeleine = madeleine
54
50
  @is_destroyed = false
55
51
  send_tick
56
- @thread = Thread.new {
52
+ @thread = Thread.new do
57
53
  until @is_destroyed
58
54
  sleep(delay)
59
55
  send_tick
60
56
  end
61
- }
57
+ end
62
58
  end
63
59
 
64
60
  def send_tick
@@ -75,20 +71,9 @@ module Madeleine
75
71
  @time = Time.at(0)
76
72
  end
77
73
 
78
- def forward_to(newTime)
79
- @time = newTime
74
+ def forward_to(new_time)
75
+ @time = new_time
80
76
  end
81
77
  end
82
-
83
- #
84
- # Internal classes below
85
- #
86
-
87
- # Deprecated. Merged into default implementation.
88
- class TimeOptimizingLogger < ::Madeleine::Logger # :nodoc:
89
- end
90
-
91
78
  end
92
79
  end
93
-
94
- ClockedSnapshotMadeleine = Madeleine::Clock::ClockedSnapshotMadeleine
@@ -0,0 +1,3 @@
1
+ module Madeleine
2
+ VERSION = "0.8.0.pre"
3
+ end
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Anders Bengtsson <ndrsbngtssn@yahoo.se>
3
- # Copyright:: Copyright (c) 2004
3
+ # Copyright:: Copyright (c) 2004-2012
4
4
  #
5
5
 
6
6
  require 'zlib'
@@ -14,8 +14,7 @@ module Madeleine
14
14
  #
15
15
  # Uses <tt>zlib</tt> to do on-the-fly compression/decompression.
16
16
  #
17
- # ZMarshal works with Ruby's own Marshal and YAML, but not with SOAP
18
- # marshalling.
17
+ # ZMarshal works with Ruby's own Marshal and YAML
19
18
  #
20
19
  # Usage:
21
20
  #
@@ -24,7 +23,7 @@ module Madeleine
24
23
  #
25
24
  # marshaller = Madeleine::ZMarshal.new(YAML)
26
25
  # madeleine = SnapshotMadeleine.new("my_example_storage", marshaller) {
27
- # SomeExampleApplication.new()
26
+ # SomeExampleApplication.new
28
27
  # }
29
28
  #
30
29
  class ZMarshal
@@ -34,14 +33,9 @@ module Madeleine
34
33
  end
35
34
 
36
35
  def load(stream)
37
- zstream = Zlib::GzipReader.new(stream)
36
+ zstream = WorkaroundGzipReader.new(stream)
38
37
  begin
39
- # Buffer into a string first, since GzipReader can't handle
40
- # Marshal's 0-sized reads and SOAP can't handle streams at all.
41
- # In a bright future we can revert to reading directly from the
42
- # stream again.
43
- buffer = zstream.read
44
- return @marshaller.load(buffer)
38
+ return @marshaller.load(zstream)
45
39
  ensure
46
40
  zstream.finish
47
41
  end
@@ -56,5 +50,18 @@ module Madeleine
56
50
  end
57
51
  nil
58
52
  end
53
+
54
+ private
55
+
56
+ class WorkaroundGzipReader < Zlib::GzipReader
57
+ # The 'psych' YAML parser, default since Ruby 1.9.3,
58
+ # assumes that its input IO has an external_encoding()
59
+ # method.
60
+ unless defined? external_encoding
61
+ def external_encoding
62
+ nil
63
+ end
64
+ end
65
+ end
59
66
  end
60
67
  end
metadata CHANGED
@@ -1,63 +1,111 @@
1
- --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.11
3
- specification_version: 1
1
+ --- !ruby/object:Gem::Specification
4
2
  name: madeleine
5
- version: !ruby/object:Gem::Version
6
- version: 0.7.3
7
- date: 2006-06-11 00:00:00 +02:00
8
- summary: Madeleine is a Ruby implementation of Object Prevalence
9
- require_paths:
10
- - lib
11
- email: ndrsbngtssn@yahoo.se
12
- homepage: http://madeleine.rubyforge.org
13
- rubyforge_project:
14
- description:
15
- autorequire: madeleine
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: false
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.8.1
24
- version:
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0.pre
5
+ prerelease: 6
25
6
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- authors:
7
+ authors:
29
8
  - Anders Bengtsson
30
- files:
31
- - lib/madeleine.rb
32
- - lib/madeleine/automatic.rb
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 4.3.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 4.3.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rdoc
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Transparent persistence of system state using logging and snapshots
63
+ email: ndrsbngtssn@yahoo.se
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
33
68
  - lib/madeleine/clock.rb
34
69
  - lib/madeleine/files.rb
35
70
  - lib/madeleine/sanity.rb
71
+ - lib/madeleine/version.rb
36
72
  - lib/madeleine/zmarshal.rb
37
- - samples/dictionary_client.rb
73
+ - lib/madeleine.rb
38
74
  - samples/clock_click.rb
75
+ - samples/dictionary_client.rb
39
76
  - samples/dictionary_server.rb
40
77
  - samples/painter.rb
41
- - contrib/test_batched.rb
42
78
  - contrib/batched.rb
43
79
  - contrib/benchmark.rb
44
- - contrib/threaded_benchmark.rb
45
80
  - contrib/create_command.rb
81
+ - contrib/test_batched.rb
46
82
  - contrib/test_scalability.rb
83
+ - contrib/threaded_benchmark.rb
47
84
  - README
48
- - NEWS
85
+ - CHANGES.txt
49
86
  - COPYING
50
- test_files: []
51
-
87
+ homepage: http://github.com/ghostganz/madeleine
88
+ licenses: []
89
+ post_install_message:
52
90
  rdoc_options: []
53
-
54
- extra_rdoc_files: []
55
-
56
- executables: []
57
-
58
- extensions: []
59
-
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: 1.8.7
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>'
103
+ - !ruby/object:Gem::Version
104
+ version: 1.3.1
60
105
  requirements: []
61
-
62
- dependencies: []
63
-
106
+ rubyforge_project:
107
+ rubygems_version: 1.8.24
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Madeleine is a Ruby implementation of Object Prevalence
111
+ test_files: []
@@ -1,429 +0,0 @@
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-2006
11
- # Version:: 0.42
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.system.foo # not logged
56
- # print mad.system.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
- @read_only_methods ||= []
80
- Prox.new(_old_new(*args, &block))
81
- end
82
- #
83
- # Called when a method added - remember symbol if read only
84
- # This is a good place to add in any superclass's read only methods also
85
- #
86
- def method_added(symbol)
87
- self.instance_eval {
88
- @read_only_methods ||= []
89
- @auto_read_only_flag ||= false
90
- @read_only_methods << symbol if @auto_read_only_flag
91
- c = self
92
- while (c = c.superclass)
93
- if (c.instance_eval {instance_variables.include? "@read_only_methods"})
94
- @read_only_methods |= c.instance_eval {@read_only_methods}
95
- end
96
- end
97
- }
98
- end
99
- #
100
- # Set the read only flag, or add read only methods
101
- #
102
- def automatic_read_only(*list)
103
- if (list == [])
104
- self.instance_eval {@auto_read_only_flag = true}
105
- else
106
- list.each {|s| self.instance_eval {@read_only_methods ||= []; @read_only_methods << s}}
107
- end
108
- end
109
- #
110
- # Clear the read only flag, or remove read only methods
111
- #
112
- def automatic_read_write(*list)
113
- if (list == [])
114
- self.instance_eval {@auto_read_only_flag = false}
115
- else
116
- list.each {|s| self.instance_eval {@read_only_methods ||= []; @read_only_methods.delete(s)}}
117
- end
118
- end
119
-
120
- end
121
- end
122
- #
123
- # Return the list of read only methods so Automatic_proxy#method_missing can find what to and what not to make into a command
124
- #
125
- def read_only_methods
126
- self.class.instance_eval {@read_only_methods}
127
- end
128
- end
129
-
130
- #
131
- # A Command object is automatically created for each method call to an object within the system that comes from without.
132
- # These objects are recorded in the log by Madeleine.
133
- #
134
- class Command
135
- def initialize(symbol, myid, *args)
136
- @symbol = symbol
137
- @myid = myid
138
- @args = args
139
- end
140
- #
141
- # Called by madeleine when the command is done either first time, or when restoring the log
142
- #
143
- def execute(system)
144
- Thread.current[:system].myid2ref(@myid).thing.send(@symbol, *@args)
145
- end
146
- end
147
- #
148
- # This is a little class to pass to SnapshotMadeleine. This is used for snapshots only.
149
- # It acts as the marshaller, and just passes marshalling requests on to the user specified
150
- # marshaller. This defaults to Marshal, but could be YAML or another.
151
- # After we have done a restore, the ObjectSpace is searched for instances of Prox to
152
- # add new objects to the list in AutomaticSnapshotMadeleine
153
- #
154
- class Automatic_marshaller #:nodoc:
155
- def Automatic_marshaller.load(io)
156
- restored_obj = Deserialize.load(io, Thread.current[:system].marshaller)
157
- p restored_obj if restored_obj.class != Prox
158
- ObjectSpace.each_object(Prox) {|o| Thread.current[:system].restore(o) if (o.sysid == restored_obj.sysid)}
159
- restored_obj
160
- end
161
- def Automatic_marshaller.dump(obj, io = nil)
162
- Thread.current[:system].marshaller.dump(obj, io)
163
- end
164
- end
165
- #
166
- # A Prox object is generated and returned by Interceptor each time a system object is created.
167
- #
168
- class Prox #:nodoc:
169
- attr_accessor :thing, :myid, :sysid
170
-
171
- def initialize(thing)
172
- if (thing)
173
- raise "App object created outside of app" unless Thread.current[:system]
174
- @sysid = Thread.current[:system].sysid
175
- @myid = Thread.current[:system].add(self)
176
- @thing = thing
177
- end
178
- end
179
- #
180
- # This automatically makes and executes a new Command if a method is called from
181
- # outside the system.
182
- #
183
- def method_missing(symbol, *args, &block)
184
- # print "Sending #{symbol} to #{@thing.to_s}, myid=#{@myid}, sysid=#{@sysid}\n"
185
- raise NoMethodError, "Undefined method" unless @thing.respond_to?(symbol)
186
- if (Thread.current[:system])
187
- @thing.send(symbol, *args, &block)
188
- else
189
- raise "Cannot make command with block" if block_given?
190
- Thread.current[:system] = AutomaticSnapshotMadeleine.systems[@sysid]
191
- begin
192
- if (@thing.read_only_methods.include?(symbol))
193
- result = Thread.current[:system].execute_query(Command.new(symbol, @myid, *args))
194
- else
195
- result = Thread.current[:system].execute_command(Command.new(symbol, @myid, *args))
196
- end
197
- ensure
198
- Thread.current[:system] = false
199
- end
200
- result
201
- end
202
- end
203
- #
204
- # Custom marshalling - this adds the internal id (myid) and the system id to a marshal
205
- # of the object we are the proxy for.
206
- # We take care to not marshal the same object twice, so circular references will work.
207
- # We ignore Thread.current[:system].marshaller here - this is only called by Marshal, and
208
- # marshal is always used for Command objects
209
- #
210
- def _dump(depth)
211
- if (Thread.current[:snapshot_memory])
212
- if (Thread.current[:snapshot_memory][self])
213
- [@myid.to_s, @sysid].pack("A8A30")
214
- else
215
- Thread.current[:snapshot_memory][self] = true
216
- [@myid.to_s, @sysid].pack("A8A30") + Marshal.dump(@thing, depth)
217
- end
218
- else
219
- [@myid.to_s, @sysid].pack("A8A30") # never marshal a prox object in a command, just ref
220
- end
221
- end
222
-
223
- #
224
- # Custom marshalling for Marshal - restore a Prox object.
225
- #
226
- def Prox._load(str)
227
- x = Prox.new(nil)
228
- a = str.unpack("A8A30a*")
229
- x.myid = a[0].to_i
230
- x.sysid = a[1]
231
- x = Thread.current[:system].restore(x)
232
- x.thing = Marshal.load(a[2]) if (a[2] > "")
233
- x
234
- end
235
-
236
- end
237
-
238
- #
239
- # The AutomaticSnapshotMadeleine class contains an instance of the persister
240
- # (default is SnapshotMadeleine) and provides additional automatic functionality.
241
- #
242
- # The class is instantiated the same way as SnapshotMadeleine:
243
- # madeleine_sys = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
244
- # The second initialisation parameter is the persister. Supported persisters are:
245
- #
246
- # * Marshal (default)
247
- # * YAML
248
- # * SOAP::Marshal
249
- # * Madeleine::ZMarshal.new(Marshal)
250
- # * Madeleine::ZMarshal.new(YAML)
251
- # * Madeleine::ZMarshal.new(SOAP::Marshal)
252
- #
253
- # The class keeps a record of all the systems that currently exist.
254
- # Each instance of the class keeps a record of Prox objects in that system by internal id (myid).
255
- #
256
- # We also add functionality to take_snapshot in order to set things up so that the custom Prox object
257
- # marshalling will work correctly.
258
- #
259
- class AutomaticSnapshotMadeleine
260
- attr_accessor :marshaller
261
- attr_reader :list, :sysid
262
-
263
- def initialize(directory_name, marshaller=Marshal, persister=SnapshotMadeleine, &new_system_block)
264
- @sysid ||= Time.now.to_f.to_s + Thread.current.object_id.to_s # Gererate a new sysid
265
- @myid_count = 0
266
- @list = {}
267
- Thread.current[:system] = self # during system startup system should not create commands
268
- Thread.critical = true
269
- @@systems ||= {} # holds systems by sysid
270
- @@systems[@sysid] = self
271
- Thread.critical = false
272
- @marshaller = marshaller # until attrb
273
-
274
- begin
275
- @persister = persister.new(directory_name, Automatic_marshaller, &new_system_block)
276
- @list.delete_if {|k,v| # set all the prox objects that now exist to have the right sysid
277
- begin
278
- obj = ObjectSpace._id2ref(v)
279
- raise unless obj.respond_to?(:sysid=)
280
- obj.sysid = @sysid
281
- false
282
- rescue RangeError
283
- true # Id was to a GC'd object, delete it
284
- rescue RuntimeError
285
- true # GC'd object, and id was reused for something else
286
- end
287
- }
288
- ensure
289
- Thread.current[:system] = false
290
- end
291
- end
292
- #
293
- # Add a proxy object to the list, return the myid for that object
294
- #
295
- def add(proxo)
296
- @list[@myid_count += 1] = proxo.object_id
297
- @myid_count
298
- end
299
- #
300
- # Restore a marshalled proxy object to list - myid_count is increased as required.
301
- # If the object already exists in the system then the existing object must be used.
302
- #
303
- def restore(proxo)
304
- if (@list[proxo.myid])
305
- proxo = myid2ref(proxo.myid)
306
- else
307
- @list[proxo.myid] = proxo.object_id
308
- @myid_count = proxo.myid if (@myid_count < proxo.myid)
309
- end
310
- proxo
311
- end
312
- #
313
- # Returns a reference to the object indicated by the internal id supplied.
314
- #
315
- def myid2ref(myid)
316
- raise "Internal id #{myid} not found" unless objid = @list[myid]
317
- ObjectSpace._id2ref(objid)
318
- end
319
- #
320
- # Take a snapshot of the system.
321
- #
322
- def take_snapshot
323
- begin
324
- Thread.current[:system] = self
325
- Thread.current[:snapshot_memory] = {}
326
- @persister.take_snapshot
327
- ensure
328
- Thread.current[:snapshot_memory] = nil
329
- Thread.current[:system] = false
330
- end
331
- end
332
- #
333
- # Returns the hash containing the systems.
334
- #
335
- def AutomaticSnapshotMadeleine.systems
336
- @@systems
337
- end
338
- #
339
- # Close method changes the sysid for Prox objects so they can't be mistaken for real ones in a new
340
- # system before GC gets them
341
- #
342
- def close
343
- begin
344
- @list.each_key {|k|
345
- ref = myid2ref(k)
346
- ref.sysid = nil if ref.class == Prox
347
- }
348
- rescue RangeError
349
- # do nothing
350
- end
351
- @persister.close
352
- end
353
-
354
- #
355
- # Pass on any other calls to the persister
356
- #
357
- def method_missing(symbol, *args, &block)
358
- @persister.send(symbol, *args, &block)
359
- end
360
- end
361
-
362
-
363
- module Deserialize #:nodoc:
364
- #
365
- # Detect format of an io stream. Leave it rewound.
366
- #
367
- def Deserialize.detect(io)
368
- c = io.getc
369
- c1 = io.getc
370
- io.rewind
371
- if (c == Marshal::MAJOR_VERSION && c1 <= Marshal::MINOR_VERSION)
372
- Marshal
373
- elsif (c == 31 && c1 == 139) # gzip magic numbers
374
- ZMarshal
375
- else
376
- while (s = io.gets)
377
- break if (s !~ /^\s*$/) # ignore blank lines
378
- end
379
- io.rewind
380
- if (s && s =~ /^\s*<\?[xX][mM][lL]/) # "<?xml" begins an xml serialization
381
- SOAP::Marshal
382
- else
383
- while (s = io.gets)
384
- break if (s !~ /^\s*#/ && s !~ /^\s*$/) # ignore blank and comment lines
385
- end
386
- io.rewind
387
- if (s && s =~ /^\s*---/) # "---" is the yaml header
388
- YAML
389
- else
390
- nil # failed to detect
391
- end
392
- end
393
- end
394
- end
395
- #
396
- # Try to deserialize object. If there was an error, try to detect marshal format,
397
- # and return deserialized object using the right marshaller
398
- # If detection didn't work, raise up the exception
399
- #
400
- def Deserialize.load(io, marshaller=Marshal)
401
- begin
402
- raise "Must detect with YAML" if marshaller == YAML
403
- marshaller.load(io)
404
- rescue Exception => e
405
- io.rewind
406
- detected_marshaller = detect(io)
407
- if (detected_marshaller == ZMarshal)
408
- zio = Zlib::GzipReader.new(io)
409
- detected_zmarshaller = detect(zio)
410
- zio.finish
411
- io.rewind
412
- if (detected_zmarshaller)
413
- ZMarshal.new(detected_zmarshaller).load(io)
414
- else
415
- raise e
416
- end
417
- elsif (detected_marshaller)
418
- detected_marshaller.load(io)
419
- else
420
- raise e
421
- end
422
- end
423
- end
424
- end
425
-
426
- end
427
- end
428
-
429
- AutomaticSnapshotMadeleine = Madeleine::Automatic::AutomaticSnapshotMadeleine