rhod 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4fdc88634e5041f5eb95de18515709647bd9edd2
4
- data.tar.gz: fa5cdadac5ca8fcaaef6066862e5b09552b39aee
3
+ metadata.gz: b3800489cac9157423933e8c61a0d8de2e602ee4
4
+ data.tar.gz: 30424cdafb3aa4d099ef5983fdd391552119693f
5
5
  SHA512:
6
- metadata.gz: bb25f0ef2719d5141211eab7f9981938efd5be0dc1fb1a64362280ff844624da0899458e714682e38e6a5021bc7ca7ecc70a48eca151d3b71b260fd7a5733947
7
- data.tar.gz: a9d86ea5c9e59a1ea39df024f69bac790adb79c4e87990af471cedff1cc341b7174d9a5bd092661746139f516b2ad0c3caa5725bdd82d15f315cd3d1a8cdd2bb
6
+ metadata.gz: 027c8d5951ce06f4fe47422dec3c46f1b4fccf8df72010ec27a608d1ff9d1ab762c7e721309bb765cdd44810e09332453edd5efb192447761e1f540e968181e2
7
+ data.tar.gz: 53921859dd5cd83d5e99d12b6c4d9e8ccf2ca6a790382f90ea169651532dca303d33ed83580de942bb4367b50a9246bb2ab75ababd1d246a575a9cd1b3db2f1b
data/Changes.md CHANGED
@@ -1,3 +1,7 @@
1
+ # v0.1.3
2
+ Update connection_pool dep to 1.1.0
3
+ Add middleware support - dinedal - PR #9
4
+
1
5
  # v0.1.2
2
6
  Added logging as a feature - dleung - PR #8
3
7
 
data/README.md CHANGED
@@ -95,7 +95,7 @@ Rhod.create_profile(:redis,
95
95
  backoffs: :^,
96
96
  pool: ConnectionPool.new(size: 3, timeout: 10) { Redis.new },
97
97
  exceptions: [Redis::BaseError],
98
- logger: Logger.new("log.txt")
98
+ logger: Logger.new(STDOUT)
99
99
  )
100
100
 
101
101
  Rhod.with_redis("1") {|r, a| r.set('test',a)}
@@ -112,6 +112,14 @@ Code within a `Rhod::Command` block with reties in use must be _idempotent_, i.e
112
112
 
113
113
  Code within a `Rhod::Command` should avoid leaking memory and/or scope by having arguments passed to it:
114
114
 
115
+ ## Logging
116
+
117
+ Rhod can optionally log all failures, very useful for debugging. Just set a logger in a profile and they will be logged at the level `:warn`
118
+
119
+ ```ruby
120
+ Rhod.create_profile(:verbose, logger: Logger.new(STDOUT))
121
+ ```
122
+
115
123
  ### Good use of argument passing:
116
124
 
