hprevalence 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/hprevalence.rb +41 -15
- data/lib/transparent.rb +65 -38
- data/rakefile.rb +2 -2
- data/test/abstract_hprevalence_testcase.rb +6 -2
- data/test/circular_reference_test.rb +96 -0
- data/test/dvd_store_model.rb +42 -2
- data/test/natural_object_model_test.rb +32 -8
- data/test/project_model.rb +99 -0
- data/test/restoring_test.rb +54 -0
- data/test/task_model.rb +4 -0
- data/test/transparent_module_test.rb +29 -9
- metadata +5 -3
- data/lib/internal/object_graph.rb +0 -68
data/lib/hprevalence.rb
CHANGED
@@ -13,15 +13,25 @@ module HPrevalence
|
|
13
13
|
class EngineBuilder
|
14
14
|
|
15
15
|
def self.build( target_dir, &system_init )
|
16
|
+
ensure_dir( target_dir )
|
16
17
|
system = system_init.call
|
17
18
|
SimpleEngine.new( target_dir, system )
|
18
19
|
end
|
19
20
|
|
20
21
|
def self.build_transparent( target_dir, &system_init )
|
22
|
+
ensure_dir( target_dir )
|
21
23
|
system = system_init.call
|
22
24
|
TransparentEngine.new( target_dir, system )
|
23
25
|
end
|
24
26
|
|
27
|
+
private
|
28
|
+
|
29
|
+
def self.ensure_dir( dir )
|
30
|
+
return if File.exists?( dir )
|
31
|
+
require 'fileutils'
|
32
|
+
FileUtils.mkdir_p( dir )
|
33
|
+
end
|
34
|
+
|
25
35
|
end
|
26
36
|
|
27
37
|
#
|
@@ -56,7 +66,6 @@ module HPrevalence
|
|
56
66
|
end
|
57
67
|
|
58
68
|
#
|
59
|
-
# Holds only one system
|
60
69
|
#
|
61
70
|
class TransparentEngine < SimpleEngine
|
62
71
|
@@engines = Hash.new()
|
@@ -70,11 +79,11 @@ module HPrevalence
|
|
70
79
|
|
71
80
|
TransparentEngine.register_engine( self )
|
72
81
|
|
73
|
-
@proxies =
|
82
|
+
@proxies = Hash.new
|
74
83
|
add_engine_id_method( system )
|
75
84
|
store_manager = TransparentStorageManager.new(self, target_dir)
|
76
85
|
within_engine {
|
77
|
-
@proxied_system = HPrevalence::Transparent::TransparentProxy.new( system )
|
86
|
+
@proxied_system = HPrevalence::Transparent::TransparentProxy.new( system, Guid.from_s('00000000-0000-0000-0000-000000000000') )
|
78
87
|
}
|
79
88
|
|
80
89
|
super( target_dir, system, store_manager )
|
@@ -103,17 +112,20 @@ module HPrevalence
|
|
103
112
|
end
|
104
113
|
|
105
114
|
def execute_command(command)
|
106
|
-
|
115
|
+
begin
|
116
|
+
Thread.current[:references] = Hash.new
|
117
|
+
super(command)
|
118
|
+
ensure
|
119
|
+
Thread.current[:references] = nil
|
120
|
+
end
|
107
121
|
end
|
108
122
|
|
109
123
|
def register_proxy(proxy)
|
110
|
-
|
111
|
-
@proxies << proxy
|
112
|
-
@proxies.length - 1
|
124
|
+
@proxies[ proxy.proxy_id.to_s ] = proxy
|
113
125
|
end
|
114
126
|
|
115
127
|
def proxy_by_id( id, related_symbol = nil, parent_proxy_id = nil )
|
116
|
-
p = @proxies[ id ]
|
128
|
+
p = @proxies[ id.to_s ]
|
117
129
|
return p unless p.nil?
|
118
130
|
if !related_symbol.nil? && !parent_proxy_id.nil?
|
119
131
|
return create_nested_proxy( parent_proxy_id, related_symbol )
|
@@ -126,12 +138,23 @@ module HPrevalence
|
|
126
138
|
parent_proxy = proxy_by_id( parent_proxy_id )
|
127
139
|
target = parent_proxy.target
|
128
140
|
nested_target = target.send( related_symbol )
|
129
|
-
new_proxy = HPrevalence::Transparent::NestedTransparentProxy.new(
|
141
|
+
new_proxy = HPrevalence::Transparent::NestedTransparentProxy.new(
|
142
|
+
nested_target, related_symbol, parent_proxy_id )
|
130
143
|
target._set_proxy( related_symbol, new_proxy )
|
131
144
|
return new_proxy
|
132
145
|
}
|
133
146
|
end
|
134
147
|
|
148
|
+
def create_result_proxy( target )
|
149
|
+
@proxies_lock.synchronize {
|
150
|
+
# parent_proxy = proxy_by_id( parent_proxy_id )
|
151
|
+
# target = parent_proxy.target
|
152
|
+
# result = target.send( related_symbol, *args )
|
153
|
+
new_proxy = HPrevalence::Transparent::TransparentProxy.new( target )
|
154
|
+
return new_proxy
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
135
158
|
def within_engine()
|
136
159
|
begin
|
137
160
|
Thread.current[:engine] = self
|
@@ -143,9 +166,9 @@ module HPrevalence
|
|
143
166
|
|
144
167
|
def restore(proxy)
|
145
168
|
proxy.engine_id = @engine_id
|
146
|
-
p = proxy_by_id(proxy.proxy_id)
|
169
|
+
p = proxy_by_id( proxy.proxy_id )
|
147
170
|
if (p.nil?)
|
148
|
-
|
171
|
+
register_proxy(proxy)
|
149
172
|
p = proxy
|
150
173
|
end
|
151
174
|
p
|
@@ -157,6 +180,12 @@ module HPrevalence
|
|
157
180
|
}
|
158
181
|
end
|
159
182
|
|
183
|
+
def self.create_result_proxy( target, engine_id )
|
184
|
+
within_engine( engine_id ) { |engine|
|
185
|
+
engine.create_result_proxy( target )
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
160
189
|
def self.within_engine( engine_id )
|
161
190
|
engine = @@engines[ engine_id ]
|
162
191
|
raise 'Engine not found' if engine.nil?
|
@@ -202,13 +231,10 @@ module HPrevalence
|
|
202
231
|
system.instance_eval {
|
203
232
|
@engine_id = temp_id
|
204
233
|
}
|
205
|
-
# (class << system; self;end).class_eval do
|
206
|
-
# define_method(:_engine_id) { @engine_id }
|
207
|
-
# end
|
208
234
|
end
|
209
|
-
|
210
235
|
end
|
211
236
|
|
237
|
+
#
|
212
238
|
class PrevalenceService
|
213
239
|
attr_reader :engine
|
214
240
|
|
data/lib/transparent.rb
CHANGED
@@ -7,10 +7,35 @@ module HPrevalence
|
|
7
7
|
|
8
8
|
def self.included(type)
|
9
9
|
class << type
|
10
|
-
def
|
10
|
+
def method_added(symbol)
|
11
|
+
self.instance_eval do
|
12
|
+
@readonly_attributes ||= []
|
13
|
+
hierarchy = self
|
14
|
+
while hierarchy = hierarchy.superclass
|
15
|
+
if (hierarchy.instance_eval {instance_variables.include? '@readonly_attributes'})
|
16
|
+
@readonly_attributes |= hierarchy.instance_eval {@readonly_attributes}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def attr_proxy_attribute( *symbols )
|
11
23
|
@proxied_attributes ||= []
|
12
24
|
symbols.each do |symbol|
|
13
|
-
|
25
|
+
self.instance_eval {
|
26
|
+
@proxied_attributes ||= []
|
27
|
+
@proxied_attributes << symbol
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def attr_proxy_method( *symbols )
|
33
|
+
@proxied_attributes ||= []
|
34
|
+
symbols.each do |symbol|
|
35
|
+
self.instance_eval {
|
36
|
+
@proxied_methods ||= []
|
37
|
+
@proxied_methods << symbol
|
38
|
+
}
|
14
39
|
end
|
15
40
|
end
|
16
41
|
|
@@ -28,14 +53,20 @@ module HPrevalence
|
|
28
53
|
def _read_only_attributes()
|
29
54
|
self.class.instance_eval { @readonly_attributes ||= [] }
|
30
55
|
end
|
31
|
-
|
56
|
+
|
32
57
|
def _proxied_attributes()
|
33
58
|
self.class.instance_eval { @proxied_attributes ||= [] }
|
34
59
|
end
|
35
60
|
|
61
|
+
def _proxied_methods()
|
62
|
+
self.class.instance_eval { @proxied_methods ||= [] }
|
63
|
+
end
|
64
|
+
|
36
65
|
def _should_proxy( symbol )
|
37
66
|
if _proxied_attributes().include?( symbol )
|
38
67
|
return true if _symbol2proxy(symbol).nil?
|
68
|
+
elsif _proxied_methods().include?( symbol )
|
69
|
+
return true
|
39
70
|
end
|
40
71
|
false
|
41
72
|
end
|
@@ -46,7 +77,7 @@ module HPrevalence
|
|
46
77
|
@symbol2proxy[symbol]
|
47
78
|
}
|
48
79
|
end
|
49
|
-
|
80
|
+
|
50
81
|
def _set_proxy( symbol, proxy )
|
51
82
|
self.instance_eval {
|
52
83
|
@symbol2proxy ||= {}
|
@@ -59,14 +90,14 @@ module HPrevalence
|
|
59
90
|
class AutomaticCommand
|
60
91
|
|
61
92
|
def initialize( proxy_id, symbol, *args )
|
62
|
-
@proxy_id, @symbol, @args = proxy_id, symbol,
|
93
|
+
@proxy_id, @symbol, @args = proxy_id, symbol, args
|
63
94
|
end
|
64
95
|
|
65
96
|
def execute(system)
|
66
97
|
TransparentEngine.current() { |engine|
|
67
98
|
proxy = engine.proxy_by_id(@proxy_id)
|
68
99
|
raise 'Could not obtain proxy' if proxy.nil?
|
69
|
-
return proxy.send(@symbol, *arguments)
|
100
|
+
return proxy.target.send(@symbol, *arguments)
|
70
101
|
}
|
71
102
|
end
|
72
103
|
|
@@ -77,7 +108,7 @@ module HPrevalence
|
|
77
108
|
end
|
78
109
|
|
79
110
|
end
|
80
|
-
|
111
|
+
|
81
112
|
class NestedAutomaticCommand < AutomaticCommand
|
82
113
|
|
83
114
|
attr_reader :related_symbol, :parent_proxy_id
|
@@ -93,7 +124,7 @@ module HPrevalence
|
|
93
124
|
TransparentEngine.current() { |engine|
|
94
125
|
proxy = engine.proxy_by_id(@proxy_id, @related_symbol, @parent_proxy_id)
|
95
126
|
raise 'Could not obtain proxy' if proxy.nil?
|
96
|
-
return proxy.send(@symbol, *arguments)
|
127
|
+
return proxy.target.send(@symbol, *arguments)
|
97
128
|
}
|
98
129
|
end
|
99
130
|
end
|
@@ -101,14 +132,15 @@ module HPrevalence
|
|
101
132
|
class TransparentProxy
|
102
133
|
attr_accessor :target, :proxy_id, :engine_id
|
103
134
|
|
104
|
-
def initialize(target)
|
135
|
+
def initialize(target, new_id = Guid.new)
|
105
136
|
raise 'A proxy must have a target' if target.nil?
|
106
137
|
raise 'Strange. A proxy is supposed to exist within an engine' unless Thread.current[:engine]
|
138
|
+
@proxy_id = new_id
|
107
139
|
@engine_id = Thread.current[:engine].engine_id
|
108
|
-
|
109
|
-
# puts "New proxy with id #{@proxy_id.to_s} for #{target.class.to_s}"
|
140
|
+
Thread.current[:engine].register_proxy(self)
|
110
141
|
@target = target
|
111
142
|
@is_custom = target.kind_of?(CustomBehavior)
|
143
|
+
# puts "New proxy with id #{@proxy_id.to_s} for #{target.class.to_s}"
|
112
144
|
end
|
113
145
|
|
114
146
|
def method_missing(symbol, *args, &block)
|
@@ -117,16 +149,15 @@ module HPrevalence
|
|
117
149
|
|
118
150
|
if (@is_custom)
|
119
151
|
if (@target._read_only_attributes.include?(symbol) )
|
120
|
-
return @target.send(symbol, *args)
|
121
|
-
elsif (@target._should_proxy(symbol) )
|
152
|
+
return @target.send(symbol, *args, &block)
|
153
|
+
elsif (args.length == 0 && @target._should_proxy(symbol) )
|
154
|
+
# Attribute being accessed
|
122
155
|
return TransparentEngine.create_nested_proxy( @proxy_id, symbol, @engine_id )
|
123
|
-
|
124
|
-
#
|
125
|
-
|
126
|
-
|
127
|
-
#
|
128
|
-
# return new_proxy
|
129
|
-
# }
|
156
|
+
elsif (args.length != 0 && @target._should_proxy(symbol) )
|
157
|
+
# Method being accessed
|
158
|
+
result = proceed(symbol, *args, &block)
|
159
|
+
return TransparentEngine.create_result_proxy( result, @engine_id )
|
160
|
+
# return TransparentProxy.new( result )
|
130
161
|
else
|
131
162
|
nested_proxy = @target._symbol2proxy(symbol)
|
132
163
|
# Returns the nested proxy associated with this symbol
|
@@ -138,30 +169,26 @@ module HPrevalence
|
|
138
169
|
proceed(symbol, *args, &block)
|
139
170
|
end
|
140
171
|
|
141
|
-
# Custom marshalling - this adds the internal id (myid) and the system id to a marshall
|
142
|
-
# of the object we are the proxy for.
|
143
|
-
# We take care to not marshal the same object twice, so circular references will work.
|
144
|
-
|
145
172
|
def _dump(depth)
|
146
173
|
references = Thread.current[:references]
|
147
174
|
if (references)
|
148
175
|
if (references[self])
|
149
|
-
|
176
|
+
# puts "> serializing placeholder #{@proxy_id.to_s}"
|
177
|
+
[@proxy_id.to_s, @engine_id.to_s].pack("A40A40")
|
150
178
|
else
|
179
|
+
# puts "> serializing proxy #{@proxy_id.to_s}"
|
151
180
|
references[self] = true
|
152
|
-
[@proxy_id.to_s, @engine_id.to_s].pack("
|
181
|
+
[@proxy_id.to_s, @engine_id.to_s].pack("A40A40") + Marshal.dump(@target, depth)
|
153
182
|
end
|
154
183
|
else
|
155
|
-
[@proxy_id.to_s, @engine_id.to_s].pack("
|
184
|
+
[@proxy_id.to_s, @engine_id.to_s].pack("A40A40")
|
156
185
|
end
|
157
186
|
end
|
158
187
|
|
159
|
-
# Custom marshalling - restore a Prox object.
|
160
|
-
|
161
188
|
def TransparentProxy._load(buffer)
|
162
189
|
proxy = TransparentProxy.allocate
|
163
|
-
list = buffer.unpack("
|
164
|
-
proxy.proxy_id = list[0]
|
190
|
+
list = buffer.unpack("A40A40a*")
|
191
|
+
proxy.proxy_id = Guid.from_s( list[0] )
|
165
192
|
proxy.engine_id = Guid.from_s( list[1] )
|
166
193
|
TransparentEngine.current() { |engine|
|
167
194
|
proxy = engine.restore(proxy)
|
@@ -193,24 +220,24 @@ module HPrevalence
|
|
193
220
|
end
|
194
221
|
result
|
195
222
|
end
|
196
|
-
|
223
|
+
|
197
224
|
end
|
198
|
-
|
225
|
+
|
199
226
|
class NestedTransparentProxy < TransparentProxy
|
200
|
-
attr_reader :related_symbol, :parent_proxy_id
|
227
|
+
attr_reader :related_symbol, :parent_proxy_id, :args
|
201
228
|
|
202
|
-
def initialize(target, symbol, parent_proxy_id)
|
229
|
+
def initialize(target, symbol, parent_proxy_id, *args)
|
203
230
|
raise 'A nested-proxy must have a symbol' if symbol.nil?
|
204
231
|
super(target)
|
205
|
-
@related_symbol, @parent_proxy_id = symbol, parent_proxy_id
|
232
|
+
@related_symbol, @parent_proxy_id, @args = symbol, parent_proxy_id, args
|
206
233
|
end
|
207
|
-
|
234
|
+
|
208
235
|
protected
|
209
236
|
|
210
237
|
def build_command( symbol, *args )
|
211
238
|
NestedAutomaticCommand.new( @related_symbol, @parent_proxy_id, @proxy_id, symbol, *args )
|
212
239
|
end
|
213
|
-
|
240
|
+
|
214
241
|
end
|
215
242
|
|
216
243
|
end
|
data/rakefile.rb
CHANGED
@@ -10,7 +10,7 @@ require 'rake/rdoctask'
|
|
10
10
|
|
11
11
|
#task :default => [ :test_units, :test_functional, :appdoc, :stats ]
|
12
12
|
desc "Run all the tests"
|
13
|
-
task :default => [ :test_units
|
13
|
+
task :default => [ :test_units ]
|
14
14
|
|
15
15
|
desc "Run the unit tests in test/unit"
|
16
16
|
Rake::TestTask.new("test_units") { |t|
|
@@ -19,7 +19,7 @@ Rake::TestTask.new("test_units") { |t|
|
|
19
19
|
t.verbose = true
|
20
20
|
}
|
21
21
|
|
22
|
-
PKG_VERSION = '0.1.
|
22
|
+
PKG_VERSION = '0.1.1'
|
23
23
|
PKG_FILES = FileList['README', './**/*.rb']
|
24
24
|
|
25
25
|
spec = Gem::Specification.new do |s|
|
@@ -1,10 +1,14 @@
|
|
1
1
|
|
2
2
|
module AbstractHPrevalenceTestHelper
|
3
3
|
|
4
|
+
def create_dir_name( name )
|
5
|
+
current_dir = File.dirname(__FILE__)
|
6
|
+
File.join( current_dir, name )
|
7
|
+
end
|
8
|
+
|
4
9
|
def reset_dir( dir )
|
5
10
|
begin
|
6
|
-
|
7
|
-
@target_dir = File.join( current_dir, dir )
|
11
|
+
@target_dir = create_dir_name( dir )
|
8
12
|
if File.exist?( @target_dir )
|
9
13
|
Dir.entries( @target_dir ).each do |file|
|
10
14
|
File.delete( File.join(@target_dir,file) ) unless File.directory?( file )
|
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib/")
|
3
|
+
|
4
|
+
require 'hprevalence'
|
5
|
+
require 'transparent'
|
6
|
+
require 'abstract_hprevalence_testcase'
|
7
|
+
require 'dvd_store_model'
|
8
|
+
require 'test/unit'
|
9
|
+
|
10
|
+
class Item
|
11
|
+
attr_accessor :sub_item
|
12
|
+
|
13
|
+
def initialize( sub )
|
14
|
+
@sub_item = sub
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class SampleDatabase
|
19
|
+
include HPrevalence::Transparent::CustomBehavior
|
20
|
+
|
21
|
+
attr_reader :list
|
22
|
+
attr_proxy_attribute :list
|
23
|
+
attr_proxy_method :create_item
|
24
|
+
|
25
|
+
def initialize()
|
26
|
+
@list = []
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_item( sub = nil )
|
30
|
+
Item.new( sub )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class CircularReferenceTest < Test::Unit::TestCase
|
35
|
+
include AbstractHPrevalenceTestHelper
|
36
|
+
|
37
|
+
def setup
|
38
|
+
reset_dir 'store/circular_test'
|
39
|
+
ensure_dir
|
40
|
+
load_engine
|
41
|
+
end
|
42
|
+
|
43
|
+
def teardown
|
44
|
+
@engine.close
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_engine
|
48
|
+
teardown unless @engine.nil?
|
49
|
+
|
50
|
+
@engine = HPrevalence::EngineBuilder.build_transparent( @target_dir ) {
|
51
|
+
SampleDatabase.new()
|
52
|
+
}
|
53
|
+
|
54
|
+
assert_not_nil @engine
|
55
|
+
@system = @engine.system
|
56
|
+
assert_not_nil @system
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_simple
|
60
|
+
item = @system.create_item()
|
61
|
+
@system.list << item
|
62
|
+
|
63
|
+
load_engine
|
64
|
+
|
65
|
+
assert_equal 1, @system.list.length
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_circular
|
69
|
+
item = @system.create_item()
|
70
|
+
item.sub_item = item
|
71
|
+
|
72
|
+
@system.list << item
|
73
|
+
|
74
|
+
load_engine
|
75
|
+
|
76
|
+
assert_equal 1, @system.list.length
|
77
|
+
item = @system.list[0]
|
78
|
+
assert_equal item, item.sub_item
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_circular_with_snapshot
|
82
|
+
item = @system.create_item()
|
83
|
+
item.sub_item = item
|
84
|
+
|
85
|
+
@system.list << item
|
86
|
+
|
87
|
+
@engine.take_snapshot
|
88
|
+
load_engine
|
89
|
+
|
90
|
+
assert_equal 1, @system.list.length
|
91
|
+
item = @system.list[0]
|
92
|
+
assert_equal item, item.sub_item
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
data/test/dvd_store_model.rb
CHANGED
@@ -2,6 +2,41 @@ $:.unshift(File.dirname(__FILE__) + "/../lib/")
|
|
2
2
|
|
3
3
|
require 'transparent'
|
4
4
|
|
5
|
+
class AbstractCollection
|
6
|
+
include Enumerable
|
7
|
+
include HPrevalence::Transparent::CustomBehavior
|
8
|
+
attr_read_only :each
|
9
|
+
|
10
|
+
def initialize()
|
11
|
+
@list = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def add( item )
|
15
|
+
@list << item
|
16
|
+
item
|
17
|
+
end
|
18
|
+
|
19
|
+
def each()
|
20
|
+
@list.each { |item|
|
21
|
+
yield(item)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def length
|
26
|
+
@list.length
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class DvdCollection < AbstractCollection
|
32
|
+
# include HPrevalence::Transparent::CustomBehavior
|
33
|
+
|
34
|
+
def initialize()
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
5
40
|
class DvdStore
|
6
41
|
include HPrevalence::Transparent::CustomBehavior
|
7
42
|
|
@@ -9,11 +44,12 @@ class DvdStore
|
|
9
44
|
attr_reader :available_categories
|
10
45
|
attr_reader :customers
|
11
46
|
|
12
|
-
|
47
|
+
attr_proxy_attribute :available_items, :available_categories, :customers
|
48
|
+
attr_proxy_method :create_dvd
|
13
49
|
attr_read_only :total_customers
|
14
50
|
|
15
51
|
def initialize()
|
16
|
-
@available_items =
|
52
|
+
@available_items = DvdCollection.new()
|
17
53
|
@available_categories = []
|
18
54
|
@customers = []
|
19
55
|
end
|
@@ -21,6 +57,10 @@ class DvdStore
|
|
21
57
|
def total_customers
|
22
58
|
@customers.length
|
23
59
|
end
|
60
|
+
|
61
|
+
def create_dvd( dvd )
|
62
|
+
dvd
|
63
|
+
end
|
24
64
|
|
25
65
|
end
|
26
66
|
|
@@ -41,8 +41,11 @@ class NaturalObjectModelTest < Test::Unit::TestCase
|
|
41
41
|
@system.available_categories << fiction
|
42
42
|
@system.available_categories << comedy
|
43
43
|
|
44
|
-
@system.
|
45
|
-
@system.
|
44
|
+
dvd1 = @system.create_dvd( Dvd.new( 'Fight Club', 22.1, fiction ) )
|
45
|
+
dvd2 = @system.create_dvd( Dvd.new( 'Monty Python and the Holy Grail', 15, comedy ) )
|
46
|
+
|
47
|
+
@system.available_items.add( dvd1 )
|
48
|
+
@system.available_items.add( dvd2 )
|
46
49
|
|
47
50
|
assert_equal 2, @system.available_items.length
|
48
51
|
assert_equal 2, @system.available_categories.length
|
@@ -53,6 +56,27 @@ class NaturalObjectModelTest < Test::Unit::TestCase
|
|
53
56
|
assert_equal 2, @system.available_categories.length
|
54
57
|
end
|
55
58
|
|
59
|
+
def test_read_only_each
|
60
|
+
assert_equal 0, @system.total_customers
|
61
|
+
|
62
|
+
fiction = Category.new('Fiction')
|
63
|
+
comedy = Category.new('Comedy')
|
64
|
+
|
65
|
+
@system.available_categories << fiction
|
66
|
+
@system.available_categories << comedy
|
67
|
+
|
68
|
+
dvd1 = @system.create_dvd( Dvd.new( 'Fight Club', 22.1, fiction ) )
|
69
|
+
dvd2 = @system.create_dvd( Dvd.new( 'Monty Python and the Holy Grail', 15, comedy ) )
|
70
|
+
|
71
|
+
@system.available_items.add( dvd1 )
|
72
|
+
@system.available_items.add( dvd2 )
|
73
|
+
|
74
|
+
@system.available_items.each() do |item|
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
56
80
|
def test_more_engine_loads
|
57
81
|
load_engine
|
58
82
|
assert_equal 0, @system.total_customers
|
@@ -66,8 +90,8 @@ class NaturalObjectModelTest < Test::Unit::TestCase
|
|
66
90
|
assert_equal 0, @system.available_items.length
|
67
91
|
assert_equal 2, @system.available_categories.length
|
68
92
|
|
69
|
-
@system.available_items
|
70
|
-
@system.available_items
|
93
|
+
@system.available_items.add( Dvd.new( 'Fight Club', 22.1, fiction ) )
|
94
|
+
@system.available_items.add( Dvd.new( 'Monty Python and the Holy Grail', 15, comedy ) )
|
71
95
|
|
72
96
|
assert_equal 2, @system.available_items.length
|
73
97
|
assert_equal 2, @system.available_categories.length
|
@@ -80,8 +104,8 @@ class NaturalObjectModelTest < Test::Unit::TestCase
|
|
80
104
|
horror = Category.new('Horror')
|
81
105
|
@system.available_categories << horror
|
82
106
|
|
83
|
-
@system.available_items
|
84
|
-
@system.available_items
|
107
|
+
@system.available_items.add( Dvd.new( 'Pet Sematary - 1989', 22.6, horror ) )
|
108
|
+
@system.available_items.add( Dvd.new( 'Fawlty Towers - 12 episodes', 40.6, comedy ) )
|
85
109
|
|
86
110
|
assert_equal 4, @system.available_items.length
|
87
111
|
assert_equal 3, @system.available_categories.length
|
@@ -102,8 +126,8 @@ class NaturalObjectModelTest < Test::Unit::TestCase
|
|
102
126
|
@system.available_categories << fiction
|
103
127
|
@system.available_categories << comedy
|
104
128
|
|
105
|
-
@system.available_items
|
106
|
-
@system.available_items
|
129
|
+
@system.available_items.add( Dvd.new( 'Fight Club', 22.1, fiction ) )
|
130
|
+
@system.available_items.add( Dvd.new( 'Monty Python and the Holy Grail', 15, comedy ) )
|
107
131
|
|
108
132
|
assert_equal 2, @system.available_items.length
|
109
133
|
assert_equal 2, @system.available_categories.length
|
@@ -0,0 +1,99 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib/")
|
2
|
+
|
3
|
+
require 'hprevalence'
|
4
|
+
require 'transparent'
|
5
|
+
|
6
|
+
class ModelBase
|
7
|
+
include HPrevalence::Transparent::CustomBehavior
|
8
|
+
attr_read_only :ident
|
9
|
+
|
10
|
+
attr_reader :ident
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
def initialize(name, id_val = name)
|
14
|
+
@ident, @name = id_val, name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ModelArrayBase
|
19
|
+
include Enumerable
|
20
|
+
include HPrevalence::Transparent::CustomBehavior
|
21
|
+
attr_read_only :[], :each, :length
|
22
|
+
|
23
|
+
def initialize()
|
24
|
+
@list = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def add( item )
|
28
|
+
@list << item
|
29
|
+
end
|
30
|
+
|
31
|
+
def each
|
32
|
+
@list.each { |item| yield(item) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](index)
|
36
|
+
if (index.kind_of?(Integer))
|
37
|
+
return @list[index]
|
38
|
+
elsif (index.kind_of?(String))
|
39
|
+
return find { |item| item.ident == index }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def length
|
44
|
+
@list.length
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
class User < ModelBase
|
50
|
+
attr_reader :password
|
51
|
+
|
52
|
+
def initialize(name, password)
|
53
|
+
super(name)
|
54
|
+
@password = password
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Project < ModelBase
|
59
|
+
attr_reader :owner
|
60
|
+
|
61
|
+
def initialize(name, idVal, owner)
|
62
|
+
super(name, idVal)
|
63
|
+
@owner = owner
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class UserCollection < ModelArrayBase
|
68
|
+
def initialize()
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ProjectCollection < ModelArrayBase
|
74
|
+
def initialize()
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ApplicationDatabase
|
80
|
+
include HPrevalence::Transparent::CustomBehavior
|
81
|
+
|
82
|
+
attr_reader :users, :projects
|
83
|
+
attr_proxy_attribute :users, :projects
|
84
|
+
attr_proxy_method :create_user, :create_project
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
@users = UserCollection.new()
|
88
|
+
@projects = ProjectCollection.new()
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_user( login, pass )
|
92
|
+
User.new( login, pass )
|
93
|
+
end
|
94
|
+
|
95
|
+
def create_project( name, id, user )
|
96
|
+
Project.new( name, id, user )
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib/")
|
3
|
+
|
4
|
+
require 'hprevalence'
|
5
|
+
require 'transparent'
|
6
|
+
require 'abstract_hprevalence_testcase'
|
7
|
+
require 'project_model'
|
8
|
+
require 'test/unit'
|
9
|
+
|
10
|
+
class RestoringTest < Test::Unit::TestCase
|
11
|
+
include AbstractHPrevalenceTestHelper
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@target_dir = create_dir_name('store/fixed')
|
15
|
+
load_engine
|
16
|
+
end
|
17
|
+
|
18
|
+
def teardown
|
19
|
+
@engine.close
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_engine
|
23
|
+
teardown unless @engine.nil?
|
24
|
+
|
25
|
+
@engine = HPrevalence::EngineBuilder.build_transparent( @target_dir ) {
|
26
|
+
ApplicationDatabase.new()
|
27
|
+
}
|
28
|
+
|
29
|
+
assert_not_nil @engine
|
30
|
+
@system = @engine.system
|
31
|
+
assert_not_nil @system
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_a
|
35
|
+
teardown
|
36
|
+
reset_dir 'store/fixed'
|
37
|
+
setup
|
38
|
+
|
39
|
+
user = @system.create_user( "test", "test" )
|
40
|
+
@system.users.add( user ) unless @system.users.length != 0
|
41
|
+
|
42
|
+
project = @system.create_project( 'cas', 'Castle', user )
|
43
|
+
@system.projects.add( project )
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def atest_aaa
|
48
|
+
|
49
|
+
assert_equal 1, @system.users.length
|
50
|
+
assert_equal 1, @system.projects.length
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/test/task_model.rb
CHANGED
@@ -5,6 +5,16 @@ require 'transparent'
|
|
5
5
|
require 'abstract_hprevalence_testcase'
|
6
6
|
require 'task_model'
|
7
7
|
require 'test/unit'
|
8
|
+
require 'guid'
|
9
|
+
|
10
|
+
class FakeProxy
|
11
|
+
attr_reader :proxy_id
|
12
|
+
|
13
|
+
def initialize()
|
14
|
+
@proxy_id = Guid.new
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
8
18
|
|
9
19
|
class TransparentTest < Test::Unit::TestCase
|
10
20
|
include AbstractHPrevalenceTestHelper
|
@@ -32,17 +42,14 @@ class TransparentTest < Test::Unit::TestCase
|
|
32
42
|
end
|
33
43
|
|
34
44
|
def test_proxy_registration
|
35
|
-
fake_proxy1 =
|
36
|
-
fake_proxy2 =
|
45
|
+
fake_proxy1 = FakeProxy.new()
|
46
|
+
fake_proxy2 = FakeProxy.new()
|
37
47
|
|
38
|
-
|
39
|
-
|
48
|
+
@engine.register_proxy( fake_proxy1 )
|
49
|
+
@engine.register_proxy( fake_proxy2 )
|
40
50
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
assert_equal fake_proxy1, @engine.proxy_by_id( proxy_id1 )
|
45
|
-
assert_equal fake_proxy2, @engine.proxy_by_id( proxy_id2 )
|
51
|
+
assert_equal fake_proxy1, @engine.proxy_by_id( fake_proxy1.proxy_id )
|
52
|
+
assert_equal fake_proxy2, @engine.proxy_by_id( fake_proxy2.proxy_id )
|
46
53
|
end
|
47
54
|
|
48
55
|
def test_simple_case
|
@@ -91,5 +98,18 @@ class TransparentTest < Test::Unit::TestCase
|
|
91
98
|
|
92
99
|
assert_equal 3, @system.tasks.length
|
93
100
|
end
|
101
|
+
|
102
|
+
def test_recursive_invocations
|
103
|
+
assert_equal 0, @system.tasks.length
|
104
|
+
@system.add( 'first', 'content' )
|
105
|
+
@system.add( 'second', 'content' )
|
106
|
+
assert_equal 2, @system.tasks.length
|
107
|
+
|
108
|
+
@engine.take_snapshot
|
109
|
+
|
110
|
+
load_engine
|
111
|
+
|
112
|
+
assert_equal 2, @system.tasks.length
|
113
|
+
end
|
94
114
|
|
95
115
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.1
|
|
3
3
|
specification_version: 1
|
4
4
|
name: hprevalence
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2004-10-
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2004-10-27
|
8
8
|
summary: Ruby based prevalence engine.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,15 +33,17 @@ files:
|
|
33
33
|
- "./lib/transparent.rb"
|
34
34
|
- "./lib/internal/command_logger.rb"
|
35
35
|
- "./lib/internal/iomanager.rb"
|
36
|
-
- "./lib/internal/object_graph.rb"
|
37
36
|
- "./lib/internal/serializer.rb"
|
38
37
|
- "./lib/internal/store_manager.rb"
|
39
38
|
- "./test/abstract_hprevalence_testcase.rb"
|
39
|
+
- "./test/circular_reference_test.rb"
|
40
40
|
- "./test/command_logger_test.rb"
|
41
41
|
- "./test/default_model_serializer_test.rb"
|
42
42
|
- "./test/dvd_store_model.rb"
|
43
43
|
- "./test/file_io_manager_test.rb"
|
44
44
|
- "./test/natural_object_model_test.rb"
|
45
|
+
- "./test/project_model.rb"
|
46
|
+
- "./test/restoring_test.rb"
|
45
47
|
- "./test/simple_engine_test.rb"
|
46
48
|
- "./test/task_model.rb"
|
47
49
|
- "./test/transparent_module_test.rb"
|
@@ -1,68 +0,0 @@
|
|
1
|
-
|
2
|
-
class ObjectGraph
|
3
|
-
|
4
|
-
def initialize()
|
5
|
-
@root = nil
|
6
|
-
@registry = Hash.new()
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.build( node )
|
10
|
-
graph = ObjectGraph.new()
|
11
|
-
graph.root = node
|
12
|
-
graph
|
13
|
-
end
|
14
|
-
|
15
|
-
def root=( node )
|
16
|
-
@root = GraphItem.new( self, node )
|
17
|
-
end
|
18
|
-
|
19
|
-
def root
|
20
|
-
@root.object
|
21
|
-
end
|
22
|
-
|
23
|
-
def object_reference( var )
|
24
|
-
graph_item = @registry[ var ]
|
25
|
-
if (graph_item.nil?)
|
26
|
-
graph_item = GraphItem.new( self, var )
|
27
|
-
@registry[ var ] = graph_item
|
28
|
-
end
|
29
|
-
graph_item
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
class GraphItem
|
35
|
-
|
36
|
-
def initialize( graph, node )
|
37
|
-
@instance_vars = Hash.new
|
38
|
-
@node_type = node.class
|
39
|
-
|
40
|
-
node.instance_variables.each do |var|
|
41
|
-
name = var.slice(1, var.length - 1)
|
42
|
-
@instance_vars[ name ] = graph.object_reference( node.instance_eval(var) )
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def object
|
47
|
-
obj = @node_type.allocate
|
48
|
-
restore_instance_vars obj
|
49
|
-
obj
|
50
|
-
end
|
51
|
-
|
52
|
-
protected
|
53
|
-
|
54
|
-
def restore_instance_vars(obj)
|
55
|
-
@instance_vars.each do |name, value|
|
56
|
-
|
57
|
-
var = value.object
|
58
|
-
|
59
|
-
setter = name + "="
|
60
|
-
if obj.respond_to?(setter)
|
61
|
-
obj.__send__(setter, var)
|
62
|
-
else
|
63
|
-
obj.instance_eval("@#{ name } = var")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|