serf 0.6.1 → 0.7.0

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.
@@ -0,0 +1,52 @@
1
+ require 'serf/runners/helper'
2
+ require 'serf/util/error_handling'
3
+
4
+ module Serf
5
+ module Runners
6
+
7
+ ##
8
+ # Direct runner drives the execution of a handler for given messages.
9
+ # This class deals with error handling and pushing handler results
10
+ # to proper error or results channels.
11
+ #
12
+ # NOTES:
13
+ # * Results returned from handlers are pushed to response channel.
14
+ # * Errors raised by handlers are pushed to error channel, not response.
15
+ #
16
+ class Direct
17
+ include Serf::Util::ErrorHandling
18
+ include Serf::Runners::Helper
19
+
20
+ def initialize(*args)
21
+ extract_options! args
22
+ opts! :response_channel
23
+ end
24
+
25
+ def call(handlers, context)
26
+ results = []
27
+ handlers.each do |handler|
28
+ ok, run_result = with_error_handling(context) do
29
+ handler.call
30
+ end
31
+ run_result = run_result.is_a?(Hash) ? [run_result] : Array(run_result)
32
+
33
+ # We only post to the response channel if we didn't catch and error.
34
+ # But we add both error and success results to the 'results' to
35
+ # pass back to the caller of the runner. This may be a background
36
+ # runner, which then the results are ignored. But it may have been
37
+ # the Serfer, which means all results should go back to the user
38
+ # as this was a foreground (synchronous) execution.
39
+ results.concat run_result
40
+ push_results run_result if ok
41
+ end
42
+ return results
43
+ end
44
+
45
+ def self.build(options={})
46
+ self.new options
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,71 @@
1
+ require 'eventmachine'
2
+
3
+ require 'serf/messages/message_accepted_event'
4
+ require 'serf/runners/direct'
5
+ require 'serf/util/error_handling'
6
+
7
+ module Serf
8
+ module Runners
9
+
10
+ ##
11
+ # This runner simply wraps another runner to execute in the
12
+ # EventMachine deferred threadpool.
13
+ #
14
+ # NOTE: Because the Serfer class validates messages before
15
+ # sending them to runners (and handlers), this class simply
16
+ # responds to the calling client with an 'MessageAcceptedEvent'
17
+ # to signal that the message will be processed later.
18
+ #
19
+ # Errors caught here will simply be logged. This is because
20
+ # the wrapped runner *MUST* handle its own errors. If an error
21
+ # should propagate up here, then it was most likely an error
22
+ # that occurred in a rescue block... we don't want to complicate
23
+ # any extra pushing to error channels because that may have
24
+ # been the cause of the error.
25
+ #
26
+ class EventMachine
27
+ include Serf::Util::ErrorHandling
28
+
29
+ def initialize(*args)
30
+ extract_options! args
31
+
32
+ # Manditory: Need a runner because this class is just a wrapper.
33
+ @runner = opts! :runner
34
+
35
+ @mae_class = opts(
36
+ :message_accepted_event_class,
37
+ ::Serf::Messages::MessageAcceptedEvent)
38
+
39
+ @evm = opts :event_machine, ::EventMachine
40
+ @logger = opts :logger, ::Serf::Util::NullObject.new
41
+ end
42
+
43
+ def call(handlers, context)
44
+ # This queues up each handler to be run separately.
45
+ handlers.each do |handler|
46
+ @evm.defer(proc do
47
+ begin
48
+ with_error_handling(context) do
49
+ @runner.call [handler], context
50
+ end
51
+ rescue => e
52
+ @logger.fatal(
53
+ "EventMachineThread: #{e.inspect}\n\n#{e.backtrace.join("\n")}")
54
+ end
55
+ end)
56
+ end
57
+ return @mae_class.new(message: context)
58
+ end
59
+
60
+ def self.build(options={})
61
+ options[:runner] = options.fetch(:runner) {
62
+ factory = options[:runner_factory] || ::Serf::Runners::Direct
63
+ factory.build options
64
+ }
65
+ self.new options
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,73 @@
1
+ require 'girl_friday'
2
+
3
+ require 'serf/messages/message_accepted_event'
4
+ require 'serf/runners/helper'
5
+ require 'serf/util/error_handling'
6
+
7
+ module Serf
8
+ module Runners
9
+
10
+ ##
11
+ #
12
+ class GirlFriday
13
+ include Serf::Util::ErrorHandling
14
+ include Serf::Runners::Helper
15
+
16
+ def initialize(*args)
17
+ extract_options! args
18
+
19
+ # Mandatory response channel.
20
+ opts! :response_channel
21
+
22
+ # Create our worker queue that will accept tasks (handler and context).
23
+ # The worker is just a block that passes on the task to the
24
+ # actual worker method.
25
+ @queue = ::GirlFriday::WorkQueue.new(
26
+ opts(:queue_name, :serf_runner),
27
+ :size => opts(:queue_size, 1)) do |task|
28
+ perform task
29
+ end
30
+ end
31
+
32
+ def call(handlers, context)
33
+ # Create our accepted event before we enqueue the handlers.
34
+ mae_class = opts(
35
+ :message_accepted_event_class,
36
+ ::Serf::Messages::MessageAcceptedEvent)
37
+ event = mae_class.new message: context
38
+
39
+ # Push each handler into the queue along with a copy of the context.
40
+ handlers.each do |handler|
41
+ @queue.push(
42
+ handler: handler,
43
+ context: context.dup)
44
+ end
45
+
46
+ # We got here, we succeeded pushing all the works.
47
+ # Now we return our accepted event.
48
+ return event
49
+ end
50
+
51
+ ##
52
+ # Builder method
53
+ #
54
+ def self.build(*args)
55
+ self.new *args
56
+ end
57
+
58
+ ##
59
+ # Actually drives the execution of individual handlers passed to job
60
+ # queue.
61
+ #
62
+ def perform(task)
63
+ with_error_handling(task[:context]) do
64
+ task[:handler].call
65
+ run_result = run_result.is_a?(Hash) ? [run_result] : Array(run_result)
66
+ push_results run_result
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,23 @@
1
+ module Serf
2
+ module Runners
3
+
4
+ module Helper
5
+
6
+ ##
7
+ # Loop over the results and push them to the response channel.
8
+ # Any error in pushing individual messages will result in
9
+ # a log event and an error channel event.
10
+ def push_results(results)
11
+ response_channel = opts! :response_channel
12
+ results.each do |message|
13
+ with_error_handling(message) do
14
+ response_channel.push message
15
+ end
16
+ end
17
+ return nil
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
data/lib/serf/serfer.rb CHANGED
@@ -1,60 +1,147 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+
1
3
  require 'serf/error'
