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