hprevalence 0.1.0

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.
@@ -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
+