2
- require 'serf/util/with_error_handling'
4
+ require 'serf/util/error_handling'
3
5
 
4
6
  module Serf
5
7
 
6
8
  class Serfer
7
- include ::Serf::Util::WithErrorHandling
9
+ include ::Serf::Util::ErrorHandling
10
+
11
+ def initialize(*args)
12
+ extract_options! args
8
13
 
9
- def initialize(options={})
10
- # Each route_set has a runner.
11
- @route_sets = options[:route_sets] || {}
14
+ # Map of Runners to Registries
15
+ @registries = opts :registries, {}
12
16
 
13
- # Optional overrides for WithErrorHandling
14
- @error_channel = options[:error_channel]
15
- @error_event_class = options[:error_event_class]
16
- @logger = options[:logger]
17
+ # Additional serf infrastructure options to pass to Endpoints#build.
18
+ @serf_options = opts :serf_options, {}
17
19
 
18
20
  # Options for handling the requests
19
- @not_found = options[:not_found] || proc do
21
+ @not_found = opts :not_found, lambda { |env|
20
22
  raise ArgumentError, 'Handler Not Found'
21
- end
23
+ }
22
24
  end
23
25
 
24
26
  ##
25
- # Rack-like call to run set of handlers for a message
27
+ # Rack-like call to run a set of handlers for a message
26
28
  #
