clickatell 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,20 @@
1
+ == 0.6.0
2
+
3
+ * API host can now be customized (John Gibbons)
4
+ * Fixed bug with the ampersands in messages, they are now escaped properly (John Brice)
5
+ * Removed reliance on Object#blank? which was causing some compatibility issues (Greg Bell)
6
+ * Added :callback option support (Greg Bell)
7
+ * Added new test mode (Pivotal Labs)
8
+
1
9
  == 0.5.0
10
+
2
11
  * Added support for mobile originated flag (courtesy Dan Weinand)
3
12
  * Added support for WAP push (experimental, courtesy Zhao Lu)
4
13
  * Updated specs to use Mocha instead of the built-in RSpec mocking
5
14
  * Improved specs in general
6
15
 
7
16
  == 0.4.1
17
+
8
18
  * Custom alphanumeric sender would not always be supported by default unless it was explicitly enabled using the req_feat parameter.
9
19
 
10
20
  == 0.4.0
File without changes
@@ -20,7 +20,18 @@ module Clickatell
20
20
 
21
21
  # Enable secure mode (SSL)
22
22
  attr_accessor :secure_mode
23
+
24
+ # Allow customizing URL
25
+ attr_accessor :api_service_host
26
+
27
+ # Set to true to test message sending; this will not actually send
28
+ # messages but will collect sent messages in a testable collection.
29
+ # (off by default)
30
+ attr_accessor :test_mode
23
31
  end
32
+ self.debug_mode = false
33
+ self.secure_mode = false
34
+ self.test_mode = false
24
35
 
25
36
  # Creates a new API instance using the specified +auth options+.
26
37
  # +auth_options+ is a hash containing either a :session_id or
@@ -61,7 +72,7 @@ module Clickatell
61
72
  #
62
73
  # Returns a new message ID if successful.
63
74
  def send_message(recipient, message_text, opts={})
64
- valid_options = opts.only(:from, :mo)
75
+ valid_options = opts.only(:from, :mo, :callback)
65
76
  valid_options.merge!(:req_feat => '48') if valid_options[:from]
66
77
  valid_options.merge!(:mo => '1') if opts[:set_mobile_originated]
67
78
  response = execute_command('sendmsg', 'http',
@@ -91,10 +102,19 @@ module Clickatell
91
102
  response = execute_command('getbalance', 'http')
92
103
  parse_response(response)['Credit'].to_f
93
104
  end
105
+
106
+ def sms_requests
107
+ @sms_requests ||= []
108
+ end
94
109
 
95
- protected
110
+ protected
96
111
  def execute_command(command_name, service, parameters={}) #:nodoc:
97
- CommandExecutor.new(auth_hash, self.class.secure_mode, self.class.debug_mode).execute(command_name, service, parameters)
112
+ executor = CommandExecutor.new(auth_hash, self.class.secure_mode, self.class.debug_mode, self.class.test_mode)
113
+ result = executor.execute(command_name, service, parameters)
114
+
115
+ (sms_requests << executor.sms_requests).flatten! if self.class.test_mode
116
+
117
+ result
98
118
  end
99
119
 
100
120
  def parse_response(raw_response) #:nodoc:
@@ -123,4 +143,4 @@ end
123
143
 
124
144
  ).each do |lib|
125
145
  require File.join(File.dirname(__FILE__), lib)
126
- end
146
+ end
@@ -1,3 +1,4 @@
1
+ require "cgi"
1
2
  module Clickatell
2
3
  class API
3
4
 
@@ -14,17 +15,17 @@ module Clickatell
14
15
 
15
16
  # Returns a URL for the given parameters (a hash).
16
17
  def with_params(param_hash)
17
- param_string = '?' + param_hash.map { |key, value| "#{key}=#{value}" }.sort.join('&')
18
- return URI.parse(File.join(api_service_uri, @command_name + URI.encode(param_string)))
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))
19
20
  end
20
21
 
21
22
  protected
22
23
  def api_service_uri
23
24
  protocol = @options[:secure] ? 'https' : 'http'
24
- port = @options[:secure] ? 443 : 80
25
- return "#{protocol}://#{API_SERVICE_HOST}:#{port}/#{@service}/"
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}/"
26
27
  end
27
28
  end
28
29
 
29
30
  end
30
- end
31
+ end
@@ -4,12 +4,25 @@ require 'net/https'
4
4
  module Clickatell
5
5
  class API
6
6
 
7
+ class FakeHttpResponse
8
+ def body
9
+ "test"
10
+ end
11
+ end
12
+
7
13
  # Used to run commands agains the Clickatell gateway.
8
14
  class CommandExecutor
