Skip to content

Commit

Permalink
Merge pull request #572 from adhearsion/feature/remove-girl-friday
Browse files Browse the repository at this point in the history
Move events system to Celluloid and do away with GirlFriday
  • Loading branch information
benlangfeld committed Jun 22, 2015
2 parents 7fae0ea + ef1d7a7 commit 6f9bc9f
Show file tree
Hide file tree
Showing 13 changed files with 121 additions and 138 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -17,6 +17,7 @@
* Feature: Add i18n support via `CallController#t`
* Feature: Integrate a Rack-based HTTP server from the Virginia plugin
* Upgrade to Celluloid 0.16
* Move events system to Celluloid and do away with GirlFriday

# [2.6.1](https://github.com/adhearsion/adhearsion/compare/v2.6.0...v2.6.1) - [2015-06-15](https://rubygems.org/gems/adhearsion/versions/2.6.1)
* Bugfix: Improve Call initialization performance. Use an ActorProxy (subclass) instead of a method_missing definition on every Call.new. This considerably improves Call.new performance; benchmarks show approximately a 30-40% improvement: https://gist.github.com/kares/3576e272250204eb66d1
Expand Down
3 changes: 1 addition & 2 deletions adhearsion.gemspec
Expand Up @@ -30,8 +30,7 @@ Gem::Specification.new do |s|
s.add_runtime_dependency 'deep_merge'
s.add_runtime_dependency 'ffi', ["~> 1.0"]
s.add_runtime_dependency 'future-resource', ["~> 1.0"]
s.add_runtime_dependency 'girl_friday'
s.add_runtime_dependency 'has-guarded-handlers', ["~> 1.6"]
s.add_runtime_dependency 'has-guarded-handlers', ["~> 1.6", ">= 1.6.3"]
s.add_runtime_dependency 'i18n', ["~> 0.6"]
s.add_runtime_dependency 'logging', ["~> 1.8"]
s.add_runtime_dependency 'nokogiri', ["~> 1.5", ">= 1.5.6"]
Expand Down
4 changes: 4 additions & 0 deletions lib/adhearsion/configuration.rb
Expand Up @@ -54,6 +54,10 @@ def initialize(&block)
Does not work under JRuby.
__

event_threads 5, transform: Proc.new { |v| Adhearsion::Configuration.validate_number v }, desc: <<-__
The number of threads to include in the event worker pool."
__