27
29
  def call(env)
28
30
  # We normalize by symbolizing the env keys
29
31
  env = env.symbolize_keys
30
32
 
31
- # We're going to concat all the results
32
- matched_routes = false
33
- results = []
34
- @route_sets.each do |route_set, runner|
35
- with_error_handling(env) do
36
- endpoints = route_set.match_routes env
37
- if endpoints.size > 0
38
- matched_routes = true
39
- results.concat Array(runner.run(endpoints, env))
40
- end
41
- end
42
- end
33
+ # Call the processor
34
+ matched, results = process_request env
43
35
 
44
36
  # If we don't have any handlers, do the not_found call.
45
37
  # NOTE: Purposefully not wrapping this in exception handling.
46
- return @not_found.call env unless matched_routes
38
+ return @not_found.call env unless matched > 0
47
39
 
48
40
  return results
49
41
  rescue => e
50
42
  e.extend(::Serf::Error)
51
43
  raise e
52
44
  end
45
+ alias_method :push, :call
46
+ alias_method :<<, :call
53
47
 
54
48
  def self.build(options={})
55
49
  self.new options
56
50
  end
57
51
 
52
+ protected
53
+
54
+ ##
55
+ # Do the work of processing the env.
56
+ # 1. Match our endpoints to run, keep associated with their runner.
57
+ # 2. Turn endpoints into actual handlers that can be called by runners.
58
+ # 3. Call runner to process the endpoints
59
+ # 4. Return results
60
+ #
61
+ # NOTES:
62
+ # * Any error in matching will be raised to the caller, not absorbed by
63
+ # the error handler.
64
+ # * Any error in Handler creation from endpoint will be caught by the
65
+ # error handler, (1) pushed to the error channel and (2)
66
+ # an error event will included in the results pass back to caller.
67
+ # (a) There may be successul handlers created that can complete.
68
+ # * If the runner raises an error, it will be caught and the error
69
+ # event will be appended to the results. This is so one
70
+ # runner failure will not affect another runner's run.
71
+ # Each runner SHOULD do their own error handling so errors in
72
+ # one handler will not affect another in the list of handlers the runner
73
+ # is to process.
74
+ # * RUNNERS MUST push errors they catch to the error channel.
75
+ #
76
+ def process_request(env)
77
+ # This will be the work we need to do.
78
+ # This is a hash of runners to handlers to run.
79
+ tasks = {}
80
+
81
+ # We're going to concat all the results
82
+ results = []
83
+
84
+ # Figure out which endpoints to run.
85
+ matches = match_endpoints env
86
+
87
+ # Now we go head and create our Tasks (Units of Work)
88
+ # for each of the matched endpoints (with their runners).
89
+ matches.each do |runner, endpoints|
90
+ # We create the unit of work
91
+ handlers = endpoints.map{ |endpoint|
92
+ # We try to build the endpoint. Any errors here will
93
+ # be caught and returned to the caller.
94
+ # This is so individual building of tasks do not affect other tasks.
95
+ ok, obj = with_error_handling(env) do
96
+ endpoint.build env.dup, @serf_options
97
+ end
98
+ # The return of this if/else statement will be result of map item.
99
+ if ok
100
+ obj
101
+ else
102
+ results << obj
103
+ nil
104
+ end
105
+ }.
106
+ select{ |h| !h.nil? }
107
+
108
+ # No we enqueue the units of work into our task queue
109
+ # List could be empty because all build calls could have error out.
110
+ tasks[runner] = handlers if handlers.size > 0
111
+ end
112
+
113
+ # We call the runners with the handlers they need to execute.
114
+ # Errors raised by the runner are pushed to the error channel.
115
+ # Errors here are also passed back the caller of the SerfApp.
116
+ #
117
+ tasks.each do |runner, handlers|
118
+ ok, run_result = with_error_handling(env) do
119
+ runner.call handlers, env
120
+ end
121
+ # We need to coerce the runner's results (nil, Hash, Array, Object)
122
+ # into an Array of messages.
123
+ # now we concat this run's results to our total results list.
124
+ run_result = run_result.is_a?(Hash) ? [run_result] : Array(run_result)
125
+ results.concat run_result
126
+ end
127
+
128
+ return matches.size, results
129
+ end
130
+
131
+ ##
132
+ # Figure out which endpoints to run
133
+ #
134
+ def match_endpoints(env)
135
+ matches = {}
136
+
137
+ @registries.each do |runner, registry|
138
+ endpoints = registry.match env
139
+ matches[runner] = endpoints if endpoints.size > 0
140
+ end
141
+
142
+ return matches
143
+ end
144
+
58
145
  end
