clickatell 0.5.0 → 0.6.0

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.
@@ -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.