serf 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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