59
146
 
60
147
  end
@@ -1,14 +1,18 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+
1
3
  require 'serf/messages/caught_exception_event'
2
4
  require 'serf/util/null_object'
5
+ require 'serf/util/options_extraction'
3
6
 
4
7
  module Serf
5
8
  module Util
6
9
 
7
10
  ##
8
11
  # Helper module to rescues exceptions from executing blocks of
9
- # code, and then logs+publishes the error event.
12
+ # code, and then logs+pushes the error event.
10
13
  #
11
- module WithErrorHandling
14
+ module ErrorHandling
15
+ include ::Serf::Util::OptionsExtraction
12
16
 
13
17
  ##
14
18
  # A block wrapper to handle errors when executing a block.
@@ -20,22 +24,31 @@ module Util
20
24
  # * @error_channel - ::Serf::Util::NullObject.new
21
25
  #
22
26
  def with_error_handling(context=nil)
23
- yield
27
+ return true, yield
24
28
  rescue => e
25
- eec = @error_event_class || ::Serf::Messages::CaughtExceptionEvent
26
- logger = @logger || ::Serf::Util::NullObject.new
27
- error_channel = @error_channel || ::Serf::Util::NullObject.new
29
+ eec = opts :error_event_class, ::Serf::Messages::CaughtExceptionEvent
30
+ logger = opts :logger, ::Serf::Util::NullObject.new
31
+ error_channel = opts :error_channel, ::Serf::Util::NullObject.new
28
32
  error_event = eec.new(
29
33
  context: context,
30
- error_message: e.inspect,
31
- error_backtrace: e.backtrace.join("\n"))
34
+ error: e.class.to_s.tableize,
35
+ message: e.message,
36
+ backtrace: e.backtrace.join("\n"))
32
37
 
33
38
  # log the error to our logger, and to our error channel.
34
39
  logger.error error_event
