hprevalence 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,218 @@
1
+
2
+ module HPrevalence
3
+
4
+ module Transparent
5
+
6
+ module CustomBehavior
7
+
8
+ def self.included(type)
9
+ class << type
10
+ def attr_proxy( *symbols )
11
+ @proxied_attributes ||= []
12
+ symbols.each do |symbol|
13
+ @proxied_attributes << symbol
14
+ end
15
+ end
16
+
17
+ def attr_read_only( *symbols )
18
+ symbols.each do |symbol|
19
+ self.instance_eval {
20
+ @readonly_attributes ||= []
21
+ @readonly_attributes << symbol
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def _read_only_attributes()
29
+ self.class.instance_eval { @readonly_attributes ||= [] }
30
+ end
31
+
32
+ def _proxied_attributes()
33
+ self.class.instance_eval { @proxied_attributes ||= [] }
34
+ end
35
+
36
+ def _should_proxy( symbol )
37
+ if _proxied_attributes().include?( symbol )
38
+ return true if _symbol2proxy(symbol).nil?
39
+ end
40
+ false
41
+ end
42
+
43
+ def _symbol2proxy( symbol )
44
+ self.instance_eval {
45
+ @symbol2proxy ||= {}
46
+ @symbol2proxy[symbol]
47
+ }
48
+ end
49
+
50
+ def _set_proxy( symbol, proxy )
51
+ self.instance_eval {
52
+ @symbol2proxy ||= {}
53
+ @symbol2proxy[symbol] = proxy
54
+ }
55
+ end
56
+
57
+ end
58
+
59
+ class AutomaticCommand
60
+
61
+ def initialize( proxy_id, symbol, *args )
62
+ @proxy_id, @symbol, @args = proxy_id, symbol, *args
63
+ end
64
+
65
+ def execute(system)
66
+ TransparentEngine.current() { |engine|
67
+ proxy = engine.proxy_by_id(@proxy_id)
68
+ raise 'Could not obtain proxy' if proxy.nil?
69
+ return proxy.send(@symbol, *arguments)
70
+ }
71
+ end
72
+
73
+ protected
74
+
75
+ def arguments()
76
+ @args ||= []
77
+ end
78
+
79
+ end
80
+
81
+ class NestedAutomaticCommand < AutomaticCommand
82
+
83
+ attr_reader :related_symbol, :parent_proxy_id
84
+
85
+ def initialize( related_symbol, parent_proxy_id, proxy_id, symbol, *args )
86
+ super( proxy_id, symbol, *args )
87
+ raise 'Missing related_symbol' if related_symbol.nil?
88
+ raise 'Missing parent_proxy_id' if parent_proxy_id.nil?
89
+ @related_symbol, @parent_proxy_id = related_symbol, parent_proxy_id
90
+ end
91
+
92
+ def execute(system)
93
+ TransparentEngine.current() { |engine|
94
+ proxy = engine.proxy_by_id(@proxy_id, @related_symbol, @parent_proxy_id)
95
+ raise 'Could not obtain proxy' if proxy.nil?
96
+ return proxy.send(@symbol, *arguments)
97
+ }
98
+ end
99
+ end
100
+
101
+ class TransparentProxy
102
+ attr_accessor :target, :proxy_id, :engine_id
103
+
104
+ def initialize(target)
105
+ raise 'A proxy must have a target' if target.nil?
106
+ raise 'Strange. A proxy is supposed to exist within an engine' unless Thread.current[:engine]
107
+ @engine_id = Thread.current[:engine].engine_id
108
+ @proxy_id = Thread.current[:engine].register_proxy(self)
109
+ # puts "New proxy with id #{@proxy_id.to_s} for #{target.class.to_s}"
110
+ @target = target
111
+ @is_custom = target.kind_of?(CustomBehavior)
112
+ end
113
+
114
+ def method_missing(symbol, *args, &block)
115
+ # puts "Sending #{symbol} to #{@target.to_s} args #{args.to_s}"
116
+ raise NoMethodError, "Undefined method" unless @target.respond_to?(symbol)
117
+
118
+ if (@is_custom)
119
+ if (@target._read_only_attributes.include?(symbol) )
120
+ return @target.send(symbol, *args)
121
+ elsif (@target._should_proxy(symbol) )
122
+ return TransparentEngine.create_nested_proxy( @proxy_id, symbol, @engine_id )
123
+ # nested_target = @target.send(symbol, *args)
124
+ # return nil if nested_target.nil?
125
+ # TransparentEngine.within_engine( @engine_id ) { |engine|
126
+ # new_proxy = TransparentProxy.new( nested_target )
127
+ # @target._set_proxy( symbol, new_proxy )
128
+ # return new_proxy
129
+ # }
130
+ else
131
+ nested_proxy = @target._symbol2proxy(symbol)
132
+ # Returns the nested proxy associated with this symbol
133
+ # puts "Returning nested proxy for #{symbol} if #{nested_proxy.class.to_s}"
134
+ return nested_proxy unless nested_proxy.nil?
135
+ end
136
+ end
137
+
138
+ proceed(symbol, *args, &block)
139
+ end
140
+
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
+ def _dump(depth)
146
+ references = Thread.current[:references]
147
+ if (references)
148
+ if (references[self])
149
+ [@proxy_id.to_s, @engine_id.to_s].pack("A8A40")
150
+ else
151
+ references[self] = true
152
+ [@proxy_id.to_s, @engine_id.to_s].pack("A8A40") + Marshal.dump(@target, depth)
153
+ end
154
+ else
155
+ [@proxy_id.to_s, @engine_id.to_s].pack("A8A40")
156
+ end
157
+ end
158
+
159
+ # Custom marshalling - restore a Prox object.
160
+
161
+ def TransparentProxy._load(buffer)
162
+ proxy = TransparentProxy.allocate
163
+ list = buffer.unpack("A8A40a*")
164
+ proxy.proxy_id = list[0].to_i
165
+ proxy.engine_id = Guid.from_s( list[1] )
166
+ TransparentEngine.current() { |engine|
167
+ proxy = engine.restore(proxy)
168
+ proxy.target = Marshal.load(list[2]) if (list[2] > "")
169
+ }
170
+ proxy
171
+ end
172
+
173
+ protected
174
+
175
+ def build_command( symbol, *args )
176
+ AutomaticCommand.new( @proxy_id, symbol, *args )
177
+ end
178
+
179
+ def proceed(symbol, *args, &block)
180
+ engine = TransparentEngine.current
181
+ result = nil
182
+
183
+ if (engine.nil?)
184
+ # In this case the invocation happens on the object by user code
185
+ raise "Blocks are not supported... yet" if block_given?
186
+
187
+ TransparentEngine.within_engine( @engine_id ) { |engine|
188
+ result = engine.execute_command( build_command(symbol, *args) )
189
+ }
190
+ else
191
+ # In this case the invocation is happening within an execute_command
192
+ result = @target.send(symbol, *args)
193
+ end
194
+ result
195
+ end
196
+
197
+ end
198
+
199
+ class NestedTransparentProxy < TransparentProxy
200
+ attr_reader :related_symbol, :parent_proxy_id
201
+
202
+ def initialize(target, symbol, parent_proxy_id)
203
+ raise 'A nested-proxy must have a symbol' if symbol.nil?
204
+ super(target)
205
+ @related_symbol, @parent_proxy_id = symbol, parent_proxy_id
206
+ end
207
+
208
+ protected
209
+
210
+ def build_command( symbol, *args )
211
+ NestedAutomaticCommand.new( @related_symbol, @parent_proxy_id, @proxy_id, symbol, *args )
212
+ end
213
+
214
+ end
215
+
216
+ end
217
+
218
+ end
data/rake.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require_gem 'rake'
3
+
4
+ RakeApp.new.run
data/rakefile.rb ADDED
@@ -0,0 +1,62 @@
1
+
2
+ require 'rake'
3
+ require 'rubygems'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+
8
+ #$VERBOSE = nil
9
+ #require 'code_statistics'
10
+
11
+ #task :default => [ :test_units, :test_functional, :appdoc, :stats ]
12
+ desc "Run all the tests"
13
+ task :default => [ :test_units, :package ]
14
+
15
+ desc "Run the unit tests in test/unit"
16
+ Rake::TestTask.new("test_units") { |t|
17
+ t.libs << "test"
18
+ t.pattern = 'test/*_test.rb'
19
+ t.verbose = true
20
+ }
21
+
22
+ PKG_VERSION = '0.1.0'
23
+ PKG_FILES = FileList['README', './**/*.rb']
24
+
25
+ spec = Gem::Specification.new do |s|
26
+ s.name = 'hprevalence'
27
+ s.version = PKG_VERSION
28
+ s.summary = "Ruby based prevalence engine."
29
+ s.description = <<-EOF
30
+ HPrevalence is an prevalence engine for Ruby
31
+ which aims to provide non-intrusive capabilities to
32
+ prevalent object models.
33
+ EOF
34
+ #
35
+ s.files = PKG_FILES.to_a
36
+ #
37
+ s.require_path = 'lib'
38
+ s.autorequire = 'hprevalence'
39
+ #
40
+ s.has_rdoc = false
41
+ #
42
+ s.author = "Hamilton Verissimo"
43
+ s.email = "hammett@apache.org"
44
+ s.homepage = "http://www.digitalcraftsmen.com.br/~hammett"
45
+ s.rubyforge_project = "hprevalence"
46
+ end
47
+
48
+ desc "Creates the gem package for hprevalence"
49
+ Rake::GemPackageTask.new(spec) do |pkg|
50
+ pkg.need_zip = true
51
+ pkg.need_tar = true
52
+ end
53
+
54
+ desc "Generate documentation for the application"
55
+ Rake::RDocTask.new("appdoc") { |rdoc|
56
+ rdoc.rdoc_dir = 'doc/app'
57
+ rdoc.title = ""
58
+ rdoc.options << '--line-numbers --inline-source'
59
+ rdoc.rdoc_files.include('doc/README_FOR_APP')
60
+ rdoc.rdoc_files.include('**/*.rb')
61
+ }
62
+
@@ -0,0 +1,26 @@
1
+
2
+ module AbstractHPrevalenceTestHelper
3
+
4
+ def reset_dir( dir )
5
+ begin
6
+ current_dir = File.dirname(__FILE__)
7
+ @target_dir = File.join( current_dir, dir )
8
+ if File.exist?( @target_dir )
9
+ Dir.entries( @target_dir ).each do |file|
10
+ File.delete( File.join(@target_dir,file) ) unless File.directory?( file )
11
+ end
12
+ end
13
+ Dir.rmdir( @target_dir )
14
+ rescue
15
+ # puts 'could not reset dir ' + $!
16
+ end
17
+ end
18
+
19
+ def ensure_dir( dir = @target_dir )
20
+ begin
21
+ Dir.mkdir( dir )
22
+ rescue
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,39 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
+
3
+ require 'internal/command_logger'
4
+ require 'internal/iomanager'
5
+ require 'abstract_hprevalence_testcase'
6
+ require 'test/unit'
7
+
8
+ class CommandLoggerTest < Test::Unit::TestCase
9
+ include AbstractHPrevalenceTestHelper
10
+
11
+ def setup
12
+ reset_dir 'store/file_command_test'
13
+
14
+ @fc = CommandLogger.new( FileIOManager.new( @target_dir ) )
15
+ end
16
+
17
+ def test_available_logs
18
+ assert_equal 0, @fc.log_files.length
19
+
20
+ ensure_dir
21
+
22
+ assert_equal 0, @fc.log_files.length
23
+ end
24
+
25
+ def test_new_log
26
+ ensure_dir
27
+ assert_equal 0, @fc.log_files.length
28
+
29
+ command_store = @fc.create_command_store
30
+ assert_equal 1, @fc.log_files.length
31
+ assert_equal File.join(@target_dir, '000000000001.commandlog'), @fc.current_file_name
32
+
33
+ command_store = @fc.create_command_store
34
+ assert_equal 2, @fc.log_files.length
35
+ assert_equal File.join(@target_dir, '000000000002.commandlog'), @fc.current_file_name
36
+ end
37
+
38
+ end
39
+
@@ -0,0 +1,65 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
+
3
+ require 'internal/iomanager'
4
+ require 'internal/serializer'
5
+ require 'abstract_hprevalence_testcase'
6
+ require 'test/unit'
7
+ require 'task_model'
8
+
9
+ class DefaultModelSerializerTest < Test::Unit::TestCase
10
+ include AbstractHPrevalenceTestHelper
11
+
12
+ def setup
13
+ reset_dir 'store/default_model_serializer'
14
+ ensure_dir
15
+
16
+ @serializer = DefaultModelSerializer.new( FileIOManager.new( @target_dir ) )
17
+ end
18
+
19
+ def test_dump_and_load
20
+
21
+ db = TaskDatabase.new()
22
+ db.add_task( Task.new( 'one', 'yada yada yada' ) )
23
+ db.add_task( Task.new( 'two', 'yada yada yada' ) )
24
+ db.add_task( Task.new( 'three', 'yada yada yada' ) )
25
+ db.priority_task = db.tasks[0]
26
+
27
+ @serializer.dump( db )
28
+ db = @serializer.load
29
+
30
+ assert_equal 3, db.tasks.length
31
+ assert_equal db.priority_task.id, db.tasks[0].id
32
+
33
+ db.priority_task.contents = 'new content'
34
+
35
+ @serializer.dump( db )
36
+ db = @serializer.load
37
+
38
+ assert_equal db.priority_task.id, db.tasks[0].id
39
+ assert_equal db.priority_task.contents, db.tasks[0].contents
40
+
41
+ end
42
+
43
+ def test_multiple_snapshots
44
+
45
+ db = TaskDatabase.new()
46
+ db.add_task( Task.new( 'one', 'yada yada yada' ) )
47
+
48
+ @serializer.dump( db )
49
+
50
+ db.add_task( Task.new( 'two', 'yada yada yada' ) )
51
+
52
+ @serializer.dump( db )
53
+
54
+ db.add_task( Task.new( 'three', 'yada yada yada' ) )
55
+
56
+ @serializer.dump( db )
57
+
58
+ db = @serializer.load
59
+
60
+ assert_equal 3, db.tasks.length
61
+
62
+ end
63
+
64
+ end
65
+
@@ -0,0 +1,57 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
+
3
+ require 'transparent'
4
+
5
+ class DvdStore
6
+ include HPrevalence::Transparent::CustomBehavior
7
+
8
+ attr_reader :available_items
9
+ attr_reader :available_categories
10
+ attr_reader :customers
11
+
12
+ attr_proxy :available_items, :available_categories, :customers
13
+ attr_read_only :total_customers
14
+
15
+ def initialize()
16
+ @available_items = []
17
+ @available_categories = []
18
+ @customers = []
19
+ end
20
+
21
+ def total_customers
22
+ @customers.length
23
+ end
24
+
25
+ end
26
+
27
+ class Category
28
+ attr_reader :name
29
+
30
+ def initialize( name )
31
+ @name = name
32
+ end
33
+
34
+ end
35
+
36
+ class Dvd
37
+ attr_accessor :name, :price, :categories
38
+
39
+ def initialize( name, price, *categories )
40
+ @name, @price = name, price
41
+ @categories = *categories.dup()
42
+ end
43
+
44
+ end
45
+
46
+ class Customer
47
+ attr_accessor :name, :items_bought, :prefered_categories
48
+
49
+ def initialize( name )
50
+ @name = name
51
+ @items_bought = []
52
+ @prefered_categories = []
53
+ end
54
+
55
+ end
56
+
57
+
@@ -0,0 +1,129 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../lib/")
2
+
3
+ require 'internal/iomanager'
4
+ require 'abstract_hprevalence_testcase'
5
+ require 'test/unit'
6
+
7
+ class FileIOManagerTest < Test::Unit::TestCase
8
+ include AbstractHPrevalenceTestHelper
9
+
10
+ def setup
11
+ reset_dir 'store/file_io_manager_test'
12
+ ensure_dir
13
+
14
+ @filemanager = FileIOManager.new( @target_dir )
15
+ end
16
+
17
+ def test_create_new_stream
18
+
19
+ output = @filemanager.create_new_stream( 'test_extension' )
20
+ assert_not_nil output
21
+ assert_equal '000000000001.test_extension', output.basename
22
+
23
+ output.puts 'Testing'
24
+ output.close
25
+
26
+ end
27
+
28
+ def test_create_new_stream_twice
29
+
30
+ output1 = @filemanager.create_new_stream( 'test_extension' )
31
+ assert_not_nil output1
32
+ output2 = @filemanager.create_new_stream( 'test_extension' )
33
+ assert_not_nil output2
34
+
35
+ assert_equal '000000000001.test_extension', output1.basename
36
+ assert_equal '000000000002.test_extension', output2.basename
37
+
38
+ output1.close
39
+ output2.close
40
+
41
+ end
42
+
43
+ def test_list_simple_situation
44
+
45
+ @filemanager.create_new_stream( 'test' ).close
46
+ @filemanager.create_new_stream( 'test' ).close
47
+ @filemanager.create_new_stream( 'test' ).close
48
+
49
+ files = @filemanager.list( 'test' )
50
+
51
+ assert_not_nil files
52
+ assert_equal 3, files.length
53
+
54
+ assert_equal '000000000001.test', files[0]
55
+ assert_equal '000000000002.test', files[1]
56
+ assert_equal '000000000003.test', files[2]
57
+
58
+ end
59
+
60
+ def test_list_not_so_simple_situation
61
+
62
+ @filemanager.create_new_stream( 'test' ).close
63
+ @filemanager.create_new_stream( 'test' ).close
64
+ @filemanager.create_new_stream( 'test' ).close
65
+ @filemanager.create_new_stream( 'other' ).close
66
+ @filemanager.create_new_stream( 'other' ).close
67
+
68
+ files = @filemanager.list( 'test' )
69
+
70
+ assert_not_nil files
71
+ assert_equal 3, files.length
72
+
73
+ assert_equal '000000000001.test', files[0]
74
+ assert_equal '000000000002.test', files[1]
75
+ assert_equal '000000000003.test', files[2]
76
+
77
+ files = @filemanager.list( 'other' )
78
+
79
+ assert_not_nil files
80
+ assert_equal 2, files.length
81
+
82
+ assert_equal '000000000001.other', files[0]
83
+ assert_equal '000000000002.other', files[1]
84
+
85
+ end
86
+
87
+ def test_obtain_ordered_read_streams
88
+
89
+ output = @filemanager.create_new_stream( 'test' )
90
+ output.puts 'Testing 1'
91
+ output.close
92
+
93
+ output = @filemanager.create_new_stream( 'test' )
94
+ output.puts 'Testing 2'
95
+ output.close
96
+
97
+ streams = @filemanager.obtain_ordered_read_streams( 'test' )
98
+ assert_not_nil streams
99
+ assert_equal 2, streams.length
100
+
101
+ stream = streams[0]
102
+ assert_equal "Testing 1\n", stream.readline
103
+ stream.close
104
+
105
+ stream = streams[1]
106
+ assert_equal "Testing 2\n", stream.readline
107
+ stream.close
108
+
109
+ end
110
+
111
+ def test_obtain_latest_read_stream
112
+
113
+ output = @filemanager.create_new_stream( 'test' )
114
+ output.puts 'Testing 1'
115
+ output.close
116
+
117
+ output = @filemanager.create_new_stream( 'test' )
118
+ output.puts 'Testing 2'
119
+ output.close
120
+
121
+ stream = @filemanager.obtain_latest_read_stream( 'test' )
122
+ assert_not_nil stream
123
+ assert_equal "Testing 2\n", stream.readline
124
+ stream.close
125
+
126
+ end
127
+
128
+ end
129
+