9
- def initialize(authentication_hash, secure=false, debug=false)
15
+ def initialize(authentication_hash, secure=false, debug=false, test_mode=false)
10
16
  @authentication_hash = authentication_hash
11
17
  @debug = debug
12
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
13
26
  end
14
27
 
15
28
  # Builds a command object and sends it using HTTP GET.
@@ -29,12 +42,27 @@ module Clickatell
29
42
  end
30
43
 
31
44
  def get_response(uri)
32
- http = Net::HTTP.new(uri.host, uri.port)
33
- http.use_ssl = (uri.scheme == 'https')
34
- http.start do |http|
35
- resp, body = http.get([uri.path, uri.query].join('?'))
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
36
54
  end
37
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
38
66
  end
39
67
 
40
68
  end
@@ -10,6 +10,8 @@ module Clickatell
10
10
 
11
11
  # Returns the HTTP response body data as a hash.
12
12
  def parse(http_response)
13
+ return { 'OK' => 'session_id' } if API.test_mode
14
+
13
15
  if http_response.body.scan(/ERR/).any?
14
16
  raise Clickatell::API::Error.parse(http_response.body)
15
17
  end
@@ -1,7 +1,7 @@
1
1
  module Clickatell #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 5
4
+ MINOR = 6
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
@@ -14,14 +14,36 @@ module Clickatell
14
14
  @command = API::Command.new('cmdname')
15
15
  end
16
16
 
17
+ after do
18
+ Clickatell::API.api_service_host = nil
19
+ end
20
+
17
21
  it "should return encoded URL for the specified command and parameters" do
18
22
  url = @command.with_params(:param_one => 'abc', :param_two => '123')
19
23
  url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc&param_two=123")
20
24
  end
21
25
 
22
26
  it "should URL encode any special characters in parameters" do
23
- url = @command.with_params(:param_one => 'abc', :param_two => 'hello world')
24
- url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc&param_two=hello%20world")
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&param_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&param_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&param_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&param_two=123")
25
47
  end
26
48
  end
27
49
 
@@ -35,7 +57,7 @@ module Clickatell
35
57
  url.should == URI.parse("https://api.clickatell.com/http/cmdname?param_one=abc&param_two=123")
36
58
  end
37
59
  end
38
-
60
+
39
61
  describe "Command executor" do
40
62
  it "should create an API command with the given params" do
41
63
  executor = API::CommandExecutor.new(:session_id => '12345')
@@ -68,9 +90,11 @@ module Clickatell
68
90
  before do
69
91
  API.debug_mode = false
70
92
  API.secure_mode = false
93
+ API.test_mode = false
94
+
71
95
  @executor = mock('command executor')
72
96
  @api = API.new(:session_id => '1234')
73
- API::CommandExecutor.stubs(:new).with({:session_id => '1234'}, false, false).returns(@executor)
97
+ API::CommandExecutor.stubs(:new).with({:session_id => '1234'}, false, false, false).returns(@executor)
74
98
  end
75
99
 
76
100
  it "should use the api_id, username and password to authenticate and return the new session id" do
@@ -91,10 +115,10 @@ module Clickatell
91
115
  it "should support sending messages to a specified number, returning the message id" do
92
116
  @executor.expects(:execute).with('sendmsg', 'http',
93
117
  :to => '4477791234567',
94
- :text => 'hello world'
118
+ :text => 'hello world & goodbye'
95
119
  ).returns(response = stub('response'))
96
120
  Response.stubs(:parse).with(response).returns('ID' => 'message_id')
97
- @api.send_message('4477791234567', 'hello world').should == 'message_id'
121
+ @api.send_message('4477791234567', 'hello world & goodbye').should == 'message_id'
98
122
  end
99
123
 
100
124
  it "should set the :from parameter and set the :req_feat to 48 when using a custom from string when sending a message" do
@@ -109,6 +133,12 @@ module Clickatell
109
133
  @api.send_message('4477791234567', 'hello world', :set_mobile_originated => true)
110
134
  end
111
135
 
136
+ it "should set the callback flag to the number passed in the options hash" do
137
+ @executor.expects(:execute).with('sendmsg', 'http', has_entry(:callback => 1)).returns(response=mock('response'))
138
+ Response.stubs(:parse).with(response).returns('ID' => 'message_id')
139
+ @api.send_message('4477791234567', 'hello world', :callback => 1)
140
+ end
141
+
112
142
  it "should ignore any invalid parameters when sending a message" do
113
143
  @executor.expects(:execute).with('sendmsg', 'http', Not(has_key(:any_old_param))).returns(response = stub('response'))
114
144
  Response.stubs(:parse).returns('ID' => 'foo')
@@ -145,10 +175,8 @@ module Clickatell
145
175
 
146
176
  describe API, ' with no authentication options set' do
147
177
  it "should build commands with no authentication options" do
148
- API.debug_mode = false
149
- API.secure_mode = false
150
178
  api = API.new
151
- API::CommandExecutor.stubs(:new).with({}, false, false).returns(executor = stub('command executor'))
179
+ API::CommandExecutor.stubs(:new).with({}, false, false, false).returns(executor = stub('command executor'))
152
180
  executor.stubs(:execute)
153
181
  api.ping('1234')
154
182
  end
@@ -156,10 +184,9 @@ module Clickatell
156
184
 
157
185
  describe API, ' in secure mode' do
158
186
  it "should execute commands securely" do
159
- API.debug_mode = false
160
187
  API.secure_mode = true
161
188
  api = API.new
162
- API::CommandExecutor.expects(:new).with({}, true, false).returns(executor = stub('command executor'))
189
+ API::CommandExecutor.expects(:new).with({}, true, false, false).returns(executor = stub('command executor'))
163
190
  executor.stubs(:execute)
164
191
  api.ping('1234')
165
192
  end
@@ -174,4 +201,30 @@ module Clickatell
174
201
  end
175
202
  end
176
203
 
204
+ describe API, "#test_mode" do
205
+ before(:each) do
206
+ API.secure_mode = false
207
+ API.test_mode = true
208
+ @api = API.new
209
+ end
210
+
211
+ it "should create a new CommandExecutor with test_mode parameter set to true" do
212
+ API::CommandExecutor.expects(:new).with({}, false, false, true).once.returns(executor = mock('command executor'))
213
+ executor.stubs(:execute)
214
+ executor.stubs(:sms_requests).returns([])
215
+ @api.ping('1234')
216
+ end
217
+
218
+ it "should record all commands" do
219
+ @api.ping('1234')
220
+ @api.sms_requests.should_not be_empty
221
+ end
222
+
223
+ it "should return the recorded commands in a flattened array" do
224
+ @api.ping('1234')
225
+ @api.sms_requests.size.should == 1
226
+ @api.sms_requests.first.should_not be_instance_of(Array)
227
+ end
228
+ end
229
+
177
230
  end
@@ -0,0 +1,75 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + '/../lib/clickatell'
3
+
4
+ module Clickatell
5
+ describe "API::CommandExecutor" do
6
+ it "should have test mode" do
7
+ executor = API::CommandExecutor.new({}, false, false, :test_mode)
8
+ executor.should be_in_test_mode
9
+ end
10
+
11
+ it "should default to not test mode" do
12
+ executor = API::CommandExecutor.new({})
13
+ executor.should_not be_in_test_mode
14
+ end
15
+
16
+ describe "#execute" do
17
+ describe "in non-test mode" do
18
+ before(:each) do
19
+ @executor = API::CommandExecutor.new({})
20
+ end
21
+
22
+ it "should not record requests" do
23
+ @executor.should_not respond_to(:sms_requests)
24
+ end
25
+ end
26
+
27
+ describe "in test mode" do
28
+ before(:each) do
29
+ @params = {:foo => 1, :bar => 2}
30
+ @executor = API::CommandExecutor.new(@params, false, false, :test_mode)
31
+ end
32
+
33
+ it "should not make any network calls" do
34
+ Net::HTTP.expects(:new).never
35
+ @executor.execute("foo", "http")
36
+ end
37
+
38
+ it "should start with an empty request collection" do
39
+ @executor.sms_requests.should be_empty
40
+ end
41
+
42
+ it "should record sms requests" do
43
+ @executor.execute("bar", "http")
44
+ @executor.sms_requests.should_not be_empty
45
+ end
46
+
47
+ it "should record a request for each call" do
48
+ @executor.execute("wibble", "http")
49
+ @executor.sms_requests.size.should == 1
50
+ @executor.execute("foozle", "http")
51
+ @executor.sms_requests.size.should == 2
52
+ end
53
+
54
+ it "should return a response that approximates a true Net::HttpResponse" do
55
+ response = @executor.execute("throat-warbler", "http")
56
+ response.body.should == "test"
57
+ end
58
+
59
+ describe "each recorded request" do
60
+ it "should return the command information" do
61
+ command = "rum_tum_tum"
62
+ @executor.execute(command, "http")
63
+
64
+ uri = @executor.sms_requests.first
65
+ uri.host.should == "api.clickatell.com"
66
+ uri.path.should include(command)
67
+ @params.collect { |k, v| "#{k}=#{v}"}.each do |query_parameter|
68
+ uri.query.should include(query_parameter)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -8,6 +8,10 @@ module Clickatell
8
8
  Clickatell::API::Error.stubs(:parse).returns(Clickatell::API::Error.new('', ''))
9
9
  end
10
10
 