desc "Log configuration"
logging {
level :info, :transform => Proc.new { |v| v.to_sym }, :desc => <<-__
Expand Down
113 changes: 47 additions & 66 deletions lib/adhearsion/events.rb
@@ -1,95 +1,76 @@
# encoding: utf-8

require 'has_guarded_handlers'
require 'girl_friday'
require 'singleton'
require 'celluloid'

module Adhearsion
class Events
module Events

include HasGuardedHandlers
class Handler
include HasGuardedHandlers
include Singleton

Message = Struct.new :type, :object

class << self
def method_missing(method_name, *args, &block)
instance.send method_name, *args, &block
def call_handler(handler, guards, event)
super
throw :pass
end

def respond_to_missing?(method_name, include_private = false)
instance.respond_to? method_name, include_private
end
alias :register_callback :register_handler

def instance
@@instance || refresh!
def method_missing(method_name, *args, &block)
register_handler method_name, *args, &block
end

def refresh!
@@instance = new
def respond_to_missing?(method_name, include_private = false)
respond_to?(method_name, include_private)
end
end

refresh!
class Worker
include Celluloid

def queue
queue? ? @queue : reinitialize_queue!
end

def trigger(type, object = nil)
queue.push_async Message.new(type, object)
end

def trigger_immediately(type, object = nil)
queue.push_immediately Message.new(type, object)
end

def queue?
instance_variable_defined? :@queue
end

def reinitialize_queue!
GirlFriday.shutdown! if queue?
# TODO: Extract number of threads to use from Adhearsion.config
@queue = GirlFriday::WorkQueue.new 'main_queue', :error_handler => ErrorHandler do |message|
work message
def work(type, object)
Handler.instance.trigger_handler type, object
rescue => e
raise if type == :exception
async.work :exception, e
end
end

def work(message)
handle_message message
rescue => e
raise if message.type == :exception
trigger :exception, e
end

def handle_message(message)
trigger_handler message.type, message.object
end
class << self
def method_missing(method_name, *args, &block)
Handler.instance.send method_name, *args, &block
end

def draw(&block)
instance_exec(&block)
end
def respond_to_missing?(method_name, include_private = false)
Handler.instance.respond_to? method_name, include_private
end

def method_missing(method_name, *args, &block)
register_handler method_name, *args, &block
end
def trigger(type, object = nil)
queue.async.work type, object
end

def respond_to_missing?(method_name, include_private = false)
instance_variable_defined?(:@handlers) && @handlers.has_key?(method_name)
end
def trigger_immediately(type, object = nil)
queue.work type, object
end

alias :register_callback :register_handler
def draw(&block)
Handler.instance.instance_exec(&block)
end

private
def queue
@queue || refresh!
end

def call_handler(handler, guards, event)
super
throw :pass
end
def init
@queue = Worker.pool(size: Adhearsion.config.platform.event_threads)
end

class ErrorHandler
def handle(exception)
logger.error "Exception encountered in exception handler!"
logger.error exception
def refresh!
@queue = nil
Handler.instance.clear_handlers
init
end
end

Expand Down
1 change: 1 addition & 0 deletions lib/adhearsion/initializer.rb
Expand Up @@ -143,6 +143,7 @@ def load_config_file
end

def load_events_file
Adhearsion::Events.init
path = "#{Adhearsion.config.root}/config/events.rb"
load path if File.exists?(path)
end
Expand Down
1 change: 0 additions & 1 deletion spec/adhearsion/call_controller/record_spec.rb
Expand Up @@ -212,7 +212,6 @@ class CallController
component.execute!
component.trigger_event_handler response
expect(latch.wait(1)).to be true
Adhearsion::Events.clear_handlers :exception
end
end

Expand Down
1 change: 0 additions & 1 deletion spec/adhearsion/call_controller_spec.rb
Expand Up @@ -685,7 +685,6 @@ def foobar
end
subject.exec
expect(latch.wait(1)).to be true
Adhearsion::Events.clear_handlers :exception
end

it "should call the requested method when an exception is encountered" do
Expand Down
2 changes: 0 additions & 2 deletions spec/adhearsion/call_spec.rb
Expand Up @@ -434,7 +434,6 @@ module Adhearsion
subject.register_event_handler { |e| raise 'foo' }
expect { subject << :foo }.not_to raise_error
expect(latch.wait(3)).to be true
Adhearsion::Events.clear_handlers :exception
end
end
end
Expand Down Expand Up @@ -1588,7 +1587,6 @@ def expect_unjoin_with_options(options = {})
end
subject.execute_controller BrokenController.new(subject), lambda { |call| latch.countdown! }
expect(latch.wait(3)).to be true
Adhearsion::Events.clear_handlers :exception
end

it "should execute a callback after the controller executes" do
Expand Down
62 changes: 17 additions & 45 deletions spec/adhearsion/events_spec.rb
Expand Up @@ -12,67 +12,51 @@ module Adhearsion
Events.refresh!
end

it "should have a GirlFriday::Queue to handle events" do
expect(Events.queue).to be_a GirlFriday::WorkQueue
end

it "should allow adding events to the queue and handle them appropriately" do
t = nil
o = nil
e = nil
latch = CountDownLatch.new 1

expect(Events.instance).to receive(:handle_message) do |message|
t = message.type
o = message.object
Events.register_handler :event do |event|
e = event
latch.countdown!
end

Events.trigger :event, :foo

expect(latch.wait(2)).to be_truthy
expect(t).to eq(:event)
expect(o).to eq(:foo)
expect(e).to eq(:foo)
end

it "should allow executing events immediately" do
t = nil
o = nil
e = nil

expect(Events.instance).to receive(:handle_message) do |message|
Events.register_handler :event do |event|
sleep 0.25
t = message.type
o = message.object
e = event
end

Events.trigger_immediately :event, :foo

expect(t).to eq(:event)
expect(o).to eq(:foo)
expect(e).to eq(:foo)
end

it "should handle events using registered guarded handlers" do
result = nil
it "should handle exceptions in event processing by raising the exception as an event" do
e = nil
latch = CountDownLatch.new 1

Events.register_handler :event, EventClass do |event|
result = :foo
Events.register_handler :exception do |event|
e = event
latch.countdown!
end

Events.trigger_immediately :event, EventClass.new

expect(result).to eq(:foo)

Events.clear_handlers :event, EventClass
end

it "should handle exceptions in event processing by raising the exception as an event" do
expect(Events.instance).to receive(:trigger).with(:exception, kind_of(ExceptionClass)).once

Events.register_handler :event, EventClass do |event|
raise ExceptionClass
end

Events.trigger_immediately :event, EventClass.new
Events.clear_handlers :event, EventClass

expect(latch.wait(2)).to be_truthy
expect(e).to be_a(ExceptionClass)
end

it "should implicitly pass on all handlers" do
Expand All @@ -89,16 +73,6 @@ module Adhearsion
Events.trigger_immediately :event, EventClass.new

expect(result).to eq(:bar)

Events.clear_handlers :event, EventClass
end

it "should respond_to? any methods corresponding to classes for which handlers are defined" do
Events.register_handler :event_type_1 do |event|
end

expect(Events).to respond_to(:event_type_1)
expect(Events).not_to respond_to(:event_type_2)
end

describe '#draw' do
Expand All @@ -113,8 +87,6 @@ module Adhearsion
Events.trigger_immediately :event

expect(result).to eq(:foo)

Events.clear_handlers :event
end
end

Expand Down
4 changes: 2 additions & 2 deletions spec/adhearsion/initializer_spec.rb
Expand Up @@ -18,7 +18,7 @@
end

after do
Adhearsion::Events.reinitialize_queue!
Adhearsion::Events.refresh!
end

after :all do
Expand Down Expand Up @@ -65,7 +65,7 @@
::Logging.reset
Adhearsion::Logging.start
Adhearsion::Logging.silence!
Adhearsion::Events.reinitialize_queue!
Adhearsion::Events.refresh!
end

it "should start logging with valid parameters" do
Expand Down
12 changes: 11 additions & 1 deletion spec/adhearsion/rayo/initializer_spec.rb
Expand Up @@ -179,9 +179,19 @@ def initialize_rayo(options = {})
end

it 'should place events into the event handler' do
expect(Adhearsion::Events.instance).to receive(:trigger).once.with(:rayo, offer)
event = nil
latch = CountDownLatch.new 1

Adhearsion::Events.register_handler :rayo do |offer|
event = offer
latch.countdown!
end

initialize_rayo
described_class.client.handle_event offer

expect(latch.wait(2)).to be_truthy
expect(event).to be(offer)
end

describe "dispatching an offer" do
Expand Down

0 comments on commit 6f9bc9f

Please sign in to comment.