35
- error_channel.publish error_event
40
+ begin
41
+ error_channel.push error_event
42
+ rescue => e1
43
+ logger.error("
44
+ Failed pushing to ErrorChannel:
45
+ #{e1.message}
46
+ #{e1.backtrace.join('\n')}
47
+ ")
48
+ end
36
49
 
37
50
  # We're done, so just return this error.
38
- return error_event
51
+ return false, error_event
39
52
  end
40
53
 
41
54
  end
@@ -0,0 +1,106 @@
1
+ module Serf
2
+ module Util
3
+
4
+ ##
5
+ # Module that provides helpers for dealing with options hash passed
6
+ # to initializers.
7
+ #
8
+ # class Example
9
+ # include Serf::Util::OptionsExtraction
10
+ #
11
+ # def initialize(*args, &block)
12
+ # extract_options! args
13
+ # puts args # Rest of args w/o the options hash.
14
+ # end
15
+ #
16
+ # def do_work
17
+ # my_option = opts :my_option, 'Default Value'
18
+ # puts my_option
19
+ # end
20
+ # end
21
+ #
22
+ # example = Example.new my_option: 'Another Value'
23
+ # example.do_work
24
+ # # => 'Another Value'
25
+ #
26
+ module OptionsExtraction
27
+
28
+ ##
29
+ # Reader method for the options hash.
30
+ #
31
+ def options
32
+ return @options || {}
33
+ end
34
+
35
+ ##
36
+ # Helper method to lookup an option from our options hash.
37
+ #
38
+ # Examples:
39
+ #
40
+ # # Optional parameter whose default value is nil.
41
+ # do_extra = opts :do_extra
42
+ #
43
+ # # Optional params that defaults to [1,2,3]
44
+ # start_array = opts :start_array, [1,2,3]
45
+ #
46
+ # Returns default value when:
47
+ # * Key is non-existent in Options Hash.
48
+ # * OR when the value of the key in the Options Hash is nil.
49
+ #
50
+ # Returns nil when:
51
+ # * Default is nil and the Options Hash lookup returns a nil.
52
+ # Options Hash returns a nil because of either a
53
+ # non-existent key or a nil value in said hash for said key.
54
+ #
55
+ def opts(key, default=nil, &block)
56
+ value = options[key]
57
+ value = default if value.nil?
58
+ value = block.call if value.nil? && block
59
+ return value
60
+ end
61
+
62
+ ##
63
+ # Use this lookup helper if a nil value is unacceptable for processing.
64
+ #
65
+ # There are cases where an option may have a default value for a feature,
66
+ # but the caller may just want to disable said feature. To do so,
67
+ # users of this module should allow for the caller to pass in 'false'
68
+ # as an option value instead of nil to disable said feature. The
69
+ # implementer will have to do a boolean check of the returned option value
70
+ # and disable accordingly.
71
+ #
72
+ # Examples:
73
+ #
74
+ # # Has a default logger, but we can disable logging by passing false.
75
+ # # If caller had passed in nil for :logger, it would have
76
+ # # defaulted to ::Log4r['my_logger'].
77
+ # logger = opts! :logger, ::Log4r['my_logger']
78
+ # logger = Serf::NullObject.new unless logger
79
+ #
80
+ # # Here we force the end user to pass in a Non-nil option as a
81
+ # # mandatory parameter.
82
+ # max_threads = opts! :max_threads
83
+ #
84
+ # Raises error when `opts` returns a nil.
85
+ #
86
+ def opts!(key, default=nil, &block)
87
+ value = opts key, default, &block
88
+ raise "Nil value found for option: #{key}, #{default}" if value.nil?
89
+ return value
90
+ end
91
+
92
+ protected
93
+
94
+ ##
95
+ # Extracts the options from the arguments list.
96
+ #
97
+ def extract_options!(args)
98
+ _, @options = args.last.is_a?(::Hash) ?
99
+ [true, args.pop.dup] :
100
+ [false, {}]
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+ end
data/lib/serf/version.rb CHANGED
@@ -2,8 +2,8 @@ module Serf
2
2
 
3
3
  module Version
4
4
  MAJOR = 0
5
- MINOR = 6
6
- PATCH = 1
5
+ MINOR = 7
6
+ PATCH = 0
7
7
  BUILD = nil
8
8
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join '.'
9
9
  end
data/serf.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "serf"
8
- s.version = "0.6.1"
8
+ s.version = "0.7.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Benjamin Yu"]
12
- s.date = "2012-02-09"
12
+ s.date = "2012-03-13"
13
13
  s.description = "Event-Driven SOA with CQRS"
14
14
  s.email = "benjaminlyu@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -25,22 +25,27 @@ Gem::Specification.new do |s|
25
25
  "NOTICE.txt",
26
26
  "README.md",
27
27
  "Rakefile",
28
+ "docs/thread_pools.txt",
28
29
  "lib/serf.rb",
29
30
  "lib/serf/builder.rb",
31
+ "lib/serf/command.rb",
30
32
  "lib/serf/error.rb",
31
33
  "lib/serf/message.rb",
32
34
  "lib/serf/messages/caught_exception_event.rb",
33
35
  "lib/serf/messages/message_accepted_event.rb",
34
36
  "lib/serf/middleware/uuid_tagger.rb",
35
- "lib/serf/runners/direct_runner.rb",
36
- "lib/serf/runners/em_runner.rb",
37
+ "lib/serf/routing/endpoint.rb",
38
+ "lib/serf/routing/registry.rb",
39
+ "lib/serf/runners/direct.rb",
40
+ "lib/serf/runners/event_machine.rb",
41
+ "lib/serf/runners/girl_friday.rb",
42
+ "lib/serf/runners/helper.rb",
37
43
  "lib/serf/serfer.rb",
