The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

WWW::Twilio::TwiML - Light and fast TwiML generator

SYNOPSIS

  use WWW::Twilio::TwiML;

  my $t = new WWW::Twilio::TwiML;
  $t->Response->Dial("+1234567890");
  print $t->to_string;

DESCRIPTION

WWW::Twilio::TwiML creates Twilio-compatible TwiML documents. Documents can be built by creating and nesting one element at a time or by chaining objects. Elements can contain attributes, text content, or other elements.

TwiML, being XML, could be trivially generated with XML::LibXML or any number of other XML parsers/generators. Philosophically, WWW::Twilio::TwiML represents an economical TwiML generator. It has a small footprint (TwiML documents are typically small and simple) and means to make TwiML creation straightforward and moderately fun.

WWW::Twilio::TwiML's primary aim is for economy of expression. Therefore, Any method you call on a TwiML object (except those described below) will create new TwiML objects by the name of the method you called. By chaining method calls, you can create robust TwiML documents with very little code.

new( key => value, ... )

Creates a new TwiML object. With no arguments, this will create a root for your TwiML document. You can also call new with name, content, or attributes arguments to create unattached elements.

The following examples all create the this TwiML document using different calling styles:

  <?xml version="1.0" encoding="UTF-8" ?>
  <Response>
    <Say voice="man">Kilroy was here</Say>
  </Response>

The upside-down, piecemeal, verbose way:

  my $say = new WWW::Twilio::TwiML;
  $say->name('Say');
  $say->content("Kilroy was here");
  $say->attributes({voice => "man"});

  my $resp = new WWW::Twilio::TwiML;
  $resp->name('Response');
  $resp->content($say);

  my $tw = new WWW::Twilio::TwiML;
  $tw->content($resp);
  print $tw->to_string;

The same thing, with a little more powerful constructor:

  my $say = new WWW::Twilio::TwiML(name => 'Say',
                                   content => "Kilroy was here",
                                   attributes => {voice => "man"});

  my $tw = new WWW::Twilio::TwiML;
  $tw->Response->add_child($say);
  print $tw->to_string;

The concise way:

  my $tw = new WWW::Twilio::TwiML;
  $tw->Response->Say({voice => "man"}, "Kilroy was here");
  print $tw->to_string;

And the obligatory one-liner (spread across 4 lines for readability):

  print WWW::Twilio::TwiML->new
    ->Response
    ->Say({voice => "man"}, "Kilroy was here")
    ->root->to_string;

What you don't see in the latter two examples is that both Response and Say create and return objects with the names Response and Say respectively. When called in this way, methods can chain, making for compact, yet readable expressions.

Any TwiML Verb( string | { attributes } )

Constructor shortcuts. TwiML verbs are described at http://www.twilio.com/docs/api/twiml. Some examples include Response, Say, Play, Gather, Record, Sms, Dial, Number, Client, Conference, Hangup, Redirect, Reject, and Pause (this list may be out of date with respect to the official documentation).

See Twilio's documentation for usage for these and other TwiML verbs.

The (any TwiML verb) shortcut is a constructor of a TwiML object. When you call (any TwiML verb) on an existing TwiML object, the following occurs:

  • A new object is created and named by the method you called. E.g., if you called:

      $tw->Response;

    a TwiML object named 'Response' will be created.

  • The newly created object is attached to its parent (the object called to create it).

  • The parent object has the new object added to its list of children.

These last two items means the objects are "chained" to each other. Chaining objects allows concise expressions to create TwiML documents. We could add another object to the chain:

  $tw->Response
    ->Say("I'm calling you.")
      ->parent
    ->Dial("+17175558309");

The parent method returns the Say object's parent (Response object), and we chain a Dial object from it. The resulting $tw object returns:

  <?xml version="1.0" encoding="UTF-8" ?>
  <Response>
    <Say>I&apos;m calling you.</Say>
    <Dial>+17175558309</Dial>
  </Response>

name( string )

Gives a name to an element. This is what is used when the element is printed out. If you're generally chaining objects, you won't use this method often.

  $elem->name('Dial');
  $elem->content("+1234567890");

becomes:

  <Dial>+1234567890</Dial>

When no string is supplied, the name of the object is returned.

  print $elem->name . "\n";

The element name may also be given with new or is implicit when you call the constructor by the name of the element you want to create.

content( string | object )

Sets the content of an element. A TwiML object's content can be either a string or a listref of objects, but not both. If the argument is another WWW::Twilio::TwiML object, the content of the element (if any) will be replaced with the object. Any other argument will be considered string content.

  my $say = new WWW::Twilio::TwiML(name => 'Say');
  $say->content("Eat at Joe's!");  ## a string as content

becomes:

  <Say>Eat at Joe&apos;s!</Say>

Now we can add $say to another element:

  my $parent = new WWW::Twilio::TwiML(name => 'Response');
  $parent->content($say);  ## an object as content

which becomes:

  <Response>
    <Say>Eat at Joe&apos;s!</Say>
  </Response>

When no argument is supplied, the existing contents are returned.

  my $content = $elem->content;
  if( ref($content) ) {
    for my $obj ( @$content ) {
      ## do something with each $obj
    }
  }

  else {
    print $content . "\n";  ## assuming a string here
  }

add_child( object )

Adds an element to the content of the TwiML object. Returns a reference to the added object. Unlike content, add_child does not replace the existing content, but appends an object to the existing content. Also unlike content, add_child is not appropriate to use for setting text content of an element.

  my $tw = new WWW::Twilio::TwiML;
  my $resp = $tw->Response;
  $resp->add_child(new WWW::Twilio::TwiML(name => 'Say',
                                          content => 'Soooey!'));

  my $email = uri_escape('biff@example.com');
  my $msg = uri_escape("Heeer piiiig!");
  my $url = "http://twimlets.com/voicemail?Email=$email&Message=$msg";
  $resp->add_child(new WWW::Twilio::TwiML(name => 'Redirect',
                                          content => $url));

  print $tw->to_string({'Content-type' => 'text/xml'});

becomes:

  Content-type: text/xml

  <?xml version="1.0" encoding="UTF-8" ?>
  <Response>
    <Say>Soooey!</Say>
    <Redirect>http://twimlets.com/voicemail?Email=\
    biff%40example.com&amp;Message=Heeer%20piiiig!</Redirect>
  </Response>

attributes({ key => value })

Sets attributes for an element. If a hash reference is not supplied, a hashref of the existing attributes is returned.

  my $elem = new WWW::Twilio::TwiML(name => 'Say');
  $elem->attributes({voice => 'woman'});
  $elem->content("gimme another donut");

becomes:

  <Say voice="woman">gimme another donut</Say>

root

Returns a handle to the root object.

  print WWW::Twilio::TwiML->new
    ->Response
    ->Say("All men are brothers,")
      ->parent
    ->Say("Like Jacob and Esau.")
    ->root->to_string;

prints:

  <?xml version="1.0" encoding="UTF-8" ?>
  <Response>
    <Say>All men are brothers,</Say>
    <Say>Like Jacob and Esau.</Say>
  </Response>

root is a convenient way to get a handle to the root TwiML object when you're ready to print.

to_string( { header => value } )

Returns the object as a string. Unnamed (root) elements will include the XML declaration entity. If a hashref is supplied, those will be emitted as RFC 822 headers followed by a blank line.

Example:

  print WWW::Twilio::TwiML->new->to_string;

prints:

  <?xml version="1.0" encoding="UTF-8" ?>

while this:

  print WWW::Twilio::TwiML->new
    ->Response
    ->Say("plugh")
    ->root->to_string;

prints:

  <?xml version="1.0" encoding="UTF-8" ?>
  <Response>
    <Say>plugh</Say>
  </Response>

If we forget the call to root in the previous example, like this:

  print WWW::Twilio::TwiML->new
    ->Response
    ->Say("plugh")
    ->to_string;

we get:

  <Say>plugh</Say>

because to_string is being applied to the object created by Say, not $tw.

By specifying a hashref, you can add RFC 822 headers to your documents:

  $tw = new WWW::Twilio::TwiML;
  $tw->Response->Say('Arf!');
  $tw->to_string({'Content-type' => 'text/xml'});

which returns:

  Content-type: text/xml

  <?xml version="1.0" encoding="UTF-8" ?>
  <Response>
    <Say>Arf!</Say>
  </Response>

parent( object )

Sets the parent of the object; this done automatically by add_child and content. When no arguments are given, the existing parent object is returned.

Because WWW::Twilio::TwiML objects chain, parent is useful for getting the previous object so you can add more content to it:

  WWW::Twilio::TwiML->new
    ->Response
    ->Gather({action => "/process_gather.cgi", method => "GET"})
      ->Say("Please enter your account number.")
        ->parent  ## Say's parent, Gather
      ->parent    ## Gather's parent, Response
    ->Say("We didn't receive any input. Goodbye!");

becomes:

  <Response>
    <Gather action="/process_gather.cgi" method="GET">
      <Say>Please enter your account number.</Say>
    </Gather>
    <Say>We didn't receive any input. Goodbye!</Say>
  </Response>

A note on readability: the author recommends indenting multi-line chains to show the parent-child relationship. Each time parent is invoked, the next line should be outdented, as illustrated above.

PACKAGE VARIABLES

You may control the behavior of WWW::Twilio::TwiML in several ways by setting package variables described in this section.

Newlines

You may change the default newline from "\n" to anything else by setting the $NL package variable:

  local $WWW::Twilio::TwiML::NL = "\r\n";

Strict mode

WWW:Twilio::TwiML is capable of generating well-formed but invalid TwiML documents. WWW::Twilio::TwiML uses autoloaded methods to determine the name of TwiML elements (Response, Say, Dial, Redirect, etc.); this means that if you specify an incorrectly named method, your TwiML will be incorrect:

  $tw->Response->Saay('plugh');

Saay is not a valid Twilio TwiML tag and you will not know it until Twilio's TwiML parser attempts to handle your TwiML document.

You may enable strict checks on the TwiML elements at runtime by setting two package variables:

$STRICT

When true, WWW::Twilio::TwiML's autoloader will look up the unhandled method call in the %TAGS package variable (below). If the method name is not in that hash, the autoloader will die with an "Undefined subroutine" error.

%TAGS

Empty by default. When $STRICT is true, this hash will be consulted to determine whether a method call is a valid TwiML tag or not.

For example:

  local $WWW::Twilio::TwiML::STRICT = 1;
  local %WWW::Twilio::TwiML::TAGS = (Response => 1, Say => 1, Dial => 1);

Now any methods invoked on WWW::Twilio::TwiML objects that are not Response, Say, or Dial will die with an error. E.g.:

  WWW::Twilio::TwiML->Response->Saay("Let's play Twister!");

generates the following fatal error:

  Undefined subroutine Saay at line 1.

You may wish to use the fast hash creation with hash slices (I learned this syntax from Damian Conway at a conference some years ago--it's faster than map over an array for building hashes):

  ## TwiML verbs taken from http://www.twilio.com/docs/api/twiml
  my @tags = qw(Response Say Play Gather Record Sms Dial Number
                Client Conference Hangup Redirect Reject Pause);

  local @WWW::Twilio::TwiML::TAGS{@tags} = (1) x @tags;
  local $WWW::Twilio::TwiML::STRICT = 1;

  ## all method calls in this scope are now strict
  ...

EXAMPLES

This section demonstrates a few things you can do with WWW::Twilio::TwiML.

Example 1

  $t = new WWW::Twilio::TwiML;
  $t->Response->Say({voice => "woman"}, "This is Jenny");
  print $t->to_string({'Content-type' => 'text/xml'});

Output:

  Content-type: text/xml

  <?xml version="1.0" encoding="UTF-8" ?>
  <Response>
    <Say voice="woman">This is Jenny</Say>
  </Response>

Examples from twilio.com

The following examples are from twilio.com's TwiML documentation, listed by the primary verb they implement. Assume a TwiML object $tw for each of these examples has already been created:

  my $tw = new WWW::Twilio::TwiML;

and consequently each example would be printed with:

  print $tw->to_string;

See the t/twilio.t test file distributed with this package for additional context for these examples.

Say
  $tw->Response
    ->Say({voice => "woman", loop => "2"}, "Hello");
Play
  $tw->Response
    ->Play("http://foo.com/cowbell.mp3");
Gather
  $tw->Response
    ->Gather({action => "/process_gather.cgi", method => "GET"})
      ->Say("Enter something, or not")
        ->parent
      ->parent
    ->Redirect({method => "GET"}, "/process_gather.cgi?Digits=TIMEOUT");
Record
  $tw->Response
    ->Say("Please leave a message at the beep. \
           Press the star key when finished.")
      ->parent
    ->Record({action => "http://foo.edu/handleRecording.cgi",
              method => "GET",
              maxLength => "20",
              finishOnKey => "*"});
      ->parent
    ->Say("I did not receive a recording");
Sms
  $tw->Response
    ->Say("Our store is located at 123 East St.")
      ->parent
    ->Sms({action => "/smsHandler.cgi", method => "POST"},
          "Store location: 123 East St.");
Dial
  $tw->Response
    ->Dial
      ->Number("858-987-6543")->parent
      ->Number("415-123-4567")->parent
      ->Number("619-765-4321");
Conference
  $tw->Response
    ->Dial
      ->Conference({startConferenceOnEnter => "true",
                    endConferenceOnExit => "true"},
                   "1234");
Hangup
  $tw->Response->Hangup;
Redirect
  $tw->Response
    ->Dial("415-123-4567")->parent
    ->Redirect("http://www.foo.com/nextInstructions");
Reject
  $tw->Response
    ->Reject({reason => "busy"});
Pause
  $tw->Response
    ->Pause({length => 5})->parent
    ->Say("Hi there.");

Other examples

Other examples may be found in the t directory that came with this package, also available on CPAN.

COMPATIBILITY

WWW::Twilio::TwiML will likely be forward compatible with all future revisions of Twilio's TwiML language. This is because method calls are constructors which generate TwiML objects on the fly.

For example, say Twilio began to support a Belch verb (if only!), we could take advantage of it immediately by simply calling a Belch method like this:

  my $tw = new WWW::Twilio::TwiML;
  $tw->Response->Belch('Braaaaaap!');
  print $tw->to_string;

Because there is no Belch method, WWW::Twilio::TwiML assumes you want to create a node by that name and makes one for you:

  <?xml version="1.0" encoding="UTF-8" ?>
  <Response>
    <Belch>Braaaaaap!</Belch>
  </Response>

If the $STRICT package variable is enabled, all we need to do is add Belch to our %TAGS hash and we're good to go.

SEE ALSO

WWW::Twilio::API

AUTHOR

Scott Wiersdorf, <scott@perlcode.org>

COPYRIGHT AND LICENSE

Copyright (C) 2011 by Scott Wiersdorf

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.1 or, at your option, any later version of Perl 5 you may have available.