twilio_resource 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +78 -0
- data/lib/twilio_resource.rb +27 -0
- data/lib/twilio_resource/account.rb +6 -0
- data/lib/twilio_resource/base.rb +83 -0
- data/lib/twilio_resource/call.rb +20 -0
- data/lib/twilio_resource/exceptions.rb +39 -0
- data/lib/twilio_resource/incoming_phone_number.rb +6 -0
- data/lib/twilio_resource/local_incoming_phone_number.rb +14 -0
- data/lib/twilio_resource/toll_free_incoming_phone_number.rb +14 -0
- data/lib/twilio_resource/twilio_format.rb +56 -0
- data/test/account_test.rb +17 -0
- data/test/call_test.rb +33 -0
- data/test/incoming_phone_number_test.rb +20 -0
- data/test/local_incoming_phone_number_test.rb +39 -0
- data/test/test_helper.rb +7 -0
- data/test/toll_free_incoming_phone_number_test.rb +25 -0
- data/test/twilio_mock.rb +334 -0
- metadata +95 -0
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
Description
|
2
|
+
-----------
|
3
|
+
|
4
|
+
TwilioResource is a wrapper around the [Twilio API](http://www.twilio.com/docs/api/2008-08-01/rest/)
|
5
|
+
using Rails' ActiveResource. This allows you to treat Twilio objects
|
6
|
+
as if they're local ActiveRecord models:
|
7
|
+
|
8
|
+
phone = TwilioResource::LocalIncomingPhoneNumber.new(:url => 'http://www.example.com',
|
9
|
+
:area_code => '206',
|
10
|
+
:method => 'GET',
|
11
|
+
:friendly_name =g> "My First Phone Number",
|
12
|
+
:account_id => Twilio::Base.user)
|
13
|
+
phone.save
|
14
|
+
|
15
|
+
|
16
|
+
The wrapper doesn't support the entire Twilio API, but it should be
|
17
|
+
simple enough to add new functionality as needed by following the
|
18
|
+
models that are already built.
|
19
|
+
|
20
|
+
Supported Functionality
|
21
|
+
=======================
|
22
|
+
- Requesting, listing, and deleting new local and toll-free numbers
|
23
|
+
- Placing and listing calls made to a phone number
|
24
|
+
|
25
|
+
Todo
|
26
|
+
----
|
27
|
+
- Conference resource
|
28
|
+
- Redirecting calls
|
29
|
+
- Support data returned in xml attributes
|
30
|
+
- SMS resource
|
31
|
+
- Recordings, Transcriptions, and Notifications
|
32
|
+
- Wrap more exceptions
|
33
|
+
- Add documentation on supported attributes to the ActiveResource models
|
34
|
+
|
35
|
+
Setup
|
36
|
+
=====
|
37
|
+
|
38
|
+
Before you start using TwilioResource, you have to give it your Twilio
|
39
|
+
login credentials. You can do this in your Rails initializer:
|
40
|
+
|
41
|
+
require 'twilio_resource'
|
42
|
+
TwilioResource.setup('token', 'sid')
|
43
|
+
|
44
|
+
Examples
|
45
|
+
========
|
46
|
+
|
47
|
+
Requesting a new phone number:
|
48
|
+
|
49
|
+
phone = TwilioResource::LocalIncomingPhoneNumber.new(:url => 'http://www.example.com',
|
50
|
+
:area_code => '206',
|
51
|
+
:method => 'GET',
|
52
|
+
:friendly_name => "My First Phone Number",
|
53
|
+
:account_id => Twilio::Base.user)
|
54
|
+
phone.save
|
55
|
+
|
56
|
+
Requesting a new toll-free phone number:
|
57
|
+
|
58
|
+
phone = TwilioResource::TollFreeIncomingPhoneNumber.new(:url => 'http://www.example.com',
|
59
|
+
:area_code => '206',
|
60
|
+
:method => 'GET',
|
61
|
+
:friendly_name => "My First Phone Number",
|
62
|
+
:account_id => Twilio::Base.user)
|
63
|
+
phone.save
|
64
|
+
|
65
|
+
Retrieving a list of completed calls to a given phone number:
|
66
|
+
|
67
|
+
TwilioResource::Call.find(:all, :params => {
|
68
|
+
:account_id => Twilio::Base.user,
|
69
|
+
:status => Twilio::Call::COMPLETE,
|
70
|
+
:called => '2065551212'})
|
71
|
+
|
72
|
+
Placing a call:
|
73
|
+
|
74
|
+
TwilioResource::Call.new({
|
75
|
+
:account_id => Twilio::Base.user,
|
76
|
+
:caller => '2065551111',
|
77
|
+
:called => '2065551212',
|
78
|
+
:url => 'http://example.com/call.xml'})
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'activeresource'
|
3
|
+
|
4
|
+
require 'twilio_resource/twilio_format'
|
5
|
+
require 'twilio_resource/exceptions'
|
6
|
+
|
7
|
+
module TwilioResource
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :logger
|
11
|
+
end
|
12
|
+
|
13
|
+
# Sets up the credentials the ActiveResources will use to access the
|
14
|
+
# twilio API. Optionally takes a +logger+, which defaults to +STDOUT+.
|
15
|
+
def self.setup(sid, token, logger = nil)
|
16
|
+
@logger = logger || Logger.new(STDOUT)
|
17
|
+
TwilioResource::Base.sid = sid
|
18
|
+
TwilioResource::Base.token = token
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'twilio_resource/base'
|
23
|
+
require 'twilio_resource/account'
|
24
|
+
require 'twilio_resource/call'
|
25
|
+
require 'twilio_resource/incoming_phone_number'
|
26
|
+
require 'twilio_resource/local_incoming_phone_number'
|
27
|
+
require 'twilio_resource/toll_free_incoming_phone_number'
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Encapsulates the changes that need to be made to active_resource's
|
2
|
+
# defaults in order to communicate with Twilio. There are a few main
|
3
|
+
# issues with ActiveResource's defaults:
|
4
|
+
#
|
5
|
+
# 1. Twilio's urls don't take an extension. ActiveResource requires
|
6
|
+
# endpoints to have an extension corresponding to the type of data,
|
7
|
+
# for example: +resource.xml+ or +resource.json+. Twilio's are
|
8
|
+
# always xml, but have no extension.
|
9
|
+
#
|
10
|
+
# 2. Twilio uses capitalized names in their URLs. For instance,
|
11
|
+
# instead of '/account/1/calls/1', they use '/Account/1/Calls/1'.
|
12
|
+
#
|
13
|
+
# 3. Twilio takes form encoded params, like token=blah&sid=foo, but
|
14
|
+
# returns data in XML. These changes are encapsulated in
|
15
|
+
# ActiveResource::Formats::TwilioFormat.
|
16
|
+
#
|
17
|
+
# All of the Twilio ActiveResource classes inherit from this class.
|
18
|
+
class TwilioResource::Base < ActiveResource::Base
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_accessor :sid
|
22
|
+
attr_accessor :token
|
23
|
+
|
24
|
+
alias_method :user, :sid
|
25
|
+
alias_method :user=, :sid=
|
26
|
+
alias_method :password, :token
|
27
|
+
alias_method :password=, :token=
|
28
|
+
end
|
29
|
+
|
30
|
+
# Have to override this to make empty extensions work (without the dot)
|
31
|
+
def self.element_path(id, prefix_options = {}, query_options = nil)
|
32
|
+
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
|
33
|
+
extension = format.extension.blank? ? "" : ".#{format.extension}"
|
34
|
+
path = "#{prefix(prefix_options)}#{collection_name}/#{id}#{extension}#{query_string(query_options)}"
|
35
|
+
TwilioResource.logger.info("Request: #{path}")
|
36
|
+
path
|
37
|
+
end
|
38
|
+
|
39
|
+
# Have to override this to make empty extensions work (without the dot)
|
40
|
+
def self.collection_path(prefix_options = {}, query_options = nil)
|
41
|
+
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
|
42
|
+
extension = format.extension.blank? ? "" : ".#{format.extension}"
|
43
|
+
path = "#{prefix(prefix_options)}#{collection_name}#{extension}#{query_string(query_options)}"
|
44
|
+
TwilioResource.logger.info("Request: #{path}")
|
45
|
+
path
|
46
|
+
end
|
47
|
+
|
48
|
+
# Have to override this to make empty extensions work (without the dot)
|
49
|
+
def self.custom_method_collection_url(method_name, options = {})
|
50
|
+
prefix_options, query_options = split_options(options)
|
51
|
+
extension = format.extension.blank? ? "" : ".#{format.extension}"
|
52
|
+
path = "#{prefix(prefix_options)}#{collection_name}/#{method_name}#{extension}#{query_string(query_options)}"
|
53
|
+
TwilioResource.logger.info("Request: #{path}")
|
54
|
+
path
|
55
|
+
end
|
56
|
+
|
57
|
+
# Twilio uses capitalized path names
|
58
|
+
def self.element_name
|
59
|
+
super.camelize
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.query_string(params)
|
63
|
+
# camelize all the param keys, because that's what Twilio expects
|
64
|
+
fixed_params_list = params.map {|k, v| [k.to_s.camelize, v]}.flatten
|
65
|
+
fixed_params_hash = Hash[*fixed_params_list] # Hash really needs a decent #map
|
66
|
+
super(fixed_params_hash)
|
67
|
+
end
|
68
|
+
|
69
|
+
# we should wrap the exceptions we can
|
70
|
+
def save(*params)
|
71
|
+
begin
|
72
|
+
super(*params)
|
73
|
+
rescue => e
|
74
|
+
raise TwilioResource::Exception.find_exception(e)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
self.site = "https://api.twilio.com"
|
81
|
+
self.prefix = '/2008-08-01/'
|
82
|
+
self.format = :twilio
|
83
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
# Encapsulates a Twilio call resource. Documentation:
|
3
|
+
# - http://www.twilio.com/docs/api/2008-08-01/rest/call
|
4
|
+
# - http://www.twilio.com/docs/api/2008-08-01/rest/making_calls
|
5
|
+
class TwilioResource::Call < TwilioResource::Base
|
6
|
+
self.prefix = superclass.prefix_source + 'Accounts/:account_id/'
|
7
|
+
|
8
|
+
NOT_DIALED = 0
|
9
|
+
IN_PROGRESS = 1
|
10
|
+
COMPLETE = 2
|
11
|
+
|
12
|
+
# Returns the status of the call. Can be
|
13
|
+
# TwilioResource::Call::NOT_DIALED,
|
14
|
+
# TwilioResource::Call::IN_PROGRESS, or
|
15
|
+
# TwilioResource::Call::COMPLETE
|
16
|
+
def status
|
17
|
+
attributes["status"].to_i if attributes["status"]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module TwilioResource
|
2
|
+
end
|
3
|
+
|
4
|
+
# Wraps Twilio exceptions with a ruby exception class. Currently supports:
|
5
|
+
#
|
6
|
+
# - Error 21452 - No Phone Numbers Found
|
7
|
+
class TwilioResource::Exception < StandardError
|
8
|
+
|
9
|
+
# Given a twilio error xml response, returns the corresponding
|
10
|
+
# exception if it is mapped, or the xml response otherwise.
|
11
|
+
def self.find_exception(e)
|
12
|
+
new_exception = if e.respond_to?(:response)
|
13
|
+
exception_data = Hash.from_xml(e.response.body)
|
14
|
+
|
15
|
+
|
16
|
+
code = exception_data['twilio_response'] &&
|
17
|
+
exception_data['twilio_response']['rest_exception'] &&
|
18
|
+
exception_data['twilio_response']['rest_exception']['code']
|
19
|
+
exception_klass = twilio_exceptions[code.to_i] if code
|
20
|
+
exception_klass.nil? ? e : exception_klass.new
|
21
|
+
else
|
22
|
+
e
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Hash mapping error codes to exception classes.
|
27
|
+
def self.twilio_exceptions
|
28
|
+
{
|
29
|
+
21452 => TwilioResource::NoPhoneNumbersFoundException,
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class TwilioResource::NoPhoneNumbersFoundException < TwilioResource::Exception
|
36
|
+
def message
|
37
|
+
"No phone numbers found"
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# Represents a Twilio-controlled phone number that can receive
|
2
|
+
# incoming phone calls. Documentation is at
|
3
|
+
# http://www.twilio.com/docs/api/2008-08-01/rest/incoming-phone-numbers
|
4
|
+
class TwilioResource::IncomingPhoneNumber < TwilioResource::Base
|
5
|
+
self.prefix = superclass.prefix_source + 'Accounts/:account_id/'
|
6
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Represents a Twilio-controlled "local" phone number that can receive
|
2
|
+
# incoming phone calls. If there are no numbers available in the
|
3
|
+
# specified area code, #save will throw a
|
4
|
+
# TwilioResource::NoPhoneNumbersFoundException. Documentation is at
|
5
|
+
# http://www.twilio.com/docs/api/2008-08-01/rest/incoming-phone-numbers
|
6
|
+
class TwilioResource::LocalIncomingPhoneNumber < TwilioResource::IncomingPhoneNumber
|
7
|
+
|
8
|
+
self.prefix = superclass.prefix_source + 'IncomingPhoneNumbers/'
|
9
|
+
|
10
|
+
def self.collection_name
|
11
|
+
'Local'
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Represents a Twilio-controlled toll-free phone number that can receive
|
2
|
+
# incoming phone calls. If there are no numbers available in the
|
3
|
+
# specified area code, #save will throw a
|
4
|
+
# TwilioResource::NoPhoneNumbersFoundException. Documentation is at
|
5
|
+
# http://www.twilio.com/docs/api/2008-08-01/rest/incoming-phone-numbers
|
6
|
+
class TwilioResource::TollFreeIncomingPhoneNumber < TwilioResource::IncomingPhoneNumber
|
7
|
+
|
8
|
+
self.prefix = superclass.prefix_source + 'IncomingPhoneNumbers/'
|
9
|
+
|
10
|
+
def self.collection_name
|
11
|
+
'TollFree'
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
module Formats
|
3
|
+
module TwilioFormat
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def extension
|
7
|
+
""
|
8
|
+
end
|
9
|
+
|
10
|
+
def mime_type
|
11
|
+
'application/x-www-form-urlencoded'
|
12
|
+
end
|
13
|
+
|
14
|
+
# twilio uses camelcase for all of its params, and uses a query string for posts.
|
15
|
+
def encode(hash, options = {})
|
16
|
+
camelized_hash = ActiveSupport::OrderedHash.new
|
17
|
+
hash.each do |k, v|
|
18
|
+
camelized_hash[k.camelize] = v
|
19
|
+
end
|
20
|
+
|
21
|
+
TwilioResource.logger.info("Request: #{camelized_hash.to_query}")
|
22
|
+
camelized_hash.to_query
|
23
|
+
end
|
24
|
+
|
25
|
+
def decode(xml)
|
26
|
+
TwilioResource.logger.debug("Response: #{xml}")
|
27
|
+
from_xml_data(Hash.from_xml(xml))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
# Manipulate from_xml Hash, because twilio doesn't format their
|
32
|
+
# xml in the way active_resource expects it.
|
33
|
+
def from_xml_data(data)
|
34
|
+
if data.is_a?(Hash) && data['twilio_response']
|
35
|
+
from_xml_data(data['twilio_response'])
|
36
|
+
else
|
37
|
+
data.each do |k, v|
|
38
|
+
# convert array-like things to actual arrays. twilio
|
39
|
+
# paginates their arrays, so it's easy to detect here. If
|
40
|
+
# it's the empty set, initialize it to an array so
|
41
|
+
# active_resource doesn't throw a fit.
|
42
|
+
if v["page"]
|
43
|
+
array_data = v[k.singularize]
|
44
|
+
data[k] = [array_data].flatten.compact
|
45
|
+
elsif v.blank?
|
46
|
+
data[k] = []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# now we have "calls" => [c1, c2, ...], but active_resource
|
50
|
+
# expects [c1, c2, c3]
|
51
|
+
data.values.first
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'twilio_mock'
|
3
|
+
|
4
|
+
class TwilioResource::AccountTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
TwilioMock.setup_remote_fixtures
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_find_account
|
12
|
+
TwilioResource::Base.user = 1
|
13
|
+
account = TwilioResource::Account.find(1)
|
14
|
+
assert_equal "2", account.status
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/test/call_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'twilio_mock'
|
3
|
+
|
4
|
+
class TwilioResource::CallTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
TwilioMock.setup_remote_fixtures
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_find_all
|
12
|
+
calls = TwilioResource::Call.find(:all, :params => {:account_id => 1})
|
13
|
+
assert_equal 2, calls.length
|
14
|
+
end
|
15
|
+
|
16
|
+
# what the hell, twilio? who uses capitalization to distinguish
|
17
|
+
# between param types? you, that's who.
|
18
|
+
def test_find_by_status
|
19
|
+
calls = TwilioResource::Call.find(:all, :params => {:account_id => 1, :status => TwilioResource::Call::COMPLETE})
|
20
|
+
assert_equal TwilioResource::Call::COMPLETE, calls.first.status
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_find_by_start_time
|
24
|
+
calls = TwilioResource::Call.find(:all, :params => {:account_id => 1, :start_time => '2009-09-01'})
|
25
|
+
assert_equal 1, calls.length
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_no_results
|
29
|
+
calls = TwilioResource::Call.find(:all, :params => {:account_id => 1, :status => TwilioResource::Call::COMPLETE, :start_time => '2009-09-01'})
|
30
|
+
assert_equal 0, calls.length
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'twilio_mock'
|
3
|
+
|
4
|
+
class TwilioResource::IncomingPhoneNumberTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
TwilioMock.setup_remote_fixtures
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_find
|
12
|
+
local_number = TwilioResource::IncomingPhoneNumber.find(1, :params => {:account_id => 1})
|
13
|
+
assert_equal '4158675309', local_number.phone_number
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_delete_from_class
|
17
|
+
assert TwilioResource::IncomingPhoneNumber.delete(1, :account_id => 1)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'twilio_mock'
|
3
|
+
|
4
|
+
class TwilioResource::LocalIncomingPhoneNumberTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
TwilioMock.setup_remote_fixtures
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_provision_local_number
|
12
|
+
phone = TwilioResource::LocalIncomingPhoneNumber.new(:url => "http://example.com/calls",
|
13
|
+
:area_code => "206",
|
14
|
+
:method => 'POST',
|
15
|
+
:friendly_name => "My Local Number",
|
16
|
+
:account_id => 1)
|
17
|
+
assert_equal "AreaCode=206&FriendlyName=My+Local+Number&Method=POST&Url=http%3A%2F%2Fexample.com%2Fcalls", phone.encode
|
18
|
+
phone.save
|
19
|
+
|
20
|
+
assert_equal '2064567890', phone.phone_number
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_save_with_unavailable_number_throws_exception
|
24
|
+
TwilioResource::Base.user = 2
|
25
|
+
phone = TwilioResource::LocalIncomingPhoneNumber.new(:url => "http://example.com/calls",
|
26
|
+
:area_code => "815",
|
27
|
+
:method => 'POST',
|
28
|
+
:friendly_name => "My Local Number",
|
29
|
+
:account_id => TwilioResource::Base.user)
|
30
|
+
assert_equal "AreaCode=815&FriendlyName=My+Local+Number&Method=POST&Url=http%3A%2F%2Fexample.com%2Fcalls", phone.encode
|
31
|
+
assert_raises TwilioResource::NoPhoneNumbersFoundException do
|
32
|
+
phone.save
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# test find, update
|
38
|
+
|
39
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'twilio_mock'
|
3
|
+
|
4
|
+
class TwilioResource::TollFreeIncomingPhoneNumberTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
TwilioMock.setup_remote_fixtures
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_provision_toll_free_number
|
12
|
+
TwilioResource::Base.user = 1
|
13
|
+
phone = TwilioResource::TollFreeIncomingPhoneNumber.new(:url => "http://example.com/calls",
|
14
|
+
:method => 'POST',
|
15
|
+
:friendly_name => "My Local Number",
|
16
|
+
:account_id => TwilioResource::Base.user)
|
17
|
+
assert_equal "FriendlyName=My+Local+Number&Method=POST&Url=http%3A%2F%2Fexample.com%2Fcalls", phone.encode
|
18
|
+
phone.save
|
19
|
+
|
20
|
+
assert_equal '8774567890', phone.phone_number
|
21
|
+
end
|
22
|
+
|
23
|
+
# test find, update
|
24
|
+
|
25
|
+
end
|
data/test/twilio_mock.rb
ADDED
@@ -0,0 +1,334 @@
|
|
1
|
+
require 'active_resource/http_mock'
|
2
|
+
|
3
|
+
class TwilioMock
|
4
|
+
|
5
|
+
def self.setup_remote_fixtures
|
6
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
7
|
+
mock.get '/2008-08-01/Accounts/1', auth_get(1), main_account
|
8
|
+
mock.post '/2008-08-01/Accounts/1/IncomingPhoneNumbers/Local', auth_post(1), local_number_provision_success
|
9
|
+
mock.post '/2008-08-01/Accounts/2/IncomingPhoneNumbers/Local', auth_post(2), no_local_number, 400
|
10
|
+
mock.post '/2008-08-01/Accounts/1/IncomingPhoneNumbers/TollFree', auth_post(1), toll_free_number_provision_success
|
11
|
+
|
12
|
+
mock.get '/2008-08-01/Accounts/1/Calls', auth_get(1), all_calls
|
13
|
+
mock.get '/2008-08-01/Accounts/1/Calls?StartTime=2009-09-01', auth_get(1), recent_calls
|
14
|
+
mock.get '/2008-08-01/Accounts/1/Calls?Status=2', auth_get(1), succeeded_recent_calls
|
15
|
+
mock.get '/2008-08-01/Accounts/1/Calls?StartTime=2009-09-01&Status=2', auth_get(1), no_calls
|
16
|
+
|
17
|
+
mock.get '/2008-08-01/Accounts/1/IncomingPhoneNumbers/1', auth_get(1), phone_number_data
|
18
|
+
mock.delete '/2008-08-01/Accounts/1/IncomingPhoneNumbers/1', auth_delete(1), phone_number_delete_success
|
19
|
+
mock.delete '/2008-08-01/Accounts/2/IncomingPhoneNumbers/2', auth_delete(2), "", 404
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.auth_delete(account_id)
|
26
|
+
old_account_id = TwilioResource::Base.user
|
27
|
+
TwilioResource::Base.user = account_id
|
28
|
+
auth = TwilioResource::Account.connection.send(:build_request_headers, {}, :delete)
|
29
|
+
TwilioResource::Base.user = old_account_id
|
30
|
+
auth
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.auth_post(account_id)
|
34
|
+
old_account_id = TwilioResource::Base.user
|
35
|
+
TwilioResource::Base.user = account_id
|
36
|
+
auth = TwilioResource::Account.connection.send(:build_request_headers, {}, :post)
|
37
|
+
TwilioResource::Base.user = old_account_id
|
38
|
+
auth
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.auth_get(account_id)
|
42
|
+
old_account_id = TwilioResource::Base.user
|
43
|
+
TwilioResource::Base.user = account_id
|
44
|
+
auth = TwilioResource::Account.connection.send(:build_request_headers, {}, :get)
|
45
|
+
TwilioResource::Base.user = old_account_id
|
46
|
+
auth
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.phone_number_data
|
50
|
+
<<END
|
51
|
+
<TwilioResponse>
|
52
|
+
<IncomingPhoneNumber>
|
53
|
+
<Sid>PNe536dfda7c6184afab78d980cb8cdf43</Sid>
|
54
|
+
<AccountSid>AC35542fc30a091bed0c1ed511e1d9935d</AccountSid>
|
55
|
+
<FriendlyName>My Home Phone Number</FriendlyName>
|
56
|
+
<PhoneNumber>4158675309</PhoneNumber>
|
57
|
+
<Url>http://mycompany.com/handleMainLineCall.asp</Url>
|
58
|
+
<Method>GET</Method>
|
59
|
+
<DateCreated>Tue, 01 Apr 2008 11:26:32 -0700</DateCreated>
|
60
|
+
<DateUpdated>Tue, 01 Apr 2008 11:26:32 -0700</DateUpdated>
|
61
|
+
</IncomingPhoneNumber>
|
62
|
+
</TwilioResponse>
|
63
|
+
END
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.main_account
|
67
|
+
<<END
|
68
|
+
<TwilioResponse>
|
69
|
+
<Account>
|
70
|
+
<Sid>AC309475e5fede1b49e100272a8640f438</Sid>
|
71
|
+
<FriendlyName>My Twilio Account</FriendlyName>
|
72
|
+
<Status>2</Status>
|
73
|
+
<StatusText>Active</StatusText>
|
74
|
+
<DateCreated>Wed, 02 Apr 2008 17:33:38 -0700</DateCreated>
|
75
|
+
<DateUpdated>Wed, 02 Apr 2008 17:34:18 -0700</DateUpdated>
|
76
|
+
<AuthToken>3a2630a909aadbf60266234756fb15a0</AuthToken>
|
77
|
+
</Account>
|
78
|
+
</TwilioResponse>
|
79
|
+
END
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.phone_number_delete_success
|
83
|
+
<<END
|
84
|
+
<TwilioResponse />
|
85
|
+
END
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.all_calls
|
89
|
+
<<END
|
90
|
+
<TwilioResponse>
|
91
|
+
<Calls page="0" numpages="1" pagesize="50" total="38" start="0" end="37">
|
92
|
+
<Call>
|
93
|
+
<Sid>CA42ed11f93dc08b952027ffbc406d0868</Sid>
|
94
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
95
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
96
|
+
<CallSegmentSid/>
|
97
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
98
|
+
<Called>4159633717</Called>
|
99
|
+
<Caller>4156767925</Caller>
|
100
|
+
<PhoneNumberSid>PN01234567890123456789012345678900</PhoneNumberSid>
|
101
|
+
<Status>2</Status>
|
102
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
103
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
104
|
+
<Duration>14</Duration>
|
105
|
+
<Price/>
|
106
|
+
<Flags>1</Flags>
|
107
|
+
</Call>
|
108
|
+
<Call>
|
109
|
+
<Sid>CA751e8fa0a0105cf26a0d7a9775fb4bfb</Sid>
|
110
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
111
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
112
|
+
<CallSegmentSid/>
|
113
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
114
|
+
<Called>2064287985</Called>
|
115
|
+
<Caller>4156767925</Caller>
|
116
|
+
<PhoneNumberSid>PNd59c2ba27ef48264773edb90476d1674</PhoneNumberSid>
|
117
|
+
<Status>2</Status>
|
118
|
+
<StartTime>Thu, 03 Apr 2008 01:37:05 -0400</StartTime>
|
119
|
+
<EndTime>Thu, 03 Apr 2008 01:37:40 -0400</EndTime>
|
120
|
+
<Duration>35</Duration>
|
121
|
+
<Price/>
|
122
|
+
<Flags>1</Flags>
|
123
|
+
</Call>
|
124
|
+
</Calls>
|
125
|
+
</TwilioResponse>
|
126
|
+
END
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.succeeded_calls
|
130
|
+
<<END
|
131
|
+
<TwilioResponse>
|
132
|
+
<Calls page="0" numpages="1" pagesize="50" total="38" start="0" end="37">
|
133
|
+
<Call>
|
134
|
+
<Sid>CA42ed11f93dc08b952027ffbc406d0868</Sid>
|
135
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
136
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
137
|
+
<CallSegmentSid/>
|
138
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
139
|
+
<Called>4159633717</Called>
|
140
|
+
<Caller>4156767925</Caller>
|
141
|
+
<PhoneNumberSid>PN01234567890123456789012345678900</PhoneNumberSid>
|
142
|
+
<Status>2</Status>
|
143
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
144
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
145
|
+
<Duration>14</Duration>
|
146
|
+
<Price/>
|
147
|
+
<Flags>1</Flags>
|
148
|
+
</Call>
|
149
|
+
<Call>
|
150
|
+
<Sid>1000</Sid>
|
151
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
152
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
153
|
+
<CallSegmentSid>10001</CallSegmentSid>
|
154
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
155
|
+
<Called>4159633717</Called>
|
156
|
+
<Caller>4156767925</Caller>
|
157
|
+
<PhoneNumberSid>ABC123</PhoneNumberSid>
|
158
|
+
<Status>2</Status>
|
159
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
160
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
161
|
+
<Duration>14</Duration>
|
162
|
+
<Price/>
|
163
|
+
<Flags>1</Flags>
|
164
|
+
</Call>
|
165
|
+
<Call>
|
166
|
+
<Sid>1001</Sid>
|
167
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
168
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
169
|
+
<CallSegmentSid>10002</CallSegmentSid>
|
170
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
171
|
+
<Called>4159633717</Called>
|
172
|
+
<Caller>4156767925</Caller>
|
173
|
+
<PhoneNumberSid>ABC123</PhoneNumberSid>
|
174
|
+
<Status>2</Status>
|
175
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
176
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
177
|
+
<Duration>14</Duration>
|
178
|
+
<Price/>
|
179
|
+
<Flags>1</Flags>
|
180
|
+
</Call>
|
181
|
+
<Call>
|
182
|
+
<Sid>1002</Sid>
|
183
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
184
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
185
|
+
<CallSegmentSid>10003</CallSegmentSid>
|
186
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
187
|
+
<Called>4159633717</Called>
|
188
|
+
<Caller>4156767925</Caller>
|
189
|
+
<PhoneNumberSid>ABC123</PhoneNumberSid>
|
190
|
+
<Status>2</Status>
|
191
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
192
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
193
|
+
<Duration>14</Duration>
|
194
|
+
<Price/>
|
195
|
+
<Flags>1</Flags>
|
196
|
+
</Call>
|
197
|
+
<Call>
|
198
|
+
<Sid>1003</Sid>
|
199
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
200
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
201
|
+
<CallSegmentSid>10004</CallSegmentSid>
|
202
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
203
|
+
<Called>4159633717</Called>
|
204
|
+
<Caller>4156767925</Caller>
|
205
|
+
<PhoneNumberSid>UNKNOWN</PhoneNumberSid>
|
206
|
+
<Status>2</Status>
|
207
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
208
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
209
|
+
<Duration>14</Duration>
|
210
|
+
<Price/>
|
211
|
+
<Flags>1</Flags>
|
212
|
+
</Call>
|
213
|
+
<Call>
|
214
|
+
<Sid>AVENDORIDAVENDORIDAVENDORID</Sid>
|
215
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
216
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
217
|
+
<CallSegmentSid>10004</CallSegmentSid>
|
218
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
219
|
+
<Called>4159633717</Called>
|
220
|
+
<Caller>4156767925</Caller>
|
221
|
+
<PhoneNumberSid>ABC123</PhoneNumberSid>
|
222
|
+
<Status>2</Status>
|
223
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
224
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
225
|
+
<Duration>14</Duration>
|
226
|
+
<Price/>
|
227
|
+
<Flags>1</Flags>
|
228
|
+
</Call>
|
229
|
+
</Calls>
|
230
|
+
</TwilioResponse>
|
231
|
+
END
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.recent_calls
|
235
|
+
<<END
|
236
|
+
<TwilioResponse>
|
237
|
+
<Calls page="0" numpages="1" pagesize="50" total="38" start="0" end="37">
|
238
|
+
<Call>
|
239
|
+
<Sid>CA42ed11f93dc08b952027ffbc406d0868</Sid>
|
240
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
241
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
242
|
+
<CallSegmentSid/>
|
243
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
244
|
+
<Called>4159633717</Called>
|
245
|
+
<Caller>4156767925</Caller>
|
246
|
+
<PhoneNumberSid>PN01234567890123456789012345678900</PhoneNumberSid>
|
247
|
+
<Status>1</Status>
|
248
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
249
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
250
|
+
<Duration>14</Duration>
|
251
|
+
<Price/>
|
252
|
+
<Flags>1</Flags>
|
253
|
+
</Call>
|
254
|
+
</Calls>
|
255
|
+
</TwilioResponse>
|
256
|
+
END
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
def self.succeeded_recent_calls
|
261
|
+
<<END
|
262
|
+
<TwilioResponse>
|
263
|
+
<Calls page="0" numpages="1" pagesize="50" total="38" start="0" end="37">
|
264
|
+
<Call>
|
265
|
+
<Sid>CA42ed11f93dc08b952027ffbc406d0868</Sid>
|
266
|
+
<DateCreated>Sat, 07 Feb 2009 13:15:19 -0800</DateCreated>
|
267
|
+
<DateUpdated>Sat, 07 Feb 2009 13:15:19 -0800</DateUpdated>
|
268
|
+
<CallSegmentSid>8142ed11f93dc08b952027ffbc406d0868</CallSegmentSid>
|
269
|
+
<AccountSid>AC309475e5fede1b49e100272a8640f438</AccountSid>
|
270
|
+
<Called>4159633717</Called>
|
271
|
+
<Caller>4156767925</Caller>
|
272
|
+
<PhoneNumberSid>PN01234567890123456789012345678900</PhoneNumberSid>
|
273
|
+
<Status>2</Status>
|
274
|
+
<StartTime>Thu, 03 Apr 2008 04:36:33 -0400</StartTime>
|
275
|
+
<EndTime>Thu, 03 Apr 2008 04:36:47 -0400</EndTime>
|
276
|
+
<Duration>14</Duration>
|
277
|
+
<Price/>
|
278
|
+
<Flags>1</Flags>
|
279
|
+
</Call>
|
280
|
+
</Calls>
|
281
|
+
</TwilioResponse>
|
282
|
+
END
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.no_calls
|
286
|
+
<<END
|
287
|
+
<TwilioResponse>
|
288
|
+
<Calls page="0" numpages="1" pagesize="50" total="38" start="0" end="37">
|
289
|
+
</Calls>
|
290
|
+
</TwilioResponse>
|
291
|
+
END
|
292
|
+
end
|
293
|
+
|
294
|
+
def self.local_number_provision_success
|
295
|
+
<<END
|
296
|
+
<TwilioResponse>
|
297
|
+
<IncomingPhoneNumber>
|
298
|
+
<Sid>PNe536dfda7c6184afab78d980cb8cdf43</Sid>
|
299
|
+
<AccountSid>AC35542fc30a091bed0c1ed511e1d9935d</AccountSid>
|
300
|
+
<FriendlyName>My Local Number</FriendlyName>
|
301
|
+
<PhoneNumber>2064567890</PhoneNumber>
|
302
|
+
<Url>http://example.com/calls</Url>
|
303
|
+
<Method>GET</Method>
|
304
|
+
<DateCreated>Tue, 01 Apr 2008 11:26:32 -0700</DateCreated>
|
305
|
+
<DateUpdated>Tue, 01 Apr 2008 11:26:32 -0700</DateUpdated>
|
306
|
+
</IncomingPhoneNumber>
|
307
|
+
</TwilioResponse>
|
308
|
+
END
|
309
|
+
end
|
310
|
+
|
311
|
+
def self.toll_free_number_provision_success
|
312
|
+
<<END
|
313
|
+
<TwilioResponse>
|
314
|
+
<IncomingPhoneNumber>
|
315
|
+
<Sid>PNe536dfda7c6184afab78d980cb8cdf43</Sid>
|
316
|
+
<AccountSid>AC35542fc30a091bed0c1ed511e1d9935d</AccountSid>
|
317
|
+
<FriendlyName>My Toll Free Number</FriendlyName>
|
318
|
+
<PhoneNumber>8774567890</PhoneNumber>
|
319
|
+
<Url>http://example.com/calls</Url>
|
320
|
+
<Method>GET</Method>
|
321
|
+
<DateCreated>Tue, 01 Apr 2008 11:26:32 -0700</DateCreated>
|
322
|
+
<DateUpdated>Tue, 01 Apr 2008 11:26:32 -0700</DateUpdated>
|
323
|
+
</IncomingPhoneNumber>
|
324
|
+
</TwilioResponse>
|
325
|
+
END
|
326
|
+
end
|
327
|
+
|
328
|
+
def self.no_local_number
|
329
|
+
<<END
|
330
|
+
<TwilioResponse><RestException><Status>400</Status><Message>No phone numbers found</Message><Code>21452</Code><MoreInfo>http://www.twilio.com/docs/errors/21452</MoreInfo></RestException></TwilioResponse>
|
331
|
+
END
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: twilio_resource
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Justin Weiss
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-16 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: activeresource
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 2
|
29
|
+
- 3
|
30
|
+
- 2
|
31
|
+
version: 2.3.2
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
description:
|
35
|
+
email: justin@uberweiss.org
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- README.md
|
42
|
+
files:
|
43
|
+
- lib/twilio_resource/account.rb
|
44
|
+
- lib/twilio_resource/base.rb
|
45
|
+
- lib/twilio_resource/call.rb
|
46
|
+
- lib/twilio_resource/exceptions.rb
|
47
|
+
- lib/twilio_resource/incoming_phone_number.rb
|
48
|
+
- lib/twilio_resource/local_incoming_phone_number.rb
|
49
|
+
- lib/twilio_resource/toll_free_incoming_phone_number.rb
|
50
|
+
- lib/twilio_resource/twilio_format.rb
|
51
|
+
- lib/twilio_resource.rb
|
52
|
+
- test/account_test.rb
|
53
|
+
- test/call_test.rb
|
54
|
+
- test/incoming_phone_number_test.rb
|
55
|
+
- test/local_incoming_phone_number_test.rb
|
56
|
+
- test/test_helper.rb
|
57
|
+
- test/toll_free_incoming_phone_number_test.rb
|
58
|
+
- test/twilio_mock.rb
|
59
|
+
- README.md
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://github.com/justinweiss/twilio_resource
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.6
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: An ActiveResource API wrapper for Twilio
|
90
|
+
test_files:
|
91
|
+
- test/account_test.rb
|
92
|
+
- test/call_test.rb
|
93
|
+
- test/incoming_phone_number_test.rb
|
94
|
+
- test/local_incoming_phone_number_test.rb
|
95
|
+
- test/toll_free_incoming_phone_number_test.rb
|