hprevalence 0.1.0 → 0.1.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/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
|