madeleine 0.7.1
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.
- data/COPYING +31 -0
- data/NEWS +55 -0
- data/README +78 -0
- data/contrib/batched.rb +298 -0
- data/contrib/benchmark.rb +35 -0
- data/contrib/test_batched.rb +245 -0
- data/contrib/test_scalability.rb +248 -0
- data/contrib/threaded_benchmark.rb +44 -0
- data/lib/madeleine.rb +419 -0
- data/lib/madeleine/automatic.rb +418 -0
- data/lib/madeleine/clock.rb +94 -0
- data/lib/madeleine/files.rb +19 -0
- data/lib/madeleine/zmarshal.rb +60 -0
- data/samples/clock_click.rb +73 -0
- data/samples/dictionary_client.rb +23 -0
- data/samples/dictionary_server.rb +94 -0
- data/samples/painter.rb +60 -0
- metadata +53 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# Wrapper for Ruby's file services, replaced during testing
|
3
|
+
# so we can run tests without touching a real filesystem.
|
4
|
+
#
|
5
|
+
|
6
|
+
class FileService
|
7
|
+
|
8
|
+
def open(*args)
|
9
|
+
super(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def exist?(name)
|
13
|
+
File.exist?(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def dir_entries(name)
|
17
|
+
Dir.entries(name)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Anders Bengtsson <ndrsbngtssn@yahoo.se>
|
3
|
+
# Copyright:: Copyright (c) 2004
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
module Madeleine
|
9
|
+
#
|
10
|
+
# Snapshot marshaller for compressed snapshots.
|
11
|
+
#
|
12
|
+
# Compresses the snapshots created by another marshaller. Uses either
|
13
|
+
# Marshal (the default) or another supplied marshaller.
|
14
|
+
#
|
15
|
+
# Uses <tt>zlib</tt> to do on-the-fly compression/decompression.
|
16
|
+
#
|
17
|
+
# ZMarshal works with Ruby's own Marshal and YAML, but not with SOAP
|
18
|
+
# marshalling.
|
19
|
+
#
|
20
|
+
# Usage:
|
21
|
+
#
|
22
|
+
# require 'madeleine'
|
23
|
+
# require 'madeleine/zmarshal'
|
24
|
+
#
|
25
|
+
# marshaller = Madeleine::ZMarshal.new(YAML)
|
26
|
+
# madeleine = SnapshotMadeleine.new("my_example_storage", marshaller) {
|
27
|
+
# SomeExampleApplication.new()
|
28
|
+
# }
|
29
|
+
#
|
30
|
+
class ZMarshal
|
31
|
+
|
32
|
+
def initialize(marshaller=Marshal)
|
33
|
+
@marshaller = marshaller
|
34
|
+
end
|
35
|
+
|
36
|
+
def load(stream)
|
37
|
+
zstream = Zlib::GzipReader.new(stream)
|
38
|
+
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)
|
45
|
+
ensure
|
46
|
+
zstream.finish
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def dump(system, stream)
|
51
|
+
zstream = Zlib::GzipWriter.new(stream)
|
52
|
+
begin
|
53
|
+
@marshaller.dump(system, zstream)
|
54
|
+
ensure
|
55
|
+
zstream.finish
|
56
|
+
end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#
|
2
|
+
# Simple example of using time with Madeleine.
|
3
|
+
#
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(".." + File::SEPARATOR + "lib")
|
6
|
+
|
7
|
+
require 'madeleine/clock'
|
8
|
+
require 'tk'
|
9
|
+
|
10
|
+
# The Clicker keeps track of when it was last clicked.
|
11
|
+
#
|
12
|
+
# To access the time it extends ClockedSystem, which provides
|
13
|
+
# it with the 'clock' attribute.
|
14
|
+
#
|
15
|
+
class Clicker
|
16
|
+
include Madeleine::Clock::ClockedSystem
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@last_clicked = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def click
|
23
|
+
@last_clicked = clock.time
|
24
|
+
end
|
25
|
+
|
26
|
+
def last_clicked
|
27
|
+
return '-' if @last_clicked.nil?
|
28
|
+
@last_clicked.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# A command to update the Clicker with.
|
33
|
+
#
|
34
|
+
class Click
|
35
|
+
def execute(system)
|
36
|
+
system.click
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Launch a ClockedSnapshotMadeleine.
|
41
|
+
#
|
42
|
+
# ClockedSnapshotMadeleine works like the regular SnapshotMadeleine, but
|
43
|
+
# optimizes away redundant commands from TimeActor.
|
44
|
+
#
|
45
|
+
madeleine = ClockedSnapshotMadeleine.new("clock-demo") { Clicker.new }
|
46
|
+
|
47
|
+
# Launch the TimeActor.
|
48
|
+
#
|
49
|
+
# This provides time commands, without which the system's time would stand still.
|
50
|
+
#
|
51
|
+
Madeleine::Clock::TimeActor.launch(madeleine)
|
52
|
+
|
53
|
+
clicker = madeleine.system
|
54
|
+
|
55
|
+
# The GUI
|
56
|
+
|
57
|
+
root = TkRoot.new() { title "Madeleine Clock Example" }
|
58
|
+
label = TkLabel.new(root) {
|
59
|
+
text "Last clicked " + clicker.last_clicked
|
60
|
+
width 40
|
61
|
+
pack
|
62
|
+
}
|
63
|
+
button = TkButton.new(root) {
|
64
|
+
text 'Click'
|
65
|
+
command proc {
|
66
|
+
madeleine.execute_command(Click.new)
|
67
|
+
label.text("Last clicked " + clicker.last_clicked)
|
68
|
+
}
|
69
|
+
pack
|
70
|
+
}
|
71
|
+
|
72
|
+
Tk.mainloop
|
73
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# Dictionary client
|
3
|
+
#
|
4
|
+
# See dictionary_server.rb for details
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'drb'
|
8
|
+
|
9
|
+
DRb.start_service
|
10
|
+
dictionary = DRbObject.new(nil, "druby://localhost:1234")
|
11
|
+
|
12
|
+
if ARGV.length == 1
|
13
|
+
puts dictionary.lookup(ARGV[0])
|
14
|
+
elsif ARGV.length == 2
|
15
|
+
dictionary.add(ARGV[0], ARGV[1])
|
16
|
+
puts "Stored"
|
17
|
+
else
|
18
|
+
puts "Usage: dictionary_client <key> [<value>]"
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
#
|
2
|
+
# A dictionary server using Distributed Ruby (DRb).
|
3
|
+
#
|
4
|
+
# All modifications to the dictionary are done as commands,
|
5
|
+
# while read-only queries (i.e 'lookup') are done directly.
|
6
|
+
#
|
7
|
+
# First launch this server in the background, then use
|
8
|
+
# dictionary_client.rb to look up and add items to the
|
9
|
+
# dictionary.
|
10
|
+
# You can kill the server at any time. The contents of the
|
11
|
+
# dictionary will still be there when you restart it.
|
12
|
+
#
|
13
|
+
# DRb is available at http://raa.ruby-lang.org/list.rhtml?name=druby
|
14
|
+
#
|
15
|
+
|
16
|
+
$LOAD_PATH.unshift(".." + File::SEPARATOR + "lib")
|
17
|
+
require 'madeleine'
|
18
|
+
|
19
|
+
require 'drb'
|
20
|
+
|
21
|
+
|
22
|
+
class Dictionary
|
23
|
+
def initialize
|
24
|
+
@data = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def add(key, value)
|
28
|
+
@data[key] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def lookup(key)
|
32
|
+
@data[key]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
class Addition
|
38
|
+
def initialize(key, value)
|
39
|
+
@key, @value = key, value
|
40
|
+
end
|
41
|
+
|
42
|
+
def execute(system)
|
43
|
+
system.add(@key, @value)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
class Lookup
|
49
|
+
def initialize(key)
|
50
|
+
@key = key
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute(system)
|
54
|
+
system.lookup(@key)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
class DictionaryServer
|
60
|
+
|
61
|
+
def initialize(madeleine)
|
62
|
+
@madeleine = madeleine
|
63
|
+
@dictionary = madeleine.system
|
64
|
+
end
|
65
|
+
|
66
|
+
def add(key, value)
|
67
|
+
# When adding a new key-value pair we modify the system, so
|
68
|
+
# this operation has to be done through a command.
|
69
|
+
@madeleine.execute_command(Addition.new(key, value))
|
70
|
+
end
|
71
|
+
|
72
|
+
def lookup(key)
|
73
|
+
# A lookup is a read-only operation, so we can do it as a non-logged
|
74
|
+
# query. If we weren't worried about concurrency problems we could
|
75
|
+
# have just called @dictionary.lookup(key) directly instead.
|
76
|
+
@madeleine.execute_query(Lookup.new(key))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
madeleine = SnapshotMadeleine.new("dictionary-base") { Dictionary.new }
|
82
|
+
|
83
|
+
Thread.new(madeleine) {
|
84
|
+
puts "Taking snapshot every 30 seconds."
|
85
|
+
while true
|
86
|
+
sleep(30)
|
87
|
+
madeleine.take_snapshot
|
88
|
+
end
|
89
|
+
}
|
90
|
+
|
91
|
+
DRb.start_service("druby://localhost:1234",
|
92
|
+
DictionaryServer.new(madeleine))
|
93
|
+
DRb.thread.join
|
94
|
+
|
data/samples/painter.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Simple drawing program to show Madeleine's logging feature.
|
3
|
+
#
|
4
|
+
# When you restart the program, your old artwork is still there.
|
5
|
+
#
|
6
|
+
# (Note: The GUI components used here aren't marshal-able,
|
7
|
+
# so in a real app you would have to do custom marshaling for
|
8
|
+
# the Painter class to get working snapshots. Then again, in a real
|
9
|
+
# app you wouldn't use the GUI components to hold the app's data,
|
10
|
+
# would you?)
|
11
|
+
#
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(".." + File::SEPARATOR + "lib")
|
14
|
+
|
15
|
+
require 'madeleine'
|
16
|
+
|
17
|
+
require 'tkclass'
|
18
|
+
|
19
|
+
class Painter
|
20
|
+
|
21
|
+
def initialize(canvas)
|
22
|
+
@canvas = canvas
|
23
|
+
end
|
24
|
+
|
25
|
+
def draw(x, y)
|
26
|
+
line = Line.new(@canvas, x, y, x + 1, y + 1)
|
27
|
+
line.fill('black')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class PaintCommand
|
32
|
+
|
33
|
+
def initialize(x, y)
|
34
|
+
@x, @y = x, y
|
35
|
+
end
|
36
|
+
|
37
|
+
def execute(system)
|
38
|
+
system.draw(@x, @y)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
root = TkRoot.new() { title "Madeleine Painter" }
|
43
|
+
canvas = Canvas.new(root)
|
44
|
+
canvas.pack
|
45
|
+
|
46
|
+
$madeleine = Madeleine::SnapshotMadeleine.new("painter-demo") { Painter.new(canvas) }
|
47
|
+
|
48
|
+
canvas.bind("1",
|
49
|
+
proc {|x, y|
|
50
|
+
$madeleine.execute_command(PaintCommand.new(x, y))
|
51
|
+
},
|
52
|
+
"%x %y")
|
53
|
+
canvas.bind("B1-Motion",
|
54
|
+
proc {|x, y|
|
55
|
+
$madeleine.execute_command(PaintCommand.new(x, y))
|
56
|
+
},
|
57
|
+
"%x %y")
|
58
|
+
|
59
|
+
Tk.mainloop
|
60
|
+
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.4
|
3
|
+
specification_version: 1
|
4
|
+
name: madeleine
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.7.1
|
7
|
+
date: 2005-01-06
|
8
|
+
summary: Madeleine is a Ruby implementation of Object Prevalence
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: ndrsbngtssn@yahoo.se
|
12
|
+
homepage: http://madeleine.sourceforge.net
|
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
|
+
- ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.8.1
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
authors:
|
28
|
+
- Anders Bengtsson
|
29
|
+
files:
|
30
|
+
- lib/madeleine.rb
|
31
|
+
- lib/madeleine/automatic.rb
|
32
|
+
- lib/madeleine/clock.rb
|
33
|
+
- lib/madeleine/files.rb
|
34
|
+
- lib/madeleine/zmarshal.rb
|
35
|
+
- samples/clock_click.rb
|
36
|
+
- samples/dictionary_client.rb
|
37
|
+
- samples/dictionary_server.rb
|
38
|
+
- samples/painter.rb
|
39
|
+
- contrib/batched.rb
|
40
|
+
- contrib/benchmark.rb
|
41
|
+
- contrib/test_batched.rb
|
42
|
+
- contrib/test_scalability.rb
|
43
|
+
- contrib/threaded_benchmark.rb
|
44
|
+
- README
|
45
|
+
- NEWS
|
46
|
+
- COPYING
|
47
|
+
test_files: []
|
48
|
+
rdoc_options: []
|
49
|
+
extra_rdoc_files: []
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
requirements: []
|
53
|
+
dependencies: []
|