rhod 0.1.2 → 0.1.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.
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: