Adhearsion 2.0 brings many changes. It marks the first time since release 0.8.0 in 2008 that we have broken backward compatibility with previous versions. This means that, while you have access to a rich set of new features, it also means existing applications will need to be ported to run on Adhearsion 2. This document aims to list the changes required to migrate an Adhearsion 1.x application up to Adhearsion 2.
Adhearsion 2.0 has an all new list of prerequisites. Before going any further, please see the Prerequisites page.
One important thing to note for existing Adhearsion applications: Adhearsion 2.0 running with Asterisk requires version 1.8 or later. 1.6 and earlier are no longer supported. Tropo AGItate applications are also currently unsupported on Adhearsion 2.0. Work is underway to address this and announcements will be made when it is ready.
This is mostly a simple search & replace change. Find all instances of ahn_log
and replace them with logger
. However, we have also removed logger namespaces. For example, ahn_log.myapp.info
would log messages within the "myapp" namespace. This is no longer supported so it would need to become logger.info
. Fortunately we have added a lot more information to the default logging formatter that includes the originating class name that generated the log message. Hopefully have a minimal or net positive impact to most applications.
This is the single most visible change. Previously an Adhearsion application's entry point was dialplan.rb
, and the entry point in dialplan.rb
was decided by the context provided by Asterisk. Since Adhearsion 2.0 runs on multiple platforms, and the concept of a "context" is Asterisk-specific, we needed to do something different. The recommended way to migrate dialplan.rb is to break each dialplan.rb block up into a separate CallController. Then you will need to add routes to config/adhearsion.rb
that map Asterisk contexts to the new CallControllers. For example, given the following dialplan.rb:
adhearsion {
simon_game
}
my_menu {
menu 'welcome' do |link|
link.shipment_status 1
link.ordering 2
link.representative 0
end
}
shipment_status {
# ...
}
ordering {
# ...
}
representative {
dial 'SIP/operator'
}
fibonacci {
i = 1
last_i = 0
loop do
say_digits i
last_i, i = i, i + last_i
end
}
Would become the following CallController classes:
class MyMenu < Adhearsion::CallController
def run
# Important! Adhearsion 2 no longer auto-answers the call
answer
menu 'welcome' do
match 1, ShipmentStatus
match 2, Ordering
match 0 do
dial 'SIP/operator'
end
end
end
end
class ShipmentStatus < Adhearsion::CallController
def run
# ...
end
end
class Ordering < Adhearsion::CallController
def run
# ...
end
end
class Fibonacci < Adhearsion::CallController
def run
answer
i = 1
last_i = 0
loop do
say_digits i
last_i, i = i, i + last_i
end
end
end
SimonGame is already a CallController so no wrapper class is required.
Next you will need to map the Asterisk contexts back to these CallControllers. You will need the adhearsion-asterisk plugin to get the route matcher for agi_context
. This goes in config/adhearsion.rb
:
Adhearsion.router do
route 'adhearsion context', SimonGame, agi_context: 'adhearsion'
route 'my_menu context', MyMenu, agi_context: 'my_menu'
route 'fibonacci context', Fibonacci, agi_context: 'fibonacci'
end
Just as with dialplan.rb
, events.rb
has been removed. However the powerful event handling system has only become more powerful in Adhearsion 2.0. Events can now easily be defined anywhere in your application. You may choose to put them into config/adhearsion.rb
if you have a small number of events. However, in most cases it makes sense to package your event handling code either as a standalone class or along with a related CallController. This approach makes testing event handlers much easier.
One other advantage to the new eventing system is the flexibility with which you can subscribe to events. Instead of receiving all Asterisk Manager Interface events and then filtering in application code based on the name, you may elect to register to receive only specific events. More on this is described in the Events documentation.
Given an example events.rb:
events.after_initialized.each do
ahn_log.info "Adhearsion has finished starting up and all I got was this lousy :after_initialized event..."
end
events.exception.each do |e|
ahn_log.info "Ack! I got an exception! The sky is falling and it smells like #{e.class}"
end
events.asterisk.manager_interface.each do |event|
case event.name
when 'NewChannel'
ahn_log.info "A new channel has been created: #{event.inspect}"
when 'NewState'
ahn_log.info "A channel is changing state: #{event.inspect}"
end
end
Would become something like this:
Adhearsion::Events.draw do
after_initialized do
ahn_log.info "Adhearsion has finished starting up and all I got was this lousy :after_initialized event..."
end
exception do |e|
logger.info "Ack! I got an exception! The sky is falling and it smells like #{e.class}"
end
end
Asterisk Manager Interface events are a little different. In the case of NewChannel
events, we may prefer to register for Punchblock::Offer
events, which will work on Asterisk as well as Rayo or any other supported telephony engine in the future. There is an important distinction here though:
NewChannel
events report every new channel that is created in Asterisk, whether or not that call is ultimately routed to Adhearsion.Punchblock::Offer
events only fire for calls that are routed to Adhearsion.Generally, telephony backends do not offer a functional equivalent of the NewChannel
event so there is no direct replacement. Be aware of the differences here. For the purposes of this document, we will assume that Punchblock::Offer
events are sufficient for our needs.
Examples:
Adhearsion::Events.punchblock Punchblock::Offer do |offer|
# This block will only be invoked with Punchblock::Offer events
logger.info "A new channel has been created: #{offer.inspect}"
end
Adhearsion::Events.ami name: 'NewState' do |state|
# This block will only be invoked with "NewState" AMI events
logger.info "A channel is changing state: #{state.inspect}"
end
For examples on using the XMPP events within Adhearsion 2.0, please see the adhearsion-xmpp plugin page.
Adhearsion 1.x had a fixed configuration system for internal configuration and a separate, YAML-based system for configuring components. In Adhearsion 2.0 we wanted to combine these concepts and make it much easier by keeping configuration in one self-documenting location. The first change you will notice is that config/startup.rb
has become config/adhearsion.rb
. The second thing you will notice is how short the default configuration is. The default configuration is the bare minimum needed to get you up and running. However there are many more configuration options available by default. In addition, plugins can register their own configuration variables within their own namespace and these configuration options will become visible as well. To see the full list of configuration options, simply type rake config:show
within your application directory. For more information on using configuration, see the Configuration page.
Adhearsion 1.x had a concept of a "component" which was intended to be reuseable, shareable functionality for Adhearsion. These components could be installed locally within the components/
directory or by installing a gem. However they had several limitations:
Adhearsion 2.0 has reinvented the concept with the new plugin system. For more information, check out the page dedicated to Plugins.
However, you do not have to jump in all the way and create a new plugin for your existing local components. Adhearsion now automatically loads all files placed into the lib/
directory. If you want to migrate an existing component to a simple CallController, this is the fastest and easiest way to do it. For example, if we had a component named "conference_login" like this:
methods_for :dialplan do
def conference_login
room = input 4, play: 'file:///prompts/enter-room-number'
return unless (5000..6000).include? room.to_i
room
end
end
Just convert this to a CallController and move it to lib/conference_login.rb
:
class ConferenceLogin < Adhearsion::CallController
def run
...
end
def conference_login
room = input 4, play: 'enter-room-number'
return unless (5000..6000).include? room.to_i
room
end
end
One possible problem with this approach is that, under the old system, the #conference_login
method was avaialble anywhere within dialplan.rb
. Adhearsion 2.0 does offer a way to add functionality to the base CallController class, acheiving the same effect, without needing to monkeypatch. Here is a more direct replacement for the #conference_login
method, still in lib/conference_login.rb
:
module ConferenceLogin
def conference_login
room = input 4, play: 'enter-room-number'
return unless (5000..6000).include? room.to_i
room
end
end
CallController.mixin ConferenceLogin
By using the #mixin
method, the ConferenceLogin is added to all CallControllers and is therefore available anywhere within the scope of any existing or future CallController.