asir 1.2.8 → 1.2.9

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,10 @@
1
+ 2012-06-19 Kurt A. Stephens <ks.github@kurtstephens.com>
2
+
3
+ * v1.2.9: New Version: Fixes, New Functionality.
4
+ * ASIR::Error::Unforwardable.unforwardable is deprecated; use .modules.
5
+ * ASIR::AdaptiveValue: refactor ASIR::RetryBehavior sleep values.
6
+ * BUG: Transport#after_receive_message.call takes a Message::State.
7
+
1
8
  2012-03-14 Kurt A. Stephens <ks.github@kurtstephens.com>
2
9
 
3
10
  * v1.2.8: New Version: Additional functionality.
@@ -0,0 +1,81 @@
1
+ module ASIR
2
+ # Adaptive Value.
3
+ # Return a Numeric #value which adapts.
4
+ # Useful for controlling adaptive retry sleep amounts or other
5
+ # values that must be stochastic.
6
+ #
7
+ # #init must be specified.
8
+ # #rand_factor should be a Float.
9
+ #
10
+ class AdaptiveValue
11
+ attr_accessor :init, :min, :max, :add, :mult, :rand_factor
12
+
13
+ def initialize opts = nil
14
+ if opts
15
+ opts.each do | k, v |
16
+ send(:"#{k}=", v)
17
+ end
18
+ end
19
+ end
20
+
21
+ # Returns a new value limited by #min and #max after applying the addition of #rand_factor.
22
+ def value
23
+ v = @value || init_or_error
24
+ v += v * rand(@rand_factor) if @rand_factor
25
+ v = @min if @min && v < @min
26
+ v = @max if @max && v > @max
27
+ v
28
+ end
29
+ def value= x
30
+ @value = x
31
+ end
32
+
33
+ # Returns a cached #value until #reset! or #new_value!.
34
+ def value!
35
+ @value_ ||= value
36
+ end
37
+
38
+ # Returns a new cached #value.
39
+ def new_value!
40
+ @value_ = nil
41
+ value!
42
+ end
43
+
44
+ # Resets #value to #init.
45
+ def reset!
46
+ @value_ = @value = nil
47
+ self
48
+ end
49
+
50
+ def to_i
51
+ x = value.to_i
52
+ adapt!
53
+ x
54
+ end
55
+
56
+ def to_f
57
+ x = value.to_f
58
+ adapt!
59
+ x
60
+ end
61
+
62
+ # Increments value by #add, if #add is set.
63
+ # Multiplies value by #mult, if #mult is set.
64
+ # Limits value by #min and #max.
65
+ def adapt!
66
+ @value ||= init_or_error
67
+ @value += @add if @add
68
+ @value *= @mult if @mult
69
+ @value = @min if @min && @value < @min
70
+ @value = @max if @max && @value > @max
71
+ self
72
+ end
73
+
74
+ private
75
+ def init_or_error
76
+ @init or raise ArgumentError, "init: not set"
77
+ end
78
+
79
+ end
80
+ end
81
+
@@ -39,9 +39,9 @@ module ASIR
39
39
  super(msg)
40
40
  self.set_backtrace original && original.backtrace
41
41
  end
42
- def self.unforwardable; @@unforwardable; end
43
- def self.unforwardable= x; @@unforwardable = x; end
44
- @@unforwardable ||= [
42
+ def self.modules; @@modules; end
43
+ def self.modules= x; @@modules = x; end
44
+ @@modules ||= [
45
45
  ::SystemExit,
46
46
  ::SystemStackError,
47
47
  ::NoMemoryError,
@@ -377,7 +377,7 @@ END
377
377
  transport.after_receive_message = lambda do | transport, state |
378
378
  message = state.message
379
379
  $0 = "#{old_arg0} #{transport.message_count} #{message.identifier}"
380
- after_receive_message.call(transport, message)
380
+ after_receive_message.call(transport, state)
381
381
  end
382
382
  transport.run_server!
383
383
  self
@@ -18,7 +18,7 @@ module ASIR
18
18
 
19
19
  def invoke!
20
20
  @result = Result.new(self, @receiver.__send__(@selector, *@arguments))
21
- rescue *Error::Unforwardable.unforwardable => exc
21
+ rescue *Error::Unforwardable.modules => exc
22
22
  @result = Result.new(self, nil, Error::Unforwardable.new(exc))
23
23
  rescue ::Exception => exc
24
24
  @result = Result.new(self, nil, exc)
@@ -1,3 +1,6 @@
1
+ require 'asir/error'
2
+ require 'asir/adaptive_value'
3
+
1
4
  module ASIR
2
5
  # !SLIDE
3
6
  # Generic retry behavior
@@ -12,13 +15,15 @@ module ASIR
12
15
  attr_accessor :try_sleep_max
13
16
 
14
17
  # Yields:
15
- # :try, n_try
16
- # :rescue, exc
17
- # :retry, exc
18
- # :failed, nil
18
+ # :try, n_try - for each attempt.
19
+ # :rescue, exc - for any rescued exception.
20
+ # :retry, exc - before each retry.
21
+ # :failed, last_exc - when too many retrys occurred.
19
22
  def with_retry
20
23
  n_try = 0
21
- sleep_secs = try_sleep
24
+ @try_sleep_value ||= ::ASIR::AdaptiveValue.new
25
+ @try_sleep_value.init = try_sleep
26
+ @try_sleep_value.reset!
22
27
  result = done = last_exception = nil
23
28
  begin
24
29
  n_try += 1
@@ -26,24 +31,27 @@ module ASIR
26
31
  done = true
27
32
  rescue *Error::Unrecoverable.modules
28
33
  raise
29
- rescue *Error::Unforwardable.unforwardable
34
+ rescue *Error::Unforwardable.modules
30
35
  raise
31
36
  rescue ::Exception => exc
32
- last_exception = exc
37
+ last_exc = exc
33
38
  yield :rescue, exc
34
39
  if ! try_max || try_max > n_try
35
40
  yield :retry, exc
36
- if sleep_secs
41
+ if try_sleep
42
+ sleep_secs = @try_sleep_value.value
37
43
  sleep sleep_secs if sleep_secs > 0
38
- sleep_secs += try_sleep_increment if try_sleep_increment
39
- sleep_secs = try_sleep_max if try_sleep_max && sleep_secs > try_sleep_max
44
+ @try_sleep_value.init = try_sleep
45
+ @try_sleep_value.add = try_sleep_increment
46
+ @try_sleep_value.max = try_sleep_max
47
+ @try_sleep_value.adapt!
40
48
  end
41
49
  retry
42
50
  end
43
51
  end
44
52
  unless done
45
- unless yield :failed, last_exception
46
- exc = last_exception
53
+ unless yield :failed, last_exc
54
+ exc = last_exc
47
55
  raise RetryError, "Retry failed: #{exc.inspect}", exc.backtrace
48
56
  end
49
57
  end
@@ -165,7 +165,7 @@ module ASIR
165
165
  if message_ok
166
166
  if exception && ! result_ok
167
167
  case exception
168
- when *Error::Unforwardable.unforwardable
168
+ when *Error::Unforwardable.modules
169
169
  unforwardable_exception = exception = Error::Unforwardable.new(exception)
170
170
  end
171
171
  state.result = Result.new(state.message, nil, exception)
@@ -1,3 +1,3 @@
1
1
  module ASIR
2
- VERSION = "1.2.8"
2
+ VERSION = "1.2.9"
3
3
  end
@@ -0,0 +1,103 @@
1
+ require 'asir/adaptive_value'
2
+
3
+ describe ASIR::AdaptiveValue do
4
+ it "should have a #value of #init." do
5
+ av = ASIR::AdaptiveValue.new(:init => 5)
6
+ av.value.should == 5
7
+ av.value.should == 5
8
+ av.value!.should == 5
9
+ end
10
+
11
+ it "should have a #value limited by #min." do
12
+ av = ASIR::AdaptiveValue.new(:init => 5, :min => 10)
13
+ av.value.should == 10
14
+ av.value.should == 10
15
+ av.value!.should == 10
16
+ end
17
+
18
+ it "should have a #value not limited by #min." do
19
+ av = ASIR::AdaptiveValue.new(:init => 15, :min => 10)
20
+ av.value.should == 15
21
+ av.value.should == 15
22
+ av.value!.should == 15
23
+ end
24
+
25
+ it "should have a #value limited by #max." do
26
+ av = ASIR::AdaptiveValue.new(:init => 5, :max => 10)
27
+ av.value.should == 5
28
+ av.value.should == 5
29
+ av.value!.should == 5
30
+ end
31
+
32
+ it "should have a #value not limited by #max." do
33
+ av = ASIR::AdaptiveValue.new(:init => 15, :max => 10)
34
+ av.value.should == 10
35
+ av.value.should == 10
36
+ av.value!.should == 10
37
+ end
38
+
39
+ it "should have a #value adjusted by #add." do
40
+ av = ASIR::AdaptiveValue.new(:init => 1, :add => 2)
41
+ av.value.should == 1
42
+ av.value.should == 1
43
+ av.adapt!
44
+ av.value.should == 3
45
+ av.value.should == 3
46
+ av.value!.should == 3
47
+ av.adapt!
48
+ av.value.should == 5
49
+ av.value.should == 5
50
+ av.value!.should == 3
51
+ end
52
+
53
+ it "should have a #value adjusted by #mult." do
54
+ av = ASIR::AdaptiveValue.new(:init => 10, :mult => 1.2)
55
+ av.value.should == 10
56
+ av.value.should == 10
57
+ av.adapt!
58
+ av.value.to_i.should == 12
59
+ av.value.to_i.should == 12
60
+ av.value!.to_i.should == 12
61
+ av.adapt!
62
+ av.value.to_i.should == 14
63
+ av.value.to_i.should == 14
64
+ av.value!.to_i.should == 12
65
+ end
66
+
67
+ it "should have an adapting #to_i and #to_f value." do
68
+ av = ASIR::AdaptiveValue.new(:init => 10, :mult => 1.2)
69
+ av.to_i.should == 10
70
+ av.to_f.should be_within(0.1).of(12.0)
71
+ av.to_f.should be_within(0.1).of(14.4)
72
+ av.to_f.should be_within(0.01).of(17.28)
73
+ av.to_i.should == 20
74
+ end
75
+
76
+ it "should have a #value adjusted by #rand_factor." do
77
+ av = ASIR::AdaptiveValue.new(:init => 10, :rand_factor => 0.5)
78
+ def av._ri
79
+ @_ri ||= -1
80
+ @_ri += 1
81
+ end
82
+ def av._rv
83
+ @_rv ||= (0...10).to_a
84
+ end
85
+ def av.rand scale
86
+ (_rv[_ri % _rv.size] / 10.0) * scale
87
+ end
88
+
89
+ av.value.should == 10
90
+ av.value.should == 10.5
91
+ av.value!.should == 11.0
92
+ av.value.should == 11.5
93
+ av.value.should == 12.0
94
+ av.value!.should == 11.0
95
+ end
96
+
97
+ it "should raise error if #init is not set." do
98
+ av = ASIR::AdaptiveValue.new(:rand_factor => 0.5)
99
+ lambda do
100
+ av.value
101
+ end.should raise_error(ArgumentError, /init: not set/)
102
+ end
103
+ end
@@ -47,7 +47,7 @@ describe "ASIR::Message" do
47
47
  end
48
48
 
49
49
  it 'should capture Unforwardable exceptions.' do
50
- cls = ::ASIR::Error::Unforwardable.unforwardable.first
50
+ cls = ::ASIR::Error::Unforwardable.modules.first
51
51
  cls.should_not == nil
52
52
  msg = "This message".freeze
53
53
  message.selector = :raise_exception!
@@ -0,0 +1,56 @@
1
+ require 'asir/retry_behavior'
2
+
3
+ describe ASIR::RetryBehavior do
4
+ let(:cls) do
5
+ Class.new do
6
+ include ASIR::RetryBehavior
7
+ attr_accessor :sleeps
8
+ def sleeps; @sleeps ||= [ ]; end
9
+ def sleep x; sleeps << x; end
10
+ def yields; @yields ||= [ ]; end
11
+ def yielder &blk
12
+ Proc.new do | kind, value |
13
+ yields << [ kind, value ]
14
+ blk.call kind, value
15
+ end
16
+ end
17
+ end
18
+ end
19
+ subject { cls.new }
20
+
21
+ it 'should retry only try_max times, then raise RetryError' do
22
+ blk = subject.yielder do | kind, value |
23
+ raise "Fail" if kind == :try
24
+ end
25
+ subject.try_max = 10
26
+ lambda do
27
+ subject.with_retry(&blk)
28
+ end.should raise_error(ASIR::RetryBehavior::RetryError)
29
+ subject.sleeps.should == [ ]
30
+ subject.yields.select{|x| x[0] == :try}.map{|x| x[1]}.should == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
31
+ subject.yields.select{|x| x[0] == :rescue}.map{|x| x[1]}.size.should == 10
32
+ subject.yields.select{|x| x[0] == :retry}.map{|x| x[1]}.size.should == 9
33
+ subject.yields.select{|x| x[0] == :failed}.map{|x| x[1]}.size.should == 1
34
+ end
35
+
36
+ it 'should sleep for increasing amounts' do
37
+ blk = subject.yielder do | kind, value |
38
+ raise "Fail" if kind == :try
39
+ end
40
+ subject.try_max = 10
41
+ subject.try_sleep = 10
42
+ subject.try_sleep_increment = 2
43
+ lambda do
44
+ subject.with_retry(&blk)
45
+ end.should raise_error(ASIR::RetryBehavior::RetryError)
46
+ subject.sleeps.should == [10, 12, 14, 16, 18, 20, 22, 24, 26]
47
+
48
+ subject.sleeps.clear
49
+ subject.yields.clear
50
+ lambda do
51
+ subject.with_retry(&blk)
52
+ end.should raise_error(ASIR::RetryBehavior::RetryError)
53
+ subject.sleeps.should == [10, 12, 14, 16, 18, 20, 22, 24, 26]
54
+ end
55
+ end
56
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asir
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.8
4
+ version: 1.2.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-15 00:00:00.000000000 Z
12
+ date: 2013-06-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: uuid
@@ -178,6 +178,7 @@ files:
178
178
  - lab/phony_proc.rb
179
179
  - lab/spoon_test.rb
180
180
  - lib/asir.rb
181
+ - lib/asir/adaptive_value.rb
181
182
  - lib/asir/additional_data.rb
182
183
  - lib/asir/application.rb
183
184
  - lib/asir/channel.rb
@@ -242,12 +243,14 @@ files:
242
243
  - lib/asir/uri_config.rb
243
244
  - lib/asir/uuid.rb
244
245
  - lib/asir/version.rb
246
+ - spec/adaptive_value_spec.rb
245
247
  - spec/client_spec.rb
246
248
  - spec/debug_helper.rb
247
249
  - spec/demux_spec.rb
248
250
  - spec/example_spec.rb
249
251
  - spec/message_spec.rb
250
252
  - spec/performance_spec.rb
253
+ - spec/retry_behavior_spec.rb
251
254
  - spec/spec_helper.rb
252
255
  - spec/thread_pool_spec.rb
253
256
  - spec/thread_variable_spec.rb
@@ -270,7 +273,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
270
273
  version: '0'
271
274
  segments:
272
275
  - 0
273
- hash: 2378832726393842164
276
+ hash: -2116348420781161108
274
277
  required_rubygems_version: !ruby/object:Gem::Requirement
275
278
  none: false
276
279
  requirements:
@@ -279,7 +282,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
279
282
  version: '0'
280
283
  segments:
281
284
  - 0
282
- hash: 2378832726393842164
285
+ hash: -2116348420781161108
283
286
  requirements: []
284
287
  rubyforge_project:
285
288
  rubygems_version: 1.8.25
@@ -287,12 +290,14 @@ signing_key:
287
290
  specification_version: 3
288
291
  summary: Abstracting Services in Ruby
289
292
  test_files:
293
+ - spec/adaptive_value_spec.rb
290
294
  - spec/client_spec.rb
291
295
  - spec/debug_helper.rb
292
296
  - spec/demux_spec.rb
293
297
  - spec/example_spec.rb
294
298
  - spec/message_spec.rb
295
299
  - spec/performance_spec.rb
300
+ - spec/retry_behavior_spec.rb
296
301
  - spec/spec_helper.rb
297
302
  - spec/thread_pool_spec.rb
298
303
  - spec/thread_variable_spec.rb