clickatell-ruby19 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/History.txt +55 -0
- data/License.txt +20 -0
- data/RDOC_README.txt +33 -0
- data/README.textile +40 -0
- data/Rakefile +95 -0
- data/bin/sms +62 -0
- data/clickatell.gemspec +82 -0
- data/lib/clickatell.rb +10 -0
- data/lib/clickatell/api.rb +153 -0
- data/lib/clickatell/api/command.rb +31 -0
- data/lib/clickatell/api/command_executor.rb +69 -0
- data/lib/clickatell/api/error.rb +25 -0
- data/lib/clickatell/api/message_status.rb +26 -0
- data/lib/clickatell/response.rb +32 -0
- data/lib/clickatell/utility.rb +6 -0
- data/lib/clickatell/utility/options.rb +109 -0
- data/lib/clickatell/version.rb +13 -0
- data/lib/core-ext/hash.rb +15 -0
- data/scripts/txt2html +67 -0
- data/spec/api_spec.rb +239 -0
- data/spec/cli_options_test.rb +26 -0
- data/spec/command_executor_spec.rb +75 -0
- data/spec/hash_ext_spec.rb +14 -0
- data/spec/response_spec.rb +48 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +8 -0
- metadata +111 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
require "cgi"
|
2
|
+
module Clickatell
|
3
|
+
class API
|
4
|
+
|
5
|
+
# Represents a Clickatell HTTP gateway command in the form
|
6
|
+
# of a complete URL (the raw, low-level request).
|
7
|
+
class Command
|
8
|
+
API_SERVICE_HOST = 'api.clickatell.com'
|
9
|
+
|
10
|
+
def initialize(command_name, service = 'http', opts={})
|
11
|
+
@command_name = command_name
|
12
|
+
@service = service
|
13
|
+
@options = { :secure => false }.merge(opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns a URL for the given parameters (a hash).
|
17
|
+
def with_params(param_hash)
|
18
|
+
param_string = '?' + param_hash.map { |key, value| "#{::CGI.escape(key.to_s)}=#{::CGI.escape(value.to_s)}" }.sort.join('&')
|
19
|
+
return URI.parse(File.join(api_service_uri, @command_name + param_string))
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
def api_service_uri
|
24
|
+
protocol = @options[:secure] ? 'https' : 'http'
|
25
|
+
api_service_host = ((Clickatell::API.api_service_host.nil? || Clickatell::API.api_service_host.empty?) ? API_SERVICE_HOST : Clickatell::API.api_service_host)
|
26
|
+
return "#{protocol}://#{api_service_host}/#{@service}/"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module Clickatell
|
5
|
+
class API
|
6
|
+
|
7
|
+
class FakeHttpResponse
|
8
|
+
def body
|
9
|
+
"test"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Used to run commands agains the Clickatell gateway.
|
14
|
+
class CommandExecutor
|
15
|
+
def initialize(authentication_hash, secure=false, debug=false, test_mode=false)
|
16
|
+
@authentication_hash = authentication_hash
|
17
|
+
@debug = debug
|
18
|
+
@secure = secure
|
19
|
+
@test_mode = test_mode
|
20
|
+
|
21
|
+
allow_request_recording if @test_mode
|
22
|
+
end
|
23
|
+
|
24
|
+
def in_test_mode?
|
25
|
+
@test_mode
|
26
|
+
end
|
27
|
+
|
28
|
+
# Builds a command object and sends it using HTTP GET.
|
29
|
+
# Will output URLs as they are requested to stdout when
|
30
|
+
# debugging is enabled.
|
31
|
+
def execute(command_name, service, parameters={})
|
32
|
+
request_uri = command(command_name, service, parameters)
|
33
|
+
puts "[debug] Sending request to #{request_uri}" if @debug
|
34
|
+
get_response(request_uri)
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def command(command_name, service, parameters) #:nodoc:
|
39
|
+
Command.new(command_name, service, :secure => @secure).with_params(
|
40
|
+
parameters.merge(@authentication_hash)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_response(uri)
|
45
|
+
if in_test_mode?
|
46
|
+
sms_requests << uri
|
47
|
+
[FakeHttpResponse.new]
|
48
|
+
else
|
49
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
50
|
+
http.use_ssl = (uri.scheme == 'https')
|
51
|
+
http.start do |http|
|
52
|
+
resp, body = http.get([uri.path, uri.query].join('?'))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def allow_request_recording
|
60
|
+
class << self
|
61
|
+
define_method :sms_requests do
|
62
|
+
@sms_requests ||= []
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Clickatell
|
2
|
+
class API
|
3
|
+
|
4
|
+
# Clickatell API Error exception.
|
5
|
+
class Error < StandardError
|
6
|
+
attr_reader :code, :message
|
7
|
+
|
8
|
+
def initialize(code, message)
|
9
|
+
@code, @message = code, message
|
10
|
+
end
|
11
|
+
|
12
|
+
# Creates a new Error from a Clickatell HTTP response string
|
13
|
+
# e.g.:
|
14
|
+
#
|
15
|
+
# Error.parse("ERR: 001, Authentication error")
|
16
|
+
# # => #<Clickatell::API::Error code='001' message='Authentication error'>
|
17
|
+
def self.parse(error_string)
|
18
|
+
error_details = error_string.split(':').last.strip
|
19
|
+
code, message = error_details.split(',').map { |s| s.strip }
|
20
|
+
self.new(code, message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Clickatell
|
2
|
+
class API
|
3
|
+
|
4
|
+
class MessageStatus
|
5
|
+
STATUS_MAP = {
|
6
|
+
1 => 'Message unknown',
|
7
|
+
2 => 'Message queued',
|
8
|
+
3 => 'Delivered to gateway',
|
9
|
+
4 => 'Received by recipient',
|
10
|
+
5 => 'Error with message',
|
11
|
+
6 => 'User cancelled messaged delivery',
|
12
|
+
7 => 'Error delivering message',
|
13
|
+
8 => 'OK',
|
14
|
+
9 => 'Routing error',
|
15
|
+
10 => 'Message expired',
|
16
|
+
11 => 'Message queued for later delivery',
|
17
|
+
12 => 'Out of credit'
|
18
|
+
}
|
19
|
+
|
20
|
+
def self.[](code)
|
21
|
+
STATUS_MAP[code]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Clickatell
|
4
|
+
|
5
|
+
# Used to parse HTTP responses returned from Clickatell API calls.
|
6
|
+
class Response
|
7
|
+
|
8
|
+
class << self
|
9
|
+
PARSE_REGEX = /[A-Za-z0-9]+:.*?(?:(?=[A-Za-z0-9]+:)|$)/
|
10
|
+
|
11
|
+
# Returns the HTTP response body data as a hash.
|
12
|
+
def parse(http_response)
|
13
|
+
return { 'OK' => 'session_id' } if API.test_mode
|
14
|
+
|
15
|
+
if http_response.body.scan(/ERR/).any?
|
16
|
+
raise Clickatell::API::Error.parse(http_response.body)
|
17
|
+
end
|
18
|
+
results = http_response.body.split("\n").map do |line|
|
19
|
+
# YAML.load converts integer strings that have leading zeroes into integers
|
20
|
+
# using octal rather than decimal. This isn't what we want, so we'll strip out any
|
21
|
+
# leading zeroes in numbers here.
|
22
|
+
response_fields = line.scan(PARSE_REGEX)
|
23
|
+
response_fields = response_fields.collect { |field| field.gsub(/\b0+(\d+)\b/, '\1') }
|
24
|
+
YAML.load(response_fields.join("\n"))
|
25
|
+
end
|
26
|
+
results.length == 1 ? results.first : results
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Clickatell
|
5
|
+
module Utility
|
6
|
+
class Options #:nodoc:
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def parse(args)
|
10
|
+
@options = self.default_options
|
11
|
+
parser = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: sms [options] recipient(s) message"
|
13
|
+
opts.separator " Recipients can be a comma-separated list, up to 100 max."
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "Specific options:"
|
16
|
+
|
17
|
+
opts.on('-u', '--username USERNAME',
|
18
|
+
"Specify the clickatell username (overrides ~/.clickatell setting)") do |username|
|
19
|
+
@options.username = username
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on('-p', '--password PASSWORD',
|
23
|
+
"Specify the clickatell password (overrides ~/.clickatell setting)") do |password|
|
24
|
+
@options.password = password
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-k', '--apikey API_KEY',
|
28
|
+
"Specify the clickatell API key (overrides ~/.clickatell setting)") do |key|
|
29
|
+
@options.api_key = key
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-f', '--from NAME_OR_NUMBER',
|
33
|
+
"Specify the name or number that the SMS will appear from") do |from|
|
34
|
+
@options.from = from
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-b', '--show-balance',
|
38
|
+
"Shows the total number of credits remaining on your account") do
|
39
|
+
@options.show_balance = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-s', '--status MESSAGE_ID',
|
43
|
+
"Displays the status of the specified message.") do |message_id|
|
44
|
+
@options.message_id = message_id
|
45
|
+
@options.show_status = true
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on('-S', '--secure',
|
49
|
+
"Sends request using HTTPS") do
|
50
|
+
Clickatell::API.secure_mode = true
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on('-d', '--debug') do
|
54
|
+
Clickatell::API.debug_mode = true
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on_tail('-h', '--help', "Show this message") do
|
58
|
+
puts opts
|
59
|
+
exit
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on_tail('-v', '--version') do
|
63
|
+
puts "Ruby Clickatell SMS Utility #{Clickatell::VERSION}"
|
64
|
+
exit
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
@options.recipient = args[-2].split(',') rescue nil
|
69
|
+
@options.message = args[-1]
|
70
|
+
|
71
|
+
parser.parse!(args)
|
72
|
+
|
73
|
+
if (@options.message.nil? || @options.recipient.nil?) && send_message?
|
74
|
+
puts "You must specify a recipient and message!"
|
75
|
+
puts parser
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
|
79
|
+
return @options
|
80
|
+
|
81
|
+
rescue OptionParser::MissingArgument => e
|
82
|
+
switch_given = e.message.split(':').last.strip
|
83
|
+
puts "The #{switch_given} option requires an argument."
|
84
|
+
puts parser
|
85
|
+
exit
|
86
|
+
end
|
87
|
+
|
88
|
+
def default_options
|
89
|
+
options = OpenStruct.new
|
90
|
+
config_file = File.open(File.join(ENV['HOME'], '.clickatell'))
|
91
|
+
config = YAML.load(config_file)
|
92
|
+
options.username = config['username']
|
93
|
+
options.password = config['password']
|
94
|
+
options.api_key = config['api_key']
|
95
|
+
options.from = config['from']
|
96
|
+
return options
|
97
|
+
rescue Errno::ENOENT
|
98
|
+
return options
|
99
|
+
end
|
100
|
+
|
101
|
+
def send_message?
|
102
|
+
(@options.show_status.nil? &&
|
103
|
+
@options.show_balance.nil?)
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns a new hash containing only the keys specified
|
3
|
+
# that exist in the current hash.
|
4
|
+
#
|
5
|
+
# {:a => '1', :b => '2', :c => '3'}.only(:a, :c)
|
6
|
+
# # => {:a => '1', :c => '3'}
|
7
|
+
#
|
8
|
+
# Keys that do not exist in the original hash are ignored.
|
9
|
+
def only(*keys)
|
10
|
+
inject( {} ) do |new_hash, (key, value)|
|
11
|
+
new_hash[key] = value if keys.include?(key)
|
12
|
+
new_hash
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/scripts/txt2html
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'redcloth'
|
5
|
+
require 'syntax/convertors/html'
|
6
|
+
require 'erb'
|
7
|
+
require File.dirname(__FILE__) + '/../lib/clickatell/version.rb'
|
8
|
+
|
9
|
+
version = Clickatell::VERSION::STRING
|
10
|
+
download = 'http://rubyforge.org/projects/clickatell'
|
11
|
+
|
12
|
+
class Fixnum
|
13
|
+
def ordinal
|
14
|
+
# teens
|
15
|
+
return 'th' if (10..19).include?(self % 100)
|
16
|
+
# others
|
17
|
+
case self % 10
|
18
|
+
when 1: return 'st'
|
19
|
+
when 2: return 'nd'
|
20
|
+
when 3: return 'rd'
|
21
|
+
else return 'th'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Time
|
27
|
+
def pretty
|
28
|
+
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def convert_syntax(syntax, source)
|
33
|
+
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
34
|
+
end
|
35
|
+
|
36
|
+
if ARGV.length >= 1
|
37
|
+
src, template = ARGV
|
38
|
+
template ||= File.dirname(__FILE__) + '/../website/template.rhtml'
|
39
|
+
|
40
|
+
else
|
41
|
+
puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
|
42
|
+
exit!
|
43
|
+
end
|
44
|
+
|
45
|
+
template = ERB.new(File.open(template).read)
|
46
|
+
|
47
|
+
title = nil
|
48
|
+
body = nil
|
49
|
+
File.open(src) do |fsrc|
|
50
|
+
title_text = fsrc.readline
|
51
|
+
body_text = fsrc.read
|
52
|
+
syntax_items = []
|
53
|
+
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</>!m){
|
54
|
+
ident = syntax_items.length
|
55
|
+
element, syntax, source = $1, $2, $3
|
56
|
+
syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
|
57
|
+
"syntax-temp-#{ident}"
|
58
|
+
}
|
59
|
+
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
60
|
+
body = RedCloth.new(body_text).to_html
|
61
|
+
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
|
62
|
+
end
|
63
|
+
stat = File.stat(src)
|
64
|
+
created = stat.ctime
|
65
|
+
modified = stat.mtime
|
66
|
+
|
67
|
+
$stdout << template.result(binding)
|
data/spec/api_spec.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/clickatell'
|
3
|
+
|
4
|
+
class FakeHttp
|
5
|
+
def start(&block)
|
6
|
+
yield self
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Clickatell
|
11
|
+
|
12
|
+
describe "API Command" do
|
13
|
+
before do
|
14
|
+
@command = API::Command.new('cmdname')
|
15
|
+
end
|
16
|
+
|
17
|
+
after do
|
18
|
+
Clickatell::API.api_service_host = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return encoded URL for the specified command and parameters" do
|
22
|
+
url = @command.with_params(:param_one => 'abc', :param_two => '123')
|
23
|
+
url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc¶m_two=123")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should URL encode any special characters in parameters" do
|
27
|
+
url = @command.with_params(:param_one => 'abc', :param_two => 'hello world & goodbye cruel world <grin>')
|
28
|
+
url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc¶m_two=hello+world+%26+goodbye+cruel+world+%3Cgrin%3E")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should use a custom host when constructing command URLs if specified" do
|
32
|
+
Clickatell::API.api_service_host = 'api.clickatell-custom.co.uk'
|
33
|
+
url = @command.with_params(:param_one => 'abc', :param_two => '123')
|
34
|
+
url.should == URI.parse("http://api.clickatell-custom.co.uk/http/cmdname?param_one=abc¶m_two=123")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should use the default host if specified custom host is nil" do
|
38
|
+
Clickatell::API.api_service_host = nil
|
39
|
+
url = @command.with_params(:param_one => 'abc', :param_two => '123')
|
40
|
+
url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc¶m_two=123")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should use the default host if specified custom host is an empty string" do
|
44
|
+
Clickatell::API.api_service_host = ''
|
45
|
+
url = @command.with_params(:param_one => 'abc', :param_two => '123')
|
46
|
+
url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc¶m_two=123")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "Secure API Command" do
|
51
|
+
before do
|
52
|
+
@command = API::Command.new('cmdname', 'http', :secure => true)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should use HTTPS" do
|
56
|
+
url = @command.with_params(:param_one => 'abc', :param_two => '123')
|
57
|
+
url.should == URI.parse("https://api.clickatell.com/http/cmdname?param_one=abc¶m_two=123")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "Command executor" do
|
62
|
+
it "should create an API command with the given params" do
|
63
|
+
executor = API::CommandExecutor.new(:session_id => '12345')
|
64
|
+
executor.stubs(:get_response).returns([])
|
65
|
+
API::Command.expects(:new).with('cmdname', 'http', :secure => false).returns(command = stub('Command'))
|
66
|
+
command.expects(:with_params).with(:param_one => 'foo', :session_id => '12345').returns(stub_everything('URI'))
|
67
|
+
executor.execute('cmdname', 'http', :param_one => 'foo')
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should send the URI generated by the created command via HTTP get and return the response" do
|
71
|
+
executor = API::CommandExecutor.new(:session_id => '12345')
|
72
|
+
command_uri = URI.parse('http://clickatell.com:8080/path?foo=bar')
|
73
|
+
API::Command.stubs(:new).returns(command = stub('Command', :with_params => command_uri))
|
74
|
+
Net::HTTP.stubs(:new).with(command_uri.host, command_uri.port).returns(http = FakeHttp.new)
|
75
|
+
http.stubs(:use_ssl=)
|
76
|
+
http.stubs(:get).with('/path?foo=bar').returns([response = stub('HTTP Response'), 'response body'])
|
77
|
+
executor.execute('cmdname', 'http').should == response
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should send the command over a secure HTTPS connection if :secure option is set to true" do
|
81
|
+
executor = API::CommandExecutor.new({:session_id => '12345'}, secure = true)
|
82
|
+
Net::HTTP.stubs(:new).returns(http = mock('HTTP'))
|
83
|
+
http.expects(:use_ssl=).with(true)
|
84
|
+
http.stubs(:start).returns([])
|
85
|
+
executor.execute('cmdname', 'http')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "API" do
|
90
|
+
before do
|
91
|
+
API.debug_mode = false
|
92
|
+
API.secure_mode = false
|
93
|
+
API.test_mode = false
|
94
|
+
|
95
|
+
@executor = mock('command executor')
|
96
|
+
@api = API.new(:session_id => '1234')
|
97
|
+
API::CommandExecutor.stubs(:new).with({:session_id => '1234'}, false, false, false).returns(@executor)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should use the api_id, username and password to authenticate and return the new session id" do
|
101
|
+
@executor.expects(:execute).with('auth', 'http',
|
102
|
+
:api_id => '1234',
|
103
|
+
:user => 'joebloggs',
|
104
|
+
:password => 'superpass'
|
105
|
+
).returns(response = stub('response'))
|
106
|
+
Response.stubs(:parse).with(response).returns('OK' => 'new_session_id')
|
107
|
+
@api.authenticate('1234', 'joebloggs', 'superpass').should == 'new_session_id'
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should support ping, using the current session_id" do
|
111
|
+
@executor.expects(:execute).with('ping', 'http', :session_id => 'abcdefg').returns(response = stub('response'))
|
112
|
+
@api.ping('abcdefg').should == response
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should support sending messages to a specified number, returning the message id" do
|
116
|
+
@executor.expects(:execute).with('sendmsg', 'http',
|
117
|
+
:to => '4477791234567',
|
118
|
+
:text => 'hello world & goodbye'
|
119
|
+
).returns(response = stub('response'))
|
120
|
+
Response.stubs(:parse).with(response).returns('ID' => 'message_id')
|
121
|
+
@api.send_message('4477791234567', 'hello world & goodbye').should == 'message_id'
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should support sending messages to a multiple numbers, returning the message ids" do
|
125
|
+
@executor.expects(:execute).with('sendmsg', 'http',
|
126
|
+
:to => '4477791234567,447779999999',
|
127
|
+
:text => 'hello world & goodbye'
|
128
|
+
).returns(response = stub('response'))
|
129
|
+
Response.stubs(:parse).with(response).returns([{'ID' => 'message_1_id'}, {'ID' => 'message_2_id'}])
|
130
|
+
@api.send_message(['4477791234567', '447779999999'], 'hello world & goodbye').should == ['message_1_id', 'message_2_id']
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should set the :from parameter and set the :req_feat to 48 when using a custom from string when sending a message" do
|
134
|
+
@executor.expects(:execute).with('sendmsg', 'http', has_entries(:from => 'LUKE', :req_feat => '48')).returns(response = stub('response'))
|
135
|
+
Response.stubs(:parse).with(response).returns('ID' => 'message_id')
|
136
|
+
@api.send_message('4477791234567', 'hello world', :from => 'LUKE')
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should set the :mo flag to 1 when :set_mobile_originated is true when sending a message" do
|
140
|
+
@executor.expects(:execute).with('sendmsg', 'http', has_entry(:mo => '1')).returns(response=mock('response'))
|
141
|
+
Response.stubs(:parse).with(response).returns('ID' => 'message_id')
|
142
|
+
@api.send_message('4477791234567', 'hello world', :set_mobile_originated => true)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should set the callback flag to the number passed in the options hash" do
|
146
|
+
@executor.expects(:execute).with('sendmsg', 'http', has_entry(:callback => 1)).returns(response=mock('response'))
|
147
|
+
Response.stubs(:parse).with(response).returns('ID' => 'message_id')
|
148
|
+
@api.send_message('4477791234567', 'hello world', :callback => 1)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should ignore any invalid parameters when sending a message" do
|
152
|
+
@executor.expects(:execute).with('sendmsg', 'http', Not(has_key(:any_old_param))).returns(response = stub('response'))
|
153
|
+
Response.stubs(:parse).returns('ID' => 'foo')
|
154
|
+
@api.send_message('4477791234567', 'hello world', :from => 'LUKE', :any_old_param => 'test')
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should support message status query for a given message id, returning the message status" do
|
158
|
+
@executor.expects(:execute).with('querymsg', 'http', :apimsgid => 'messageid').returns(response = stub('response'))
|
159
|
+
Response.expects(:parse).with(response).returns('ID' => 'message_id', 'Status' => 'message_status')
|
160
|
+
@api.message_status('messageid').should == 'message_status'
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should support balance query, returning number of credits as a float" do
|
164
|
+
@executor.expects(:execute).with('getbalance', 'http', {}).returns(response=mock('response'))
|
165
|
+
Response.stubs(:parse).with(response).returns('Credit' => '10.0')
|
166
|
+
@api.account_balance.should == 10.0
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should raise an API::Error if the response parser raises" do
|
170
|
+
@executor.stubs(:execute)
|
171
|
+
Response.stubs(:parse).raises(Clickatell::API::Error.new('', ''))
|
172
|
+
proc { @api.account_balance }.should raise_error(Clickatell::API::Error)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe API, ' when authenticating' do
|
177
|
+
it "should authenticate to retrieve a session_id and return a new API instance using that session id" do
|
178
|
+
API.stubs(:new).returns(api = mock('api'))
|
179
|
+
api.stubs(:authenticate).with('my_api_key', 'joebloggs', 'mypassword').returns('new_session_id')
|
180
|
+
api.expects(:auth_options=).with(:session_id => 'new_session_id')
|
181
|
+
API.authenticate('my_api_key', 'joebloggs', 'mypassword')
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe API, ' with no authentication options set' do
|
186
|
+
it "should build commands with no authentication options" do
|
187
|
+
api = API.new
|
188
|
+
API::CommandExecutor.stubs(:new).with({}, false, false, false).returns(executor = stub('command executor'))
|
189
|
+
executor.stubs(:execute)
|
190
|
+
api.ping('1234')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe API, ' in secure mode' do
|
195
|
+
it "should execute commands securely" do
|
196
|
+
API.secure_mode = true
|
197
|
+
api = API.new
|
198
|
+
API::CommandExecutor.expects(:new).with({}, true, false, false).returns(executor = stub('command executor'))
|
199
|
+
executor.stubs(:execute)
|
200
|
+
api.ping('1234')
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "API Error" do
|
205
|
+
it "should parse http response string to create error" do
|
206
|
+
response_string = "ERR: 001, Authentication error"
|
207
|
+
error = Clickatell::API::Error.parse(response_string)
|
208
|
+
error.code.should == '001'
|
209
|
+
error.message.should == 'Authentication error'
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe API, "#test_mode" do
|
214
|
+
before(:each) do
|
215
|
+
API.secure_mode = false
|
216
|
+
API.test_mode = true
|
217
|
+
@api = API.new
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should create a new CommandExecutor with test_mode parameter set to true" do
|
221
|
+
API::CommandExecutor.expects(:new).with({}, false, false, true).once.returns(executor = mock('command executor'))
|
222
|
+
executor.stubs(:execute)
|
223
|
+
executor.stubs(:sms_requests).returns([])
|
224
|
+
@api.ping('1234')
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should record all commands" do
|
228
|
+
@api.ping('1234')
|
229
|
+
@api.sms_requests.should_not be_empty
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should return the recorded commands in a flattened array" do
|
233
|
+
@api.ping('1234')
|
234
|
+
@api.sms_requests.size.should == 1
|
235
|
+
@api.sms_requests.first.should_not be_instance_of(Array)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|