method_decorators 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/Contributors.md CHANGED
@@ -2,3 +2,4 @@ Thanks to everyone who's contributed.
2
2
 
3
3
  - Micah Frost - @mfrost
4
4
  - Isaac Sanders - @isaacsanders
5
+ - Toby Hsieh - @tobyhs
data/Gemfile CHANGED
@@ -2,6 +2,3 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in method_decorators.gemspec
4
4
  gemspec
5
-
6
- gem 'rake'
7
- gem 'rspec'
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 use it.
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/decorators/name_of_decorator'`, or all at once with `require 'method_decorators/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 < MethodDecorator
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,5 @@
1
+ require "method_decorators/memoize"
2
+ require "method_decorators/retry"
3
+ require "method_decorators/precondition"
4
+ require "method_decorators/within"
5
+
@@ -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
- class Memoize < MethodDecorator
2
- def call(orig, this, *args, &blk)
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
- class Precondition < MethodDecorator
2
- def initialize(&blk)
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
- class Retry < MethodDecorator
2
- def initialize(max)
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
@@ -1 +1,2 @@
1
+ # This is deprecated. Use `require "method_decorators/all"` instead.
1
2
  Dir[File.dirname(__FILE__) + '/decorators/*.rb'].each {|file| require file }
@@ -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
@@ -1,3 +1,3 @@
1
1
  module MethodDecorators
2
- VERSION = "0.9.2"
2
+ VERSION = "0.9.3"
3
3
  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
@@ -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 = MethodDecorator.current_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 = MethodDecorator.current_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
@@ -1,5 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/method_decorators/version', __FILE__)
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/decorators/memoize'
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/decorators/precondition'
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/decorators/retry'
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
@@ -1,4 +1,4 @@
1
- class AddN < MethodDecorator
1
+ class AddN < MethodDecorators::Decorator
2
2
  def initialize(n)
3
3
  @n = n
4
4
  end
@@ -6,4 +6,4 @@ class AddN < MethodDecorator
6
6
  def call(orig, this, *args, &blk)
7
7
  orig.call(*args, &blk) + @n
8
8
  end
9
- end
9
+ end
@@ -1,5 +1,5 @@
1
- class Reverse < MethodDecorator
1
+ class Reverse < MethodDecorators::Decorator
2
2
  def call(orig, this, *args, &blk)
3
3
  orig.call(*args.reverse, &blk)
4
4
  end
5
- end
5
+ end
@@ -1,4 +1,4 @@
1
- class Stringify < MethodDecorator
1
+ class Stringify < MethodDecorators::Decorator
2
2
  def call(orig, this, *args, &blk)
3
3
  orig.call(*args, &blk).to_s
4
4
  end
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.2
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: 2012-07-15 00:00:00.000000000 Z
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.17
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: