madeleine 0.7.3 → 0.8.0.pre

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