117
125
  ```ruby
@@ -2,6 +2,7 @@ require 'connection_pool'
2
2
  require 'logger'
3
3
  require_relative "rhod/version"
4
4
  require_relative "rhod/backoffs"
5
+ require_relative "rhod/middleware"
5
6
  require_relative "rhod/command"
6
7
  require_relative "rhod/profile"
7
8
 
@@ -4,23 +4,19 @@ class Rhod::Command
4
4
 
5
5
  def initialize(*args, &block)
6
6
  opts = args[-1].kind_of?(Hash) ? args.pop : {}
7
- @args = args || []
8
-
9
- @request = block
10
-
11
- @retries = opts[:retries] || 0
12
- @attempts = 0
13
-
14
- @logger = opts[:logger]
15
-
16
- @backoffs = Rhod::Backoffs.backoff_sugar_to_enumerator(opts[:backoffs])
17
- @backoffs ||= Rhod::Backoffs::Logarithmic.new(1.3)
18
-
19
- @fallback = opts[:fallback]
20
-
21
- @pool = opts[:pool]
22
-
23
- @exceptions = opts[:exceptions] || EXCEPTIONS
7
+ @env = {
8
+ :args => args || [],
9
+ :request => block,
10
+ :retries => opts[:retries] || 0,
11
+ :attempts => 0,
12
+ :logger => opts[:logger],
13
+ :backoffs => Rhod::Backoffs.backoff_sugar_to_enumerator(opts[:backoffs]) || Rhod::Backoffs::Logarithmic.new(1.3),
14
+ :fallback => opts[:fallback],
15
+ :pool => opts[:pool],
16
+ :exceptions => opts[:exceptions] || EXCEPTIONS,
17
+ :profile_name => opts[:profile_name],
18
+ :middleware => opts[:middleware_stack] || Rhod::Middleware.new,
19
+ }
24
20
  end
25
21
 
26
22
  ### Class methods
@@ -34,26 +30,51 @@ class Rhod::Command
34
30
 
35
31
  def execute
36
32
  begin
37
- if @pool
38
- @pool.with do |conn|
39
- @args = [conn].concat(@args)
33
+ if @env[:pool]
34
+ @env[:pool].with do |conn|
35
+ @env[:args] = [conn].concat(@env[:args])
40
36
 
41
- @request.call(*@args)
37
+ call_middleware_before_request
38
+ @env[:result] = @env[:request].call(*@env[:args])
39
+ call_middleware_after_request
40
+ @env[:result]
42
41
  end
43
42
  else
44
- @request.call(*@args)
43
+ call_middleware_before_request
44
+ @env[:result] = @env[:request].call(*@env[:args])
45
+ call_middleware_after_request
46
+ @env[:result]
45
47
  end
46
- rescue *@exceptions => e
47
- @attempts += 1
48
- @next_attempt = @backoffs.next
49
- if @attempts <= @retries
50
- @logger.warn("Rhod - Caught an exception: #{e.message}. Attempt #{@attempts} in #{sprintf("%.2f", @next_attempt)} secs") if @logger && @logger.respond_to?(:warn)
51
- sleep(@next_attempt)
48
+ rescue *@env[:exceptions] => e
49
+ @env[:attempts] += 1
50
+ @env[:next_attempt] = @env[:backoffs].next
51
+ if @env[:attempts] <= @env[:retries]
52
+ @env[:logger].warn("Rhod - Caught an exception: #{e.message}. Attempt #{@env[:attempts]} in #{sprintf("%.2f", @env[:next_attempt])} secs") if @env[:logger] && @env[:logger].respond_to?(:warn)
53
+ call_middleware_on_error
54
+ sleep(@env[:next_attempt])
52
55
  retry
53
56
  else
54
- return @fallback.call(*@args) if @fallback
57
+ call_middleware_on_failure
58
+ return @env[:fallback].call(*@env[:args]) if @env[:fallback]
55
59
  raise
56
60
  end
57
61
  end
58
62
  end
63
+
64
+ def call_middleware_before_request
65
+ @env = @env[:middleware].on(:before, @env)
66
+ end
67
+
68
+ def call_middleware_after_request
69
+ @env = @env[:middleware].on(:after, @env)
70
+ end
71
+
72
+ def call_middleware_on_error
73
+ @env = @env[:middleware].on(:error, @env)
74
+ end
75
+
76
+ def call_middleware_on_failure
77
+ @env = @env[:middleware].on(:failure, @env)
78
+ end
79
+
59
80
  end
@@ -1,10 +1,33 @@
1
1
  class Rhod::Middleware
2
+ class Rhod::InvalidMiddleware < Exception; end
3
+
4
+ def initialize
5
+ @stack = []
6
+ end
7
+
2
8
  def use(middleware, *args, &block)
3
- self.stack << [middleware, args, block]
9
+ @stack << [middleware, args, block]
4
10
  end
5
11
 
6
- protected
7
- def stack
8
- @stack ||= []
12
+ def build_stack
13
+ @stack = @stack.map do |current_middleware|
14
+ klass, args, block = current_middleware
15
+
16
+ if klass.is_a?(Class)
17
+ klass.new(*args, &block)
18
+ else
19
+ raise Rhod::InvalidMiddleware, "Unable to call middleware #{current_middleware}"
20
+ end
21
+ end
9
22
  end
23
+
24
+ def on(event, env)
25
+ @stack.reduce(env) do |e, current_middleware|
26
+ if current_middleware.respond_to?(:on)
27
+ e = current_middleware.on(event, e)
28
+ end
29
+ e
30
+ end
31
+ end
32
+
10
33
  end
@@ -13,6 +13,21 @@ class Rhod::Profile < Hash
13
13
 
14
14
  options.each {|k,v| self[k] = v }
15
15
 
16
+ self[:profile_name] = name
17
+
18
+ # Middleware stack construction
19
+ self[:middleware_stack] = Rhod::Middleware.new
20
+
21
+ if self[:middleware].respond_to?(:call)
22
+ self[:middleware].call(self[:middleware_stack])
23
+ elsif self[:middleware]
24
+ self[:middleware].each do |klass|
25
+ self[:middleware_stack].use klass
26
+ end
27
+ end
28
+
29
+ self[:middleware_stack].build_stack
30
+
16
31
  # Syntax sugar: named .with_#{profile} methods on this class and the module
17
32
  @@profiles[name] = self
18
33
 
@@ -1,3 +1,3 @@
1
1
  module Rhod
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
@@ -23,5 +23,5 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "minitest", "~> 4.4.0"
24
24
  spec.add_development_dependency "turn", "~> 0.9.6"
25
25
 
26
- spec.add_dependency "connection_pool", "~> 1.0.0"
26
+ spec.add_dependency "connection_pool", "~> 1.1.0"
27
27
  end
@@ -96,7 +96,77 @@ describe Rhod::Command do
96
96
  pool = ConnectionPool.new(size: 1, timeout: 0) { :conn }
97
97
  Rhod::Command.new(1, pool: pool) {|a, b| [a,b]}.execute.must_equal [:conn, 1]
98
98
  end
99
+
100
+ describe "with middleware" do
101
+ it "triggers the before and after event" do
102
+ pool = ConnectionPool.new(size: 1, timeout: 0) { :conn }
103
+
104
+ @stack = MiniTest::Mock.new
105
+
106
+ cmd = Rhod::Command.new(
107
+ middleware_stack: @stack,
108
+ pool: pool
109
+ ) {|a| a}
110
+ env = cmd.instance_eval {@env}
111
+ @stack.expect :on, env, [:before, Hash]
112
+ @stack.expect :on, env, [:after, Hash]
113
+ cmd.execute
114
+ @stack.verify
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ describe "with middleware" do
122
+ it "triggers the before and after event" do
123
+ @stack = MiniTest::Mock.new
124
+ cmd = Rhod::Command.new(middleware_stack: @stack) {|a| a}
125
+ env = cmd.instance_eval {@env}
126
+ @stack.expect :on, env, [:before, Hash]
127
+ @stack.expect :on, env, [:after, Hash]
128
+ cmd.execute
129
+ @stack.verify
130
+ end
131
+
132
+ it "triggers the failure event" do
133
+ @stack = MiniTest::Mock.new
134
+ cmd = Rhod::Command.new(middleware_stack: @stack) do
135
+ raise StandardError, "Problem"
136
+ end
137
+ env = cmd.instance_eval {@env}
138
+ @stack.expect :on, env, [:before, Hash]
139
+ @stack.expect :on, env, [:failure, Hash]
140
+
141
+ begin
142
+ cmd.execute
143
+ rescue
144
+ end
145
+
146
+ @stack.verify
147
+ end
148
+
149
+ it "triggers the error event" do
150
+ @stack = MiniTest::Mock.new
151
+ cmd = Rhod::Command.new(
152
+ middleware_stack: @stack,
153
+ retries: 1) do
154
+ raise StandardError, "Problem"
155
+ end
156
+ env = cmd.instance_eval {@env}
157
+ @stack.expect :on, env, [:before, Hash]
158
+ @stack.expect :on, env, [:error, Hash]
159
+ @stack.expect :on, env, [:failure, Hash]
160
+
161
+ begin
162
+ cmd.execute
163
+ rescue
164
+ end
165
+
166
+ @stack.verify
167
+ end
99
168
  end
100
169
 
170
+
101
171
  end
102
172
  end
@@ -0,0 +1,34 @@
1
+ require 'minitest/autorun'
2
+ require File.expand_path(File.dirname(__FILE__) + '/helper')
3
+
4
+ class TestMiddleware
5
+ def initialize(params=nil)
6
+ @opts = params
7
+ end
8
+
9
+ def on(event, env)
10
+ env
11
+ end
12
+ end
13
+
14
+ describe Rhod::Middleware do
15
+ it "inits with args passed in" do
16
+ stack = Rhod::Middleware.new
17
+
18
+ stack.use(TestMiddleware, 1)
19
+ stack.build_stack
20
+
21
+ stack.instance_eval{@stack}.first.instance_eval{@opts}.must_equal 1
22
+ end
23
+
24
+ it "calls each callback correctly" do
25
+ %I[before after error failure].each do |event|
26
+ stack = Rhod::Middleware.new
27
+
28
+ stack.use(TestMiddleware)
29
+ stack.build_stack
30
+
31
+ stack.send(:on, event, output: 0)[:output].must_equal 0
32
+ end
33
+ end
34
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rhod
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Bergeron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-05-27 00:00:00.000000000 Z
11
+ date: 2013-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - ~>
88
88
  - !ruby/object:Gem::Version
89
- version: 1.0.0
89
+ version: 1.1.0
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ~>
95
95
  - !ruby/object:Gem::Version
96
- version: 1.0.0
96
+ version: 1.1.0
97
97
  description:
98
98
  email:
99
99
  - paul.d.bergeron@gmail.com
@@ -123,6 +123,7 @@ files:
123
123
  - test/helper.rb
124
124
  - test/test_backoffs.rb
125
125
  - test/test_command.rb
126
+ - test/test_middleware.rb
126
127
  - test/test_profile.rb
127
128
  homepage: https://github.com/dinedal/rhod
128
129
  licenses:
@@ -144,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
145
  version: '0'
145
146
  requirements: []
146
147
  rubyforge_project:
147
- rubygems_version: 2.0.3
148
+ rubygems_version: 2.0.2
148
149
  signing_key:
149
150
  specification_version: 4
150
151
  summary: A High Avalibility framework for Ruby
@@ -152,4 +153,6 @@ test_files:
152
153
  - test/helper.rb
153
154
  - test/test_backoffs.rb
154
155
  - test/test_command.rb
156
+ - test/test_middleware.rb
155
157
  - test/test_profile.rb
158
+ has_rdoc: