method_decorators 0.9.2 → 0.9.3
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/Contributors.md +1 -0
- data/Gemfile +0 -3
- data/History.md +9 -0
- data/README.md +20 -5
- data/lib/method_decorators/all.rb +5 -0
- data/lib/method_decorators/decorator.rb +19 -0
- data/lib/method_decorators/decorators/memoize.rb +2 -11
- data/lib/method_decorators/decorators/precondition.rb +2 -18
- data/lib/method_decorators/decorators/retry.rb +2 -16
- data/lib/method_decorators/decorators.rb +1 -0
- data/lib/method_decorators/memoize.rb +15 -0
- data/lib/method_decorators/precondition.rb +22 -0
- data/lib/method_decorators/retry.rb +20 -0
- data/lib/method_decorators/version.rb +1 -1
- data/lib/method_decorators/within.rb +16 -0
- data/lib/method_decorators.rb +3 -20
- data/method_decorators.gemspec +6 -1
- data/spec/decorators/memoize_spec.rb +4 -4
- data/spec/decorators/precondition_spec.rb +6 -6
- data/spec/decorators/retry_spec.rb +4 -4
- data/spec/decorators/within_spec.rb +43 -0
- data/spec/support/add_n.rb +2 -2
- data/spec/support/reverse.rb +2 -2
- data/spec/support/stringify.rb +1 -1
- metadata +35 -4
data/Contributors.md
CHANGED
data/Gemfile
CHANGED
data/History.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
0.9.3
|
2
|
+
=====
|
3
|
+
- Fix double require bugs [@tobyhs]
|
4
|
+
- Namespace Decorator, Memoize, Precondition, Retry, and Within under MethodDecorators:: [@tobyhs]
|
5
|
+
|
6
|
+
0.9.2
|
7
|
+
=====
|
8
|
+
- Pass `this` (the receiver of the decorated method) to the decorators. Fixes `Precondition` with multiple preconditions and `Memoize`.
|
9
|
+
|
1
10
|
0.9.1
|
2
11
|
=====
|
3
12
|
- Added Memoize, Retry, and Precondition [@mfrost]
|
data/README.md
CHANGED
@@ -8,13 +8,13 @@ Python's function decorators for Ruby.
|
|
8
8
|
## Usage
|
9
9
|
|
10
10
|
### Using a decorator
|
11
|
-
Extend MethodDecorators in a class where you want to use them, and then stick `+DecoratorName` before your method declaration to
|
11
|
+
Extend MethodDecorators in a class where you want to use them, and then stick `+DecoratorName` before your method declaration to decorate the method.
|
12
12
|
|
13
13
|
```ruby
|
14
14
|
class Math
|
15
15
|
extend MethodDecorators
|
16
16
|
|
17
|
-
+Memoized
|
17
|
+
+MethodDecorators::Memoized
|
18
18
|
def fib(n)
|
19
19
|
if n <= 1
|
20
20
|
1
|
@@ -31,7 +31,21 @@ You can also decorate with an instance of a decorator, rather than the class. Th
|
|
31
31
|
class ExternalService
|
32
32
|
extend MethodDecorators
|
33
33
|
|
34
|
-
+Retry.new(3)
|
34
|
+
+MethodDecorators::Retry.new(3)
|
35
|
+
def request
|
36
|
+
...
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
You can also set multiple decorators for your methods. Each decorator executes within the previously declared decorator. i.e. they are nested, as expected to be.
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class ExternalService
|
45
|
+
extend MethodDecorators
|
46
|
+
|
47
|
+
+MethodDecorators::Retry.new(3)
|
48
|
+
+MethodDecorators::Within.new(2.seconds)
|
35
49
|
def request
|
36
50
|
...
|
37
51
|
end
|
@@ -40,16 +54,17 @@ end
|
|
40
54
|
|
41
55
|
### Included decorators
|
42
56
|
|
43
|
-
Include these with `require 'method_decorators/
|
57
|
+
Include these with `require 'method_decorators/name_of_decorator'`, or all at once with `require 'method_decorators/all'`.
|
44
58
|
|
45
59
|
- Memoize - caches the result of the method for each arg combination it's called with
|
46
60
|
- Retry - retries the method up to n (passed in to the constructor) times if the method errors
|
61
|
+
- Within - times outs if a request doesn't complete within n seconds
|
47
62
|
- Precondition - raises an error if the precondition (passed as a block) is not met
|
48
63
|
|
49
64
|
### Defining a decorator
|
50
65
|
|
51
66
|
```ruby
|
52
|
-
class Transactional <
|
67
|
+
class Transactional < MethodDecorators::Decorator
|
53
68
|
def call(wrapped, this, *args, &blk)
|
54
69
|
ActiveRecord::Base.transaction do
|
55
70
|
wrapped.call(*args, &blk)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MethodDecorators
|
2
|
+
class Decorator
|
3
|
+
@@current_decorators = []
|
4
|
+
|
5
|
+
def self.current_decorators
|
6
|
+
decs = @@current_decorators
|
7
|
+
@@current_decorators = []
|
8
|
+
decs
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.+@
|
12
|
+
+new
|
13
|
+
end
|
14
|
+
|
15
|
+
def +@
|
16
|
+
@@current_decorators.unshift(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,11 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
return cache(this)[args] if cache(this).has_key?(args)
|
4
|
-
cache(this)[args] = orig.call(*args, &blk)
|
5
|
-
end
|
6
|
-
|
7
|
-
private
|
8
|
-
def cache(this)
|
9
|
-
this.instance_variable_get("@_memoize_cache") || this.instance_variable_set("@_memoize_cache", {})
|
10
|
-
end
|
11
|
-
end
|
1
|
+
require "method_decorators/memoize"
|
2
|
+
::Memoize = MethodDecorators::Memoize
|
@@ -1,18 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
@block = blk
|
4
|
-
end
|
5
|
-
|
6
|
-
def call(orig, this, *args, &blk)
|
7
|
-
unless passes?(this, *args)
|
8
|
-
raise ArgumentError, "failed precondition"
|
9
|
-
end
|
10
|
-
orig.call(*args, &blk)
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def passes?(context, *args)
|
16
|
-
context.instance_exec(*args, &@block)
|
17
|
-
end
|
18
|
-
end
|
1
|
+
require "method_decorators/precondition"
|
2
|
+
Precondition = MethodDecorators::Precondition
|
@@ -1,16 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
@max = max
|
4
|
-
end
|
5
|
-
|
6
|
-
def call(orig, this, *args, &blk)
|
7
|
-
attempts = 0
|
8
|
-
begin
|
9
|
-
attempts += 1
|
10
|
-
orig.call(*args, &blk)
|
11
|
-
rescue
|
12
|
-
retry if attempts < @max
|
13
|
-
raise
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
1
|
+
require "method_decorators/retry"
|
2
|
+
::Retry = MethodDecorators::Retry
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "method_decorators"
|
2
|
+
|
3
|
+
module MethodDecorators
|
4
|
+
class Memoize < Decorator
|
5
|
+
def call(orig, this, *args, &blk)
|
6
|
+
return cache(this)[args] if cache(this).has_key?(args)
|
7
|
+
cache(this)[args] = orig.call(*args, &blk)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def cache(this)
|
12
|
+
this.instance_variable_get("@_memoize_cache") || this.instance_variable_set("@_memoize_cache", {})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "method_decorators"
|
2
|
+
|
3
|
+
module MethodDecorators
|
4
|
+
class Precondition < Decorator
|
5
|
+
def initialize(&blk)
|
6
|
+
@block = blk
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(orig, this, *args, &blk)
|
10
|
+
unless passes?(this, *args)
|
11
|
+
raise ArgumentError, "failed precondition"
|
12
|
+
end
|
13
|
+
orig.call(*args, &blk)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def passes?(context, *args)
|
19
|
+
context.instance_exec(*args, &@block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "method_decorators"
|
2
|
+
|
3
|
+
module MethodDecorators
|
4
|
+
class Retry < Decorator
|
5
|
+
def initialize(max)
|
6
|
+
@max = max
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(orig, this, *args, &blk)
|
10
|
+
attempts = 0
|
11
|
+
begin
|
12
|
+
attempts += 1
|
13
|
+
orig.call(*args, &blk)
|
14
|
+
rescue
|
15
|
+
retry if attempts < @max
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "method_decorators"
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module MethodDecorators
|
5
|
+
class Within < Decorator
|
6
|
+
def initialize(timeout)
|
7
|
+
@seconds = timeout
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(orig, this, *args, &blk)
|
11
|
+
Timeout::timeout(@seconds) do
|
12
|
+
orig.call(*args, &blk)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/method_decorators.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require "method_decorators/version"
|
2
|
+
require "method_decorators/decorator"
|
2
3
|
|
3
4
|
module MethodDecorators
|
4
5
|
def method_added(name)
|
5
6
|
super
|
6
7
|
orig_method = instance_method(name)
|
7
8
|
|
8
|
-
decorators =
|
9
|
+
decorators = Decorator.current_decorators
|
9
10
|
return if decorators.empty?
|
10
11
|
|
11
12
|
if private_method_defined?(name); visibility = :private
|
@@ -28,7 +29,7 @@ module MethodDecorators
|
|
28
29
|
super
|
29
30
|
orig_method = method(name)
|
30
31
|
|
31
|
-
decorators =
|
32
|
+
decorators = Decorator.current_decorators
|
32
33
|
return if decorators.empty?
|
33
34
|
|
34
35
|
MethodDecorators.define_others_singleton_method(self, name) do |*args, &blk|
|
@@ -53,21 +54,3 @@ module MethodDecorators
|
|
53
54
|
end
|
54
55
|
end
|
55
56
|
end
|
56
|
-
|
57
|
-
class MethodDecorator
|
58
|
-
@@current_decorators = []
|
59
|
-
|
60
|
-
def self.current_decorators
|
61
|
-
decs = @@current_decorators
|
62
|
-
@@current_decorators = []
|
63
|
-
decs
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.+@
|
67
|
-
+new
|
68
|
-
end
|
69
|
-
|
70
|
-
def +@
|
71
|
-
@@current_decorators.unshift(self)
|
72
|
-
end
|
73
|
-
end
|
data/method_decorators.gemspec
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
lib_dir = File.expand_path('lib', File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
|
4
|
+
require 'method_decorators/version'
|
3
5
|
|
4
6
|
Gem::Specification.new do |gem|
|
5
7
|
gem.authors = ["Michael Fairley"]
|
@@ -14,4 +16,7 @@ Gem::Specification.new do |gem|
|
|
14
16
|
gem.name = "method_decorators"
|
15
17
|
gem.require_paths = ["lib"]
|
16
18
|
gem.version = MethodDecorators::VERSION
|
19
|
+
|
20
|
+
gem.add_development_dependency "rake"
|
21
|
+
gem.add_development_dependency "rspec"
|
17
22
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'method_decorators/
|
2
|
+
require 'method_decorators/memoize'
|
3
3
|
|
4
|
-
describe Memoize do
|
4
|
+
describe MethodDecorators::Memoize do
|
5
5
|
describe "#call" do
|
6
6
|
let(:method) { double(:method, :call => :calculation) }
|
7
7
|
let(:this) { Object.new }
|
8
|
-
subject { Memoize.new }
|
8
|
+
subject { MethodDecorators::Memoize.new }
|
9
9
|
|
10
10
|
it "calculates the value the first time the arguments are supplied" do
|
11
11
|
method.should_receive(:call)
|
@@ -37,7 +37,7 @@ describe Memoize do
|
|
37
37
|
describe "acceptance" do
|
38
38
|
let(:klass) do
|
39
39
|
Class.new Base do
|
40
|
-
+Memoize
|
40
|
+
+MethodDecorators::Memoize
|
41
41
|
def count
|
42
42
|
@count ||= 0
|
43
43
|
@count += 1
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'method_decorators/
|
2
|
+
require 'method_decorators/precondition'
|
3
3
|
|
4
|
-
describe Precondition do
|
4
|
+
describe MethodDecorators::Precondition do
|
5
5
|
let(:receiver) { double(:receiver) }
|
6
6
|
let(:method) { double(:method, :call => :secret, :receiver => receiver) }
|
7
7
|
let(:block) { proc { |arg| true } }
|
8
|
-
subject { Precondition.new(&block) }
|
8
|
+
subject { MethodDecorators::Precondition.new(&block) }
|
9
9
|
|
10
10
|
describe "#call" do
|
11
11
|
it "raises when the precondition fails" do
|
@@ -26,13 +26,13 @@ describe Precondition do
|
|
26
26
|
@x = x
|
27
27
|
end
|
28
28
|
|
29
|
-
+Precondition.new{ |a| a + @x < 10 }
|
29
|
+
+MethodDecorators::Precondition.new{ |a| a + @x < 10 }
|
30
30
|
def multiply(a)
|
31
31
|
a * @x
|
32
32
|
end
|
33
33
|
|
34
|
-
+Precondition.new{ |a| a + @x == 10 }
|
35
|
-
+Precondition.new{ |a| a * @x == 21 }
|
34
|
+
+MethodDecorators::Precondition.new{ |a| a + @x == 10 }
|
35
|
+
+MethodDecorators::Precondition.new{ |a| a * @x == 21 }
|
36
36
|
def concat(a)
|
37
37
|
"#{@x}#{a}"
|
38
38
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'method_decorators/
|
2
|
+
require 'method_decorators/retry'
|
3
3
|
|
4
|
-
describe Retry do
|
4
|
+
describe MethodDecorators::Retry do
|
5
5
|
let(:method) { double(:method, :call => false) }
|
6
|
-
subject { Retry.new(3) }
|
6
|
+
subject { MethodDecorators::Retry.new(3) }
|
7
7
|
|
8
8
|
describe "#call" do
|
9
9
|
it "executes the method again if the first time failed " do
|
@@ -22,7 +22,7 @@ describe Retry do
|
|
22
22
|
describe "acceptance" do
|
23
23
|
let(:klass) do
|
24
24
|
Class.new Base do
|
25
|
-
+Retry.new(3)
|
25
|
+
+MethodDecorators::Retry.new(3)
|
26
26
|
def do_it(magic_number)
|
27
27
|
@times ||= 0
|
28
28
|
@times += 1
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'method_decorators/within'
|
3
|
+
|
4
|
+
describe MethodDecorators::Within do
|
5
|
+
let(:method) { double(:method, :call => false) }
|
6
|
+
subject { MethodDecorators::Within.new(2) }
|
7
|
+
|
8
|
+
describe "#call" do
|
9
|
+
it "raises when the timeout seconds have elapsed" do
|
10
|
+
method.stub(:call){ sleep 3 }
|
11
|
+
expect{ subject.call(method, nil) }.to raise_error(Timeout::Error)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "does not raise when the method has finished execution before timeout" do
|
15
|
+
method.stub(:call){ sleep 1 }
|
16
|
+
expect{ subject.call(method, nil) }.to_not raise_error(Timeout::Error)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "acceptance" do
|
21
|
+
let(:klass) do
|
22
|
+
Class.new Base do
|
23
|
+
+MethodDecorators::Within.new(2)
|
24
|
+
def do_it(execution_period)
|
25
|
+
sleep(execution_period)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
subject { klass.new }
|
30
|
+
|
31
|
+
context "with longer execution period" do
|
32
|
+
it "raises if the timeout period has elapsed" do
|
33
|
+
expect{ subject.do_it(3) }.to raise_error(Timeout::Error)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "with shorter execution period" do
|
38
|
+
it "finishes within the timeout period" do
|
39
|
+
expect{ subject.do_it(1) }.to_not raise_error(Timeout::Error)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/support/add_n.rb
CHANGED
data/spec/support/reverse.rb
CHANGED
data/spec/support/stringify.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: method_decorators
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,30 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
12
|
+
date: 2013-03-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &70128902042200 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70128902042200
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70128902041760 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70128902041760
|
14
36
|
description: Python's function decorators for Ruby
|
15
37
|
email:
|
16
38
|
- michaelfairley@gmail.com
|
@@ -28,15 +50,22 @@ files:
|
|
28
50
|
- README.md
|
29
51
|
- Rakefile
|
30
52
|
- lib/method_decorators.rb
|
53
|
+
- lib/method_decorators/all.rb
|
54
|
+
- lib/method_decorators/decorator.rb
|
31
55
|
- lib/method_decorators/decorators.rb
|
32
56
|
- lib/method_decorators/decorators/memoize.rb
|
33
57
|
- lib/method_decorators/decorators/precondition.rb
|
34
58
|
- lib/method_decorators/decorators/retry.rb
|
59
|
+
- lib/method_decorators/memoize.rb
|
60
|
+
- lib/method_decorators/precondition.rb
|
61
|
+
- lib/method_decorators/retry.rb
|
35
62
|
- lib/method_decorators/version.rb
|
63
|
+
- lib/method_decorators/within.rb
|
36
64
|
- method_decorators.gemspec
|
37
65
|
- spec/decorators/memoize_spec.rb
|
38
66
|
- spec/decorators/precondition_spec.rb
|
39
67
|
- spec/decorators/retry_spec.rb
|
68
|
+
- spec/decorators/within_spec.rb
|
40
69
|
- spec/method_decorators_spec.rb
|
41
70
|
- spec/spec_helper.rb
|
42
71
|
- spec/support/add_n.rb
|
@@ -62,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
91
|
version: '0'
|
63
92
|
requirements: []
|
64
93
|
rubyforge_project:
|
65
|
-
rubygems_version: 1.8.
|
94
|
+
rubygems_version: 1.8.7
|
66
95
|
signing_key:
|
67
96
|
specification_version: 3
|
68
97
|
summary: Python's function decorators for Ruby
|
@@ -70,8 +99,10 @@ test_files:
|
|
70
99
|
- spec/decorators/memoize_spec.rb
|
71
100
|
- spec/decorators/precondition_spec.rb
|
72
101
|
- spec/decorators/retry_spec.rb
|
102
|
+
- spec/decorators/within_spec.rb
|
73
103
|
- spec/method_decorators_spec.rb
|
74
104
|
- spec/spec_helper.rb
|
75
105
|
- spec/support/add_n.rb
|
76
106
|
- spec/support/reverse.rb
|
77
107
|
- spec/support/stringify.rb
|
108
|
+
has_rdoc:
|