11
+ before(:each) do
12
+ API.test_mode = false
13
+ end
14
+
11
15
  it "should return hash for one-line success response" do
12
16
  Response.parse(stub('response', :body => 'k1: foo k2: bar')).should == {'k1' => 'foo', 'k2' => 'bar'}
13
17
  end
@@ -15,6 +19,16 @@ module Clickatell
15
19
  it "should raise API::Error if response contains an error message" do
16
20
  proc { Response.parse(stub('response', :body => 'ERR: 001, Authentication failed')) }.should raise_error(Clickatell::API::Error)
17
21
  end
22
+
23
+ describe "in test mode" do
24
+ before(:each) do
25
+ API.test_mode = true
26
+ end
27
+
28
+ it "should return something approximating a session_id" do
29
+ Response.parse("pretty much anything").should == { 'OK' => 'session_id' }
30
+ end
31
+ end
18
32
  end
19
33
 
20
34
  end
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'spec'
3
3
  require 'mocha'
4
+ require 'test/unit'
4
5
 
5
6
  Spec::Runner.configure do |config|
6
7
  config.mock_with :mocha
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clickatell
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luke Redpath
@@ -9,74 +9,62 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-19 00:00:00 +01:00
13
- default_executable:
12
+ date: 2009-06-25 00:00:00 +01:00
13
+ default_executable: sms
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: hoe
16
+ name: rspec
17
17
  type: :development
18
18
  version_requirement:
19
19
  version_requirements: !ruby/object:Gem::Requirement
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 1.7.0
23
+ version: "0"
24
24
  version:
25
- description: Ruby interface to the Clickatell SMS gateway service.
26
- email: contact[AT]lukeredpath.co.uk
25
+ description:
26
+ email: luke@lukeredpath.co.uk
27
27
  executables:
28
28
  - sms
29
29
  extensions: []
30
30
 
31
31
  extra_rdoc_files:
32
+ - RDOC_README.txt
32
33
  - History.txt
33
34
  - License.txt
34
- - Manifest.txt
35
- - README.txt
36
- - website/index.txt
37
35
  files:
38
36
  - History.txt
39
37
  - License.txt
40
- - Manifest.txt
41
- - README.txt
38
+ - RDOC_README.txt
42
39
  - README.textile
43
- - Rakefile
44
40
  - bin/sms
45
- - lib/clickatell.rb
46
- - lib/clickatell/api.rb
41
+ - spec/api_spec.rb
42
+ - spec/command_executor_spec.rb
43
+ - spec/hash_ext_spec.rb
44
+ - spec/response_spec.rb
45
+ - spec/spec.opts
46
+ - spec/spec_helper.rb
47
+ - lib/clickatell
48
+ - lib/clickatell/api
47
49
  - lib/clickatell/api/command.rb
48
50
  - lib/clickatell/api/command_executor.rb
49
51
  - lib/clickatell/api/error.rb
50
52
  - lib/clickatell/api/message_status.rb
53
+ - lib/clickatell/api.rb
51
54
  - lib/clickatell/response.rb
52
- - lib/clickatell/utility.rb
55
+ - lib/clickatell/utility
53
56
  - lib/clickatell/utility/options.rb
57
+ - lib/clickatell/utility.rb
54
58
  - lib/clickatell/version.rb
59
+ - lib/clickatell.rb
60
+ - lib/core-ext
55
61
  - lib/core-ext/hash.rb
56
- - scripts/txt2html
57
- - setup.rb
58
- - spec/api_spec.rb
59
- - spec/hash_ext_spec.rb
60
- - spec/response_spec.rb
61
- - spec/spec.opts
62
- - spec/spec_helper.rb
63
- - website/images/footer_bg.gif
64
- - website/index.html
65
- - website/index.txt
66
- - website/javascripts/codehighlighter/code_highlighter.js
67
- - website/javascripts/codehighlighter/ruby.js
68
- - website/javascripts/rounded_corners_lite.inc.js
69
- - website/specs.html
70
- - website/stylesheets/limechoc.css
71
- - website/stylesheets/rdoc.css
72
- - website/stylesheets/screen.css
73
- - website/template.rhtml
74
62
  has_rdoc: true
75
63
  homepage: http://clickatell.rubyforge.org
76
64
  post_install_message:
77
65
  rdoc_options:
78
66
  - --main
79
- - README.txt
67
+ - RDOC_README.txt
80
68
  require_paths:
81
69
  - lib
82
70
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -94,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
82
  requirements: []
95
83
 
96
84
  rubyforge_project: clickatell
97
- rubygems_version: 1.2.0
85
+ rubygems_version: 1.3.1
98
86
  signing_key:
99
87
  specification_version: 2
100
88
  summary: Ruby interface to the Clickatell SMS gateway service.