group_delegator 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/.document +5 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +120 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/examples/compare_to_map.rb +50 -0
- data/examples/diff_w_map.rb +21 -0
- data/examples/find_troublemakers.rb +12 -0
- data/examples/remote_component_update_sim.rb +47 -0
- data/examples/search_examples_with_benchmarks.rb +40 -0
- data/lib/group_delegator.rb +23 -0
- data/lib/group_delegator/group_delegator_instances.rb +32 -0
- data/lib/group_delegator/group_delegator_klasses.rb +62 -0
- data/lib/group_delegator/source_group.rb +161 -0
- data/lib/group_delegator/source_helper.rb +17 -0
- data/spec/group_delegator_instances_spec.rb +150 -0
- data/spec/group_delegator_klasses_spec.rb +270 -0
- data/spec/group_delegator_spec.rb +12 -0
- data/spec/source_group_spec.rb +259 -0
- data/spec/spec_helper.rb +12 -0
- metadata +152 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
class GroupDelegatorKlasses
|
2
|
+
include SourceHelper
|
3
|
+
#unload these methods so the proxy object will handle them
|
4
|
+
[:to_s,:inspect,:=~,:!~,:===].each do |m|
|
5
|
+
undef_method m
|
6
|
+
end
|
7
|
+
|
8
|
+
#source_classes is the container the holds the classes that will be proxied
|
9
|
+
class << self
|
10
|
+
#unload these methods so the proxy class will handle them
|
11
|
+
[:to_s,:inspect,:=~,:!~,:===].each do |m|
|
12
|
+
undef_method m
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :__class_source_group , :__all_class_methods, :__concurrency_model
|
16
|
+
|
17
|
+
#sets the classes that will be proxied
|
18
|
+
def __set_source_classes(classes_to_proxy, concurrency_model = :iterative)
|
19
|
+
@__concurrency_model = concurrency_model
|
20
|
+
sources_data = SourceHelper.set_sources_data(classes_to_proxy)
|
21
|
+
@source_obj_methods = sources_data[:source_methods]
|
22
|
+
@sources = sources_data[:source_objs]
|
23
|
+
@__all_class_methods = @source_obj_methods.keys
|
24
|
+
@__class_source_group = SourceGroup.new(@sources, concurrency_model) if @sources.size > 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def __source_classes
|
28
|
+
@sources
|
29
|
+
end
|
30
|
+
|
31
|
+
end #class<<self
|
32
|
+
|
33
|
+
#initializing class instance variables
|
34
|
+
self.__set_source_classes([])
|
35
|
+
|
36
|
+
def self.method_missing(m, *args, &block)
|
37
|
+
if self.__all_class_methods.include? m
|
38
|
+
resp = self.__class_source_group.forward(m, *args, &block)
|
39
|
+
else
|
40
|
+
raise NoMethodError, "#{self.class} can't find the class method #{m} in any of its sources"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(*args)
|
45
|
+
#changed self to self.class
|
46
|
+
concurrency_model = self.class.__concurrency_model
|
47
|
+
raise "No Source Classes set" unless self.class.__source_classes.size > 0
|
48
|
+
proxied_objs = self.class.__source_classes.map {|klass| klass.new(*args) }
|
49
|
+
sources_data = SourceHelper.set_sources_data(proxied_objs)
|
50
|
+
@source_obj_methods = sources_data[:source_methods]
|
51
|
+
@source_objects = sources_data[:source_objs]
|
52
|
+
@instance_source_group = SourceGroup.new(@source_objects, concurrency_model)
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(m, *args, &block)
|
56
|
+
if @source_obj_methods.include? m
|
57
|
+
resp = @instance_source_group.forward(m, *args, &block)
|
58
|
+
else
|
59
|
+
raise NoMethodError, "#{self.class} object can't find the method #{m} in any of its sources"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
#This class is the container for the objects that will receive common method calls
|
4
|
+
# It also manages the concurrency model to be used when performing the method calls
|
5
|
+
class SourceGroup
|
6
|
+
attr_accessor :concurrency_model #:sources, :valid_response_trace, :invalid_response_list
|
7
|
+
|
8
|
+
#Built-in concurrency models for delegating methods
|
9
|
+
##execute forwarding in an iterative fashion
|
10
|
+
IterativeBlock = lambda{ |sources, m, *args, &block|
|
11
|
+
all_resps = {}
|
12
|
+
sources.each do |source|
|
13
|
+
this_resp = {}
|
14
|
+
begin
|
15
|
+
#collect valid responses
|
16
|
+
all_resps[:valid] ||= {}
|
17
|
+
all_resps[:valid][source] = source.__send__(m, *args, &block)
|
18
|
+
rescue NoMethodError
|
19
|
+
#oops we have some invalid responses, collect those too
|
20
|
+
all_resps[:invalid] ||= []
|
21
|
+
all_resps[:invalid] << source
|
22
|
+
end
|
23
|
+
end
|
24
|
+
all_resps
|
25
|
+
}
|
26
|
+
|
27
|
+
##If we like speed (with a dash of danger) we can thread the requests rather than iterate
|
28
|
+
ThreadedBlock= lambda{ |sources, m, *args, &block|
|
29
|
+
all_resps = {}
|
30
|
+
threads = []
|
31
|
+
sources.each do |source|
|
32
|
+
threads << Thread.new(source) do |src|
|
33
|
+
Thread.current[:src] = src
|
34
|
+
begin
|
35
|
+
Thread.current[:resp] = src.__send__(m, *args, &block)
|
36
|
+
rescue
|
37
|
+
Thread.current[:err] = src
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
threads.each do |t|
|
43
|
+
t.join
|
44
|
+
src = t[:src] #proxied object
|
45
|
+
if t[:resp]
|
46
|
+
#valid response
|
47
|
+
all_resps[:valid] ||= {}
|
48
|
+
all_resps[:valid][src] = t[:resp]
|
49
|
+
elsif t[:err]
|
50
|
+
#oops error
|
51
|
+
all_resps[:invalid] ||= []
|
52
|
+
all_resps[:invalid] << t[:err]
|
53
|
+
else
|
54
|
+
raise "source returned an invalid responseto its thread."\
|
55
|
+
"Response thread: #{t} source: #{source.inspect}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
all_resps
|
59
|
+
}
|
60
|
+
|
61
|
+
##More speed (with more danger) we can use the first valid response (note the change in t.join)
|
62
|
+
##How to react to the first response, maybe fibers?
|
63
|
+
ThreadedFirstResponseBlock= lambda{ |sources, m, *args, &block|
|
64
|
+
t0 = Time.now
|
65
|
+
first_resp = {}
|
66
|
+
source_threads = []
|
67
|
+
queue = Queue.new
|
68
|
+
sources.each do |source|
|
69
|
+
source_threads << Thread.new do
|
70
|
+
begin
|
71
|
+
queue << { source => source.__send__(m, *args, &block)}
|
72
|
+
rescue
|
73
|
+
Thread.current[:err] = source #these errored out before a valid entry in queue
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
valid_response = nil
|
79
|
+
#limit the time for responses
|
80
|
+
check_queue = Thread.new do
|
81
|
+
until valid_response do
|
82
|
+
sleep 0.01 #don't consume all available resources on a silly event loop
|
83
|
+
valid_response = queue.shift #shift not pop in case more than one response in queue
|
84
|
+
end
|
85
|
+
Thread.current[:q_response] = valid_response
|
86
|
+
end
|
87
|
+
|
88
|
+
#Continue if all source_threads finish, but if check_queue finishes first, continue regardless of source_threads status
|
89
|
+
any_thread_running = true
|
90
|
+
while any_thread_running do
|
91
|
+
sleep 0.01
|
92
|
+
any_src_thr_alive = source_threads.inject(false) {|alive, thr| alive || thr.status}
|
93
|
+
any_thread_running = check_queue.status && any_src_thr_alive
|
94
|
+
end
|
95
|
+
|
96
|
+
t1 = Time.now
|
97
|
+
|
98
|
+
if (t1-t0) < 0.1
|
99
|
+
sleep 0.05 #give time fot things to stabilize
|
100
|
+
end
|
101
|
+
#puts "Response from queue: #{check_queue[:q_response].inspect}"
|
102
|
+
|
103
|
+
|
104
|
+
invalid_resps = []
|
105
|
+
source_threads.each do |t|
|
106
|
+
invalid_resps << t[:err] if t[:err]
|
107
|
+
end
|
108
|
+
#returning source_threads so that the caller can join them if needed (i.e. ending in a know state)
|
109
|
+
#invalid_responses only contains responsed from threads that completed prior to the first valid response
|
110
|
+
first_resp = { :valid => valid_response, :invalid => invalid_resps, :threads => source_threads }
|
111
|
+
}
|
112
|
+
|
113
|
+
#
|
114
|
+
def initialize(sources, concurrency_model = :iterative)
|
115
|
+
@sources = sources
|
116
|
+
@concurrency_model = concurrency_model
|
117
|
+
end
|
118
|
+
|
119
|
+
def forward(m, *args, &block)
|
120
|
+
forward_custom(@concurrency_model, m, *args, &block)
|
121
|
+
end
|
122
|
+
|
123
|
+
def forward_custom(forward_method, m, *args, &block)
|
124
|
+
forward_block = case forward_method
|
125
|
+
when :iterative
|
126
|
+
IterativeBlock
|
127
|
+
when :threaded
|
128
|
+
ThreadedBlock
|
129
|
+
when :first_response
|
130
|
+
ThreadedFirstResponseBlock
|
131
|
+
when Proc
|
132
|
+
forward_method
|
133
|
+
else
|
134
|
+
raise "Invalid parameter: #{forward_method.inspect}"
|
135
|
+
end
|
136
|
+
|
137
|
+
@valid_response_trace = {}
|
138
|
+
@invalid_response_list = []
|
139
|
+
sources = @sources
|
140
|
+
if sources.size > 0
|
141
|
+
resp = forward_block.call(sources, m, *args, &block)
|
142
|
+
@valid_response_trace = resp[:valid]
|
143
|
+
else
|
144
|
+
raise "No sources assigned"
|
145
|
+
end
|
146
|
+
@valid_response_trace
|
147
|
+
end
|
148
|
+
|
149
|
+
#just some sugar
|
150
|
+
def forward_iterative(m, *args, &block)
|
151
|
+
forward_custom(:iterative, m, *args, & block)
|
152
|
+
end
|
153
|
+
|
154
|
+
def forward_threaded(m, *args, &block)
|
155
|
+
forward_custom(:threaded, m, *args, & block)
|
156
|
+
end
|
157
|
+
|
158
|
+
def forward_first_resp(m, *args, &block)
|
159
|
+
forward_custom(:first_response, m, *args, &block)
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SourceHelper
|
2
|
+
def self.set_sources_data(proxied_objs)
|
3
|
+
source_obj_methods = {} #map of all methods to the objects that use them
|
4
|
+
proxied_objs.each do |proxied_obj|
|
5
|
+
proxied_obj.methods.each do |proxy_method|
|
6
|
+
source_obj_methods[proxy_method] ||= [proxied_obj]
|
7
|
+
source_obj_methods[proxy_method] << proxied_obj
|
8
|
+
end
|
9
|
+
end
|
10
|
+
{:source_methods => source_obj_methods, :source_objs => proxied_objs}
|
11
|
+
end
|
12
|
+
|
13
|
+
def __set_sources_data(proxied_objs)
|
14
|
+
raise "No source instances set" unless proxied_objs.size > 0
|
15
|
+
SourceHelper.set_sources_data(proxied_objs)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
#classes for testing
|
5
|
+
class A
|
6
|
+
def common_method
|
7
|
+
:a
|
8
|
+
end
|
9
|
+
def some_method
|
10
|
+
:aa
|
11
|
+
end
|
12
|
+
def a_method
|
13
|
+
:aaa
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class B
|
18
|
+
def common_method
|
19
|
+
:b
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class C
|
24
|
+
def common_method
|
25
|
+
:c
|
26
|
+
end
|
27
|
+
def some_method
|
28
|
+
:cc
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
shared_examples_for "instance delegator - entire collection" do
|
33
|
+
it "passes methods to the underlying instances of the group" do
|
34
|
+
group_obj.common_method.should == {@a_obj => :a, @b_obj => :b, @c_obj => :c}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
shared_examples_for "instance delegator - first response" do
|
39
|
+
it "passes methods to the first responing instance" do
|
40
|
+
expected_result_set = {@a_obj=>:a, @b_obj=>:b, @c_obj=>:c}
|
41
|
+
result = group_obj.common_method
|
42
|
+
result_key = result.keys.first
|
43
|
+
expected_result_set.keys.should include result_key
|
44
|
+
result_value = result.values.first
|
45
|
+
expected_result_set.values.should include result_value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "delegating to a group of instance objects" do
|
50
|
+
before(:each) do
|
51
|
+
@a_obj = A.new; @b_obj = B.new; @c_obj = C.new
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "default concurrency model (iterative)" do
|
55
|
+
it_should_behave_like "instance delegator - entire collection" do
|
56
|
+
let(:group_obj) { GroupDelegatorInstances.new([@a_obj, @b_obj, @c_obj]) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "iterative concurrency model" do
|
61
|
+
it_should_behave_like "instance delegator - entire collection" do
|
62
|
+
let(:group_obj) { GroupDelegatorInstances.new([@a_obj, @b_obj, @c_obj], :iterative) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "threaded concurrency model" do
|
67
|
+
it_should_behave_like "instance delegator - entire collection" do
|
68
|
+
let(:group_obj) { GroupDelegatorInstances.new([@a_obj, @b_obj, @c_obj], :threaded) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "first response concurrency model" do
|
73
|
+
it_should_behave_like "instance delegator - first response" do
|
74
|
+
let(:group_obj) { GroupDelegatorInstances.new([@a_obj, @b_obj, @c_obj], :first_response) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
class BenchA
|
82
|
+
def common_method
|
83
|
+
sleep 0.3
|
84
|
+
:a
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class BenchB
|
89
|
+
def common_method
|
90
|
+
sleep 0.2
|
91
|
+
:b
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class BenchC
|
96
|
+
def common_method
|
97
|
+
sleep 0.1
|
98
|
+
:c
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "benchmarks" do
|
103
|
+
before(:all) do
|
104
|
+
@execution_times = {}
|
105
|
+
end
|
106
|
+
|
107
|
+
after(:all) do
|
108
|
+
p @execution_times
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "iterative" do
|
112
|
+
before(:each) do
|
113
|
+
@a_obj = BenchA.new; @b_obj = BenchB.new; @c_obj = BenchC.new
|
114
|
+
@group_obj = GroupDelegatorInstances.new([@a_obj, @b_obj, @c_obj], :iterative)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "executes" do
|
118
|
+
@execution_times[:iterative] = Benchmark.realtime { @group_obj.common_method }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "threaded" do
|
123
|
+
before(:each) do
|
124
|
+
@a_obj = BenchA.new; @b_obj = BenchB.new; @c_obj = BenchC.new
|
125
|
+
@group_obj = GroupDelegatorInstances.new([@a_obj, @b_obj, @c_obj], :threaded)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "executes" do
|
129
|
+
@execution_times[:threaded] = Benchmark.realtime { @group_obj.common_method }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "first response" do
|
134
|
+
before(:each) do
|
135
|
+
@a_obj = BenchA.new; @b_obj = BenchB.new; @c_obj = BenchC.new
|
136
|
+
@group_obj = GroupDelegatorInstances.new([@a_obj, @b_obj, @c_obj], :first_response)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "executes" do
|
140
|
+
@execution_times[:first_response] = Benchmark.realtime { @group_obj.common_method }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "results" do
|
145
|
+
it "checks the order of results" do
|
146
|
+
@execution_times[:first_response].should < @execution_times[:threaded]
|
147
|
+
@execution_times[:threaded].should < @execution_times[:iterative]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
#classes for testing
|
5
|
+
class A
|
6
|
+
attr_accessor :iv
|
7
|
+
def self.klass_method
|
8
|
+
:A
|
9
|
+
end
|
10
|
+
def self.a_klass_method
|
11
|
+
:AAA
|
12
|
+
end
|
13
|
+
def initialize(params)
|
14
|
+
@iv = params
|
15
|
+
end
|
16
|
+
def common_method
|
17
|
+
:a
|
18
|
+
end
|
19
|
+
def a_method
|
20
|
+
:aaa
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class B
|
25
|
+
attr_accessor :iv
|
26
|
+
def self.klass_method
|
27
|
+
:B
|
28
|
+
end
|
29
|
+
def initialize(params)
|
30
|
+
@iv = params
|
31
|
+
end
|
32
|
+
def common_method
|
33
|
+
:b
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class C
|
38
|
+
attr_accessor :iv
|
39
|
+
def self.klass_method
|
40
|
+
:C
|
41
|
+
end
|
42
|
+
def initialize(params)
|
43
|
+
@iv = params
|
44
|
+
end
|
45
|
+
def common_method
|
46
|
+
:c
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
shared_examples_for "class delegator - entire collection" do
|
52
|
+
it "passes methods to the source classes in the group" do
|
53
|
+
group_klass.klass_method.should == {A=>:A, B=>:B, C=>:C}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
shared_examples_for "class delegator - first response" do
|
58
|
+
it "passes methods to the source classes in the group" do
|
59
|
+
expected_result_set = {A=>:A, B=>:B, C=>:C}
|
60
|
+
result = group_klass.klass_method
|
61
|
+
result_key = result.keys.first
|
62
|
+
expected_result_set.keys.should include result_key
|
63
|
+
result_value = result.values.first
|
64
|
+
expected_result_set.values.should include result_value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
shared_examples_for "class delegator - initializing objects" do
|
69
|
+
it "should initialize a group of objects based on the group of classes" do
|
70
|
+
source_classes = [ A, B, C ]
|
71
|
+
objs_iv = group_obj.iv
|
72
|
+
objs_iv.each_with_index do |obj_inst_var, i|
|
73
|
+
obj = obj_inst_var[0]
|
74
|
+
inst_var = obj_inst_var[1]
|
75
|
+
obj.class.should == source_classes[i]
|
76
|
+
inst_var.should == :some_init_params
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
shared_examples_for "instance delegator - entire collection" do
|
82
|
+
it "passes methods to the underlying instances of the group" do
|
83
|
+
group_obj.common_method.values == [:a, :b, :c]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
shared_examples_for "instance delegator - first response" do
|
88
|
+
it "passes methods to the first responing instance" do
|
89
|
+
expected_result_set = [:a, :b, :c]
|
90
|
+
result = group_obj.common_method
|
91
|
+
result_value = result.values.first
|
92
|
+
expected_result_set.should include result_value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "delegating to a group of classes" do
|
97
|
+
before(:each) do
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "default concurrency model (iterative)" do
|
102
|
+
it_should_behave_like "class delegator - entire collection" do
|
103
|
+
#One line inheritance to prevent clobbering GroupDelegatorKlasses class inst var
|
104
|
+
DefaultGDK = Class.new(GroupDelegatorKlasses)
|
105
|
+
DefaultGDK.__set_source_classes( [A, B, C] )
|
106
|
+
let(:group_klass) { DefaultGDK }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "iterative concurrency model" do
|
111
|
+
it_should_behave_like "class delegator - entire collection" do
|
112
|
+
IterGDK = Class.new(GroupDelegatorKlasses)
|
113
|
+
IterGDK.__set_source_classes( [A, B, C], :iterative )
|
114
|
+
let(:group_klass) { IterGDK }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "threaded concurrency model" do
|
119
|
+
it_should_behave_like "class delegator - entire collection" do
|
120
|
+
ThreadGDK = Class.new(GroupDelegatorKlasses)
|
121
|
+
ThreadGDK.__set_source_classes( [A, B, C], :threaded )
|
122
|
+
let(:group_klass) { ThreadGDK }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "first response concurrency model" do
|
127
|
+
it_should_behave_like "class delegator - first response" do
|
128
|
+
FirstRespGDK = Class.new(GroupDelegatorKlasses)
|
129
|
+
FirstRespGDK.__set_source_classes( [A, B, C], :first_response )
|
130
|
+
let(:group_klass) { FirstRespGDK }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "default group initialization" do
|
135
|
+
it_should_behave_like "class delegator - initializing objects" do
|
136
|
+
DefObjGDK = Class.new(GroupDelegatorKlasses)
|
137
|
+
DefObjGDK.__set_source_classes( [A, B, C] )
|
138
|
+
let(:group_obj) { DefObjGDK.new(:some_init_params) }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "group initialization, iterative" do
|
143
|
+
it_should_behave_like "class delegator - initializing objects" do
|
144
|
+
IterObjGDK = Class.new(GroupDelegatorKlasses)
|
145
|
+
IterObjGDK.__set_source_classes( [A, B, C], :iterative )
|
146
|
+
let(:group_obj) { IterObjGDK.new(:some_init_params) }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "group initialization, threaded" do
|
151
|
+
it_should_behave_like "class delegator - initializing objects" do
|
152
|
+
ThreadObjGDK = Class.new(GroupDelegatorKlasses)
|
153
|
+
ThreadObjGDK.__set_source_classes( [A, B, C], :iterative )
|
154
|
+
let(:group_obj) { ThreadObjGDK.new(:some_init_params) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "group initialization, first response" do
|
159
|
+
it_should_behave_like "instance delegator - first response" do
|
160
|
+
FirstRespObjGDK = Class.new(GroupDelegatorKlasses)
|
161
|
+
FirstRespObjGDK.__set_source_classes( [A, B, C], :first_response )
|
162
|
+
let(:group_obj) { FirstRespObjGDK.new(:some_init_params) }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "newly created instances should behave as grouped delegates" do
|
168
|
+
describe "default concurrency model (iterative)" do
|
169
|
+
it_should_behave_like "instance delegator - entire collection" do
|
170
|
+
let(:group_obj) { DefObjGDK.new(:some_other_params) }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "iterative concurrency model" do
|
175
|
+
it_should_behave_like "instance delegator - entire collection" do
|
176
|
+
let(:group_obj) { IterObjGDK.new(:some_other_params) }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe "threaded concurrency model" do
|
181
|
+
it_should_behave_like "instance delegator - entire collection" do
|
182
|
+
let(:group_obj) { ThreadObjGDK.new(:some_other_params) }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "first response concurrency model" do
|
187
|
+
it_should_behave_like "instance delegator - first response" do
|
188
|
+
let(:group_obj) { FirstRespObjGDK.new(:some_other_params) }
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
class BenchA
|
195
|
+
def common_method
|
196
|
+
sleep 0.3
|
197
|
+
:a
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class BenchB
|
202
|
+
def common_method
|
203
|
+
sleep 0.2
|
204
|
+
:b
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class BenchC
|
209
|
+
def common_method
|
210
|
+
sleep 0.1
|
211
|
+
:c
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "benchmarks" do
|
216
|
+
before(:all) do
|
217
|
+
@execution_times = {}
|
218
|
+
end
|
219
|
+
|
220
|
+
after(:all) do
|
221
|
+
p @execution_times
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "iterative" do
|
225
|
+
before(:each) do
|
226
|
+
#@a_obj = BenchA.new; @b_obj = BenchB.new; @c_obj = BenchC.new
|
227
|
+
BenchObjGDK = Class.new(GroupDelegatorKlasses)
|
228
|
+
BenchObjGDK.__set_source_classes( [BenchA, BenchB, BenchC], :iterative )
|
229
|
+
@bench_obj = BenchObjGDK.new
|
230
|
+
end
|
231
|
+
|
232
|
+
it "executes" do
|
233
|
+
@execution_times[:iterative] = Benchmark.realtime { @bench_obj.common_method }
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "threaded" do
|
238
|
+
before(:each) do
|
239
|
+
#@a_obj = BenchA.new; @b_obj = BenchB.new; @c_obj = BenchC.new
|
240
|
+
BenchObjGDK = Class.new(GroupDelegatorKlasses)
|
241
|
+
BenchObjGDK.__set_source_classes( [BenchA, BenchB, BenchC], :threaded )
|
242
|
+
@bench_obj = BenchObjGDK.new
|
243
|
+
end
|
244
|
+
|
245
|
+
it "executes" do
|
246
|
+
@execution_times[:threaded] = Benchmark.realtime { @bench_obj.common_method }
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "first response" do
|
251
|
+
before(:each) do
|
252
|
+
#@a_obj = BenchA.new; @b_obj = BenchB.new; @c_obj = BenchC.new
|
253
|
+
BenchObjGDK = Class.new(GroupDelegatorKlasses)
|
254
|
+
BenchObjGDK.__set_source_classes( [BenchA, BenchB, BenchC], :first_response )
|
255
|
+
@bench_obj = BenchObjGDK.new
|
256
|
+
end
|
257
|
+
|
258
|
+
it "executes" do
|
259
|
+
@execution_times[:first_response] = Benchmark.realtime { @bench_obj.common_method }
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
describe "results" do
|
265
|
+
it "checks the order of results" do
|
266
|
+
@execution_times[:first_response].should < @execution_times[:threaded]
|
267
|
+
@execution_times[:threaded].should < @execution_times[:iterative]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|