Quantcast
Channel: Mojo Lingo
Viewing all articles
Browse latest Browse all 59

Adhearsion 2: The New Menu DSL

$
0
0

Rapid and painless creation of complex IVRs has always been one of the defining features of Adhearsion for beginning and advanced programmers alike. Through the #menu DSL method, the framework abstracts and packages the output and input management and the complex state machine needed to implement a complete menu with audio prompts, digit checking, retries and failure handling, making creating menus a breeze.

 

The menu DSL has received a major overhaul in Adhearsion 2.0, with the goals of clarifying syntax and adding functionality.

 

Call Controllers

Adhearsion 2.0 is now using a new routing architecture with powerful and flexible call controllers. Call controllers allow your code to be grouped into classes, making testing easier and improving the readability of your code. For more information see Ben Langfeld's excellent introduction to call controllers and routing.

 

The Old Way

The Adhearsion 1.x menu structure used to work as follows, with the now obsolete concept of a dialplan and context.

 

foo {
  menu "Press 1 for Administration", "Press 2 for Tech Support",
       :timeout => 8.seconds, :tries => 3 do |link|
    link.admin 1
    link.tech 2
    link.on_invalid do
      speak 'Invalid input'
    end

    link.on_premature_timeout do
      speak 'Sorry'
    end

    link.on_failure do
      speak 'Goodbye'
      hangup
    end
  end
}

admin {
  speak 'You have reached the Administration department'
}

tech {
  speak 'You have reached the Technical Support office'
}

 

The main shortcomings of this structure were mostly related to the inherent difficulty of maintaining a complex dialplan all in a single file, or breaking it up in helper components with no imposed logical structure. The old DSL was also not very intuitive, as you were specifying the target for the link before the pattern, in addition to having the block argument as a basically redundant part.

 

The New & Improved Way

The focus for the menu DSL in Adhearsion 2.0 was primarily on improving its functionality to work with call controllers and to fit the new framework structure. Working towards those goals, the menu definition block was streamlined while keeping readability and the general functionality of 1.x.

 

class MyController < Adhearsion::CallController
  def run
    answer
    menu "Where can we take you today?",
         :timeout => 8.seconds, :tries => 3 do
      match 1, BooController
      match "2", MyOtherController
      match 3, 4, { pass YetAnotherController }
      match 5, FooController
      match 6..10 do |dialed|
        say_dialed dialed
      end

      timeout { do_this_on_timeout }

      invalid do
        invoke InvalidController
      end

      failure do
        speak 'Goodbye'
        hangup
      end
    end

    speak "This code gets executed unless #pass is used"
  end

  def say_dialed(dialed)
    speak "#{dialed} was dialed"
  end

  def do_this_on_timeout
    speak 'Timeout'
  end
end

 

The first arguments to #menu are a list of sounds to play, as accepted by #play, including strings for TTS, Date and Time objects, and file paths. #play and the other input and output methods, all renovated, will be covered in a subsequent post. Sounds will be played at the beginning of the menu and after each timeout or invalid input, if the maximum number of tries has not been reached yet.

 

The :tries and :timeout options respectively specify the number of tries before going into failure, and the timeout in seconds allowed before the first and each subsequent digit input.

 

The most important section is the following block, which specifies how the menu will be constructed and handled.

 

The #match method takes an Integer, a String, a Range or any number of them as the required input(s) for the match payload to be executed. The last argument to a #match is either the name of a CallController, which will be invoked, or a block to be executed. Matched input is passed in to the associated block, or to the controller through it instance variable @options[:extension].

 

#menu executes the payload for the first exact unambiguous match it finds after each input or timing out. In a situation where there might be overlapping patterns, such as 10 and 100, #menu will wait for timeout after the second digit.

 

Internally, the state machine has been re-implemented without using exceptions as a mean for flow control, which was a concern for #menu usage in begin..rescue blocks.

 

#timeout, #invalid and #failure replace #on_invalid, #on_premature_timeout and #on_failure. All of them only accept blocks as payload, but #pass or #invoke can be used to execute controllers inside them.

 

#invalid has its associated block executed when the input does not possibly match any pattern. #timeout block is run when time expires before or between input digits, without there being at least one exact match. #failure runs its block when the maximum number of tries is reached without an input match.

 

Execution of the current context resumes after #menu finishes. If you wish to jump to an entirely different controller, #pass can be used. #menu will return :failed if failure was reached, or :done if a match was executed.

Conclusions

The menu DSL is the only the first of many consumer-facing innovations Adhearsion 2.0 contains.

It showcases the driving force behind the new version: to have Adhearsion move out of its box as a companion to Asterisk to become a general purpose telephony framework.

More posts and examples will be published showing what you can build with Adhearsion 2.0. Stay tuned!


Viewing all articles
Browse latest Browse all 59

Trending Articles