asir 1.2.8 → 1.2.9

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/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