44
+ "lib/serf/util/error_handling.rb",
38
45
  "lib/serf/util/null_object.rb",
46
+ "lib/serf/util/options_extraction.rb",
39
47
  "lib/serf/util/regexp_matcher.rb",
40
- "lib/serf/util/route_endpoint.rb",
41
- "lib/serf/util/route_set.rb",
42
48
  "lib/serf/util/uuidable.rb",
43
- "lib/serf/util/with_error_handling.rb",
44
49
  "lib/serf/version.rb",
45
50
  "serf.gemspec",
46
51
  "spec/serf_spec.rb",
@@ -49,7 +54,7 @@ Gem::Specification.new do |s|
49
54
  s.homepage = "http://github.com/byu/serf"
50
55
  s.licenses = ["Apache 2.0"]
51
56
  s.require_paths = ["lib"]
52
- s.rubygems_version = "1.8.15"
57
+ s.rubygems_version = "1.8.17"
53
58
  s.summary = "Event-Driven SOA with CQRS"
54
59
 
55
60
  if s.respond_to? :specification_version then
@@ -59,36 +64,42 @@ Gem::Specification.new do |s|
59
64
  s.add_runtime_dependency(%q<activesupport>, [">= 3.2.0"])
60
65
  s.add_runtime_dependency(%q<i18n>, [">= 0.6.0"])
61
66
  s.add_runtime_dependency(%q<uuidtools>, [">= 2.1.2"])
62
- s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
63
- s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
64
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
65
- s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
67
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
68
+ s.add_development_dependency(%q<yard>, ["~> 0.7.5"])
69
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.22"])
70
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
66
71
  s.add_development_dependency(%q<simplecov>, [">= 0"])
72
+ s.add_development_dependency(%q<log4r>, ["~> 1.1.10"])
67
73
  s.add_development_dependency(%q<msgpack>, [">= 0.4.6"])
68
74
  s.add_development_dependency(%q<eventmachine>, [">= 0.12.10"])
75
+ s.add_development_dependency(%q<girl_friday>, ["~> 0.9.7"])
69
76
  else
70
77
  s.add_dependency(%q<activesupport>, [">= 3.2.0"])
71
78
  s.add_dependency(%q<i18n>, [">= 0.6.0"])
72
79
  s.add_dependency(%q<uuidtools>, [">= 2.1.2"])
73
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
74
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
75
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
76
- s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
80
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
81
+ s.add_dependency(%q<yard>, ["~> 0.7.5"])
82
+ s.add_dependency(%q<bundler>, ["~> 1.0.22"])
83
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
77
84
  s.add_dependency(%q<simplecov>, [">= 0"])
85
+ s.add_dependency(%q<log4r>, ["~> 1.1.10"])
78
86
  s.add_dependency(%q<msgpack>, [">= 0.4.6"])
79
87
  s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
88
+ s.add_dependency(%q<girl_friday>, ["~> 0.9.7"])
80
89
  end
81
90
  else
82
91
  s.add_dependency(%q<activesupport>, [">= 3.2.0"])
83
92
  s.add_dependency(%q<i18n>, [">= 0.6.0"])
84
93
  s.add_dependency(%q<uuidtools>, [">= 2.1.2"])
85
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
86
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
87
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
88
- s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
94
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
95
+ s.add_dependency(%q<yard>, ["~> 0.7.5"])
96
+ s.add_dependency(%q<bundler>, ["~> 1.0.22"])
97
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
89
98
  s.add_dependency(%q<simplecov>, [">= 0"])
99
+ s.add_dependency(%q<log4r>, ["~> 1.1.10"])
90
100
  s.add_dependency(%q<msgpack>, [">= 0.4.6"])
91
101
  s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
102
+ s.add_dependency(%q<girl_friday>, ["~> 0.9.7"])
92
103
  end
93
104
  end
94
105