clickatell 0.1.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.
@@ -0,0 +1,98 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + '/../lib/clickatell'
3
+
4
+ module Clickatell
5
+
6
+ describe "API Command" do
7
+ it "should return encoded URL for the specified command and parameters" do
8
+ command = API::Command.new('cmdname')
9
+ url = command.with_params(:param_one => 'abc', :param_two => '123')
10
+ url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc&param_two=123")
11
+ end
12
+
13
+ it "should URL encode any special characters in parameters" do
14
+ command = API::Command.new('cmdname')
15
+ url = command.with_params(:param_one => 'abc', :param_two => 'hello world')
16
+ url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc&param_two=hello%20world")
17
+ end
18
+
19
+ it "should support non-secure api commands" do
20
+ command = API::Command.new('cmdname', :secure => true)
21
+ url = command.with_params(:param_one => 'abc', :param_two => '123')
22
+ url.should == URI.parse("https://api.clickatell.com/http/cmdname?param_one=abc&param_two=123")
23
+ end
24
+ end
25
+
26
+ describe "Command executor" do
27
+ it "should create an API command and send it via HTTP get" do
28
+ API::Command.should_receive(:new).with('cmdname').and_return(cmd=mock('command'))
29
+ cmd.should_receive(:with_params).with(:param_one => 'foo').and_return(uri=mock('uri'))
30
+ Net::HTTP.should_receive(:get_response).with(uri).and_return(raw_response=mock('http response'))
31
+ API.send(:execute_command, 'cmdname', :param_one => 'foo').should == raw_response
32
+ end
33
+ end
34
+
35
+ describe "API" do
36
+ it "should return session_id for successful authentication" do
37
+ API.should_receive(:execute_command).with('auth',
38
+ :api_id => '1234',
39
+ :user => 'joebloggs',
40
+ :password => 'superpass'
41
+ ).and_return(response=mock('response'))
42
+ Response.should_receive(:parse).with(response).and_return('OK' => 'new_session_id')
43
+ API.authenticate('1234', 'joebloggs', 'superpass').should == 'new_session_id'
44
+ end
45
+
46
+ it "should support ping" do
47
+ API.should_receive(:execute_command).with('ping', :session_id => 'abcdefg').and_return(response=mock('response'))
48
+ API.ping('abcdefg').should == response
49
+ end
50
+
51
+ it "should support sending messages with authentication, returning the message id" do
52
+ API.should_receive(:execute_command).with('sendmsg',
53
+ :api_id => '1234',
54
+ :user => 'joebloggs',
55
+ :password => 'superpass',
56
+ :to => '4477791234567',
57
+ :text => 'hello world'
58
+ ).and_return(response=mock('response'))
59
+ Response.should_receive(:parse).with(response).and_return('ID' => 'message_id')
60
+ API.send_message('4477791234567', 'hello world',
61
+ :username => 'joebloggs', :password => 'superpass', :api_key => '1234'
62
+ ).should == 'message_id'
63
+ end
64
+
65
+ it "should support sending messages with pre-auth, returning the message id" do
66
+ API.should_receive(:execute_command).with('sendmsg',
67
+ :session_id => 'abcde',
68
+ :to => '4477791234567',
69
+ :text => 'hello world'
70
+ ).and_return(response=mock('response'))
71
+ Response.should_receive(:parse).with(response).and_return('ID' => 'message_id')
72
+ API.send_message('4477791234567', 'hello world', :session_id => 'abcde').should == 'message_id'
73
+ end
74
+
75
+ it "should support message status query with authentication, returning message status" do
76
+ API.should_receive(:execute_command).with('querymsg',
77
+ :api_id => '1234',
78
+ :user => 'joebloggs',
79
+ :password => 'superpass',
80
+ :apimsgid => 'messageid'
81
+ ).and_return(response=mock('response'))
82
+ Response.should_receive(:parse).with(response).and_return('ID' => 'message_id', 'Status' => 'message_status')
83
+ API.message_status('messageid',
84
+ :username => 'joebloggs', :password => 'superpass', :api_key => '1234'
85
+ ).should == 'message_status'
86
+ end
87
+
88
+ it "should support message status query with pre-auth" do
89
+ API.should_receive(:execute_command).with('querymsg',
90
+ :session_id => 'abcde',
91
+ :apimsgid => 'messageid'
92
+ ).and_return(response=mock('response'))
93
+ Response.should_receive(:parse).with(response).and_return('ID' => 'message_id', 'Status' => 'message_status')
94
+ API.message_status('messageid', :session_id => 'abcde').should == 'message_status'
95
+ end
96
+ end
97
+
98
+ end
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + '/../lib/clickatell'
3
+
4
+ module Clickatell
5
+
6
+ describe Connection, ' when unauthenticated' do
7
+ it "should authenticate and store session_id before sending command" do
8
+ connection = Connection.new('my_api_key', 'myusername', 'mypassword')
9
+ API.should_receive(:authenticate).with('my_api_key', 'myusername', 'mypassword').and_return('new_session_id')
10
+ API.should_receive(:send_message).with('4477791234567', 'hello world', :session_id => 'new_session_id')
11
+ connection.send_message('4477791234567', 'hello world')
12
+ end
13
+ end
14
+
15
+ describe Connection, ' when authenticated' do
16
+ it "should send command with session_id without re-authenticating" do
17
+ connection = Connection.new('my_api_key', 'myusername', 'mypassword')
18
+ connection.stub!(:session_id).and_return('session_id')
19
+ API.should_receive(:authenticate).never
20
+ API.should_receive(:send_message).with('4477791234567', 'hello world', :session_id => 'session_id')
21
+ connection.send_message('4477791234567', 'hello world')
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + '/../lib/clickatell'
3
+
4
+ module Clickatell
5
+
6
+ describe "Response parser" do
7
+ it "should return hash for one-line success response" do
8
+ raw_response = stub('response')
9
+ raw_response.stub!(:body).and_return('k1: foo k2: bar')
10
+ Response.parse(raw_response).should == {'k1' => 'foo', 'k2' => 'bar'}
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,2 @@
1
+ require 'rubygems'
2
+ require 'spec'
@@ -0,0 +1,111 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
6
+ <link rel="stylesheet" href="stylesheets/limechoc.css" type="text/css" media="screen" />
7
+ <script type="text/javascript" charset="utf-8" src="javascripts/codehighlighter/code_highlighter.js"></script>
8
+ <script type="text/javascript" charset="utf-8" src="javascripts/codehighlighter/ruby.js"></script>
9
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
10
+ <title>Clickatell Ruby API</title>
11
+ </head>
12
+ <body>
13
+
14
+ <div id="wrapper">
15
+ <div id="container">
16
+
17
+ <div id="header">
18
+
19
+ <h1>Clickatell Ruby API 0.1.0
20
+ <span class="tagline">gem install clickatell</span></h1>
21
+
22
+ </div>
23
+
24
+ <div id="content">
25
+ <div class="article"><h3>What is it?</h3>
26
+
27
+
28
+ <p>A Ruby interface to the <a href="http://www.clickatell.com">Clickatell</a> SMS gateway <span class="caps">API</span>.</p>
29
+
30
+
31
+ <h3>Installing</h3>
32
+
33
+
34
+ <p>Download the <a href="http://rubyforge.org/projects/clickatell">latest version of gem</a> or install using RubyGems</p>
35
+
36
+ <pre><code>$ sudo gem install clickatell</code></pre>
37
+
38
+ <h3>The basics</h3>
39
+
40
+
41
+ <p>To use this gem, you will need sign up for an account at <a href="http://www.clickatell.com">the Clickatell website</a>.
42
+ Once you are registered and logged into your account centre, you should add
43
+ an <span class="caps">HTTP API</span> connection to your account. This will give you your <span class="caps">API</span>_ID.</p>
44
+
45
+
46
+ <h3>Demonstration of usage</h3>
47
+
48
+
49
+ <p>You can now use the library directly. You will need your <span class="caps">API</span>_ID as well as your
50
+ account username and password.</p>
51
+
52
+
53
+ <pre><code class="ruby">
54
+ require 'rubygems'
55
+ require 'clickatell'
56
+
57
+ connection = Clickatell::Connection.new('your_api_id', 'your_username', 'your_password')
58
+ connection.send_message('447771234567', 'Hello from clickatell')
59
+ </code></pre>
60
+
61
+ <h4>Command-line <span class="caps">SMS</span> Utility</h4>
62
+
63
+
64
+ <p>The Clickatell gem also comes with a command-line utility that will allow you
65
+ to send an <span class="caps">SMS</span> directly from the command-line.</p>
66
+
67
+
68
+ <p>You will need to create a <span class="caps">YAML</span> configuration file in your home directory, in a
69
+ file called .clickatell that resembles the following:</p>
70
+
71
+
72
+ <pre><code>
73
+ # ~/.clickatell
74
+ api_key: your_api_id
75
+ username: your_username
76
+ password: your_password
77
+ </code></pre>
78
+
79
+ <p>You can then use the sms utility to send a message to a single recipient:</p>
80
+
81
+
82
+ <pre><code>
83
+ $ sms 447771234567 'Hello from clickatell'
84
+ </code></pre>
85
+
86
+ <h3>License</h3>
87
+
88
+
89
+ <p>This code is free to use under the terms of the <span class="caps">MIT</span> license.</p>
90
+
91
+
92
+ <h3>Contact</h3>
93
+
94
+
95
+ <p>Comments are welcome. Send an email to <a href="mailto:contact@lukeredpath.co.uk">Luke Redpath</a> email.</p></div>
96
+ </div>
97
+
98
+ </div>
99
+
100
+ <div id="footer_wrapper">
101
+ <div id="footer">
102
+ <p class="copyright">
103
+ <a href="http://rubyforge.org/projects/clickatell">Rubyforge Project Page</a> |
104
+ <a href="http://rubyforge.org/projects/clickatell">Download latest version (0.1.0)</a>
105
+ </p>
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ </body>
111
+ </html>
@@ -0,0 +1,59 @@
1
+ h1. Clickatell Ruby API
2
+
3
+ h3. What is it?
4
+
5
+ A Ruby interface to the "Clickatell":http://www.clickatell.com SMS gateway API.
6
+
7
+ h3. Installing
8
+
9
+ <p>Download the <a href="http://rubyforge.org/projects/clickatell">latest version of gem</a> or install using RubyGems</p>
10
+
11
+ <pre><code>$ sudo gem install clickatell</code></pre>
12
+
13
+ h3. The basics
14
+
15
+ To use this gem, you will need sign up for an account at "the Clickatell website":http://www.clickatell.com.
16
+ Once you are registered and logged into your account centre, you should add
17
+ an HTTP API connection to your account. This will give you your API_ID.
18
+
19
+ h3. Demonstration of usage
20
+
21
+ You can now use the library directly. You will need your API_ID as well as your
22
+ account username and password.
23
+
24
+ <pre><code class="ruby">
25
+ require 'rubygems'
26
+ require 'clickatell'
27
+
28
+ connection = Clickatell::Connection.new('your_api_id', 'your_username', 'your_password')
29
+ connection.send_message('447771234567', 'Hello from clickatell')
30
+ </code></pre>
31
+
32
+ h4. Command-line SMS Utility
33
+
34
+ The Clickatell gem also comes with a command-line utility that will allow you
35
+ to send an SMS directly from the command-line.
36
+
37
+ You will need to create a YAML configuration file in your home directory, in a
38
+ file called .clickatell that resembles the following:
39
+
40
+ <pre><code>
41
+ # ~/.clickatell
42
+ api_key: your_api_id
43
+ username: your_username
44
+ password: your_password
45
+ </code></pre>
46
+
47
+ You can then use the sms utility to send a message to a single recipient:
48
+
49
+ <pre><code>
50
+ $ sms 447771234567 'Hello from clickatell'
51
+ </code></pre>
52
+
53
+ h3. License
54
+
55
+ This code is free to use under the terms of the MIT license.
56
+
57
+ h3. Contact
58
+
59
+ Comments are welcome. Send an email to "Luke Redpath":mailto:contact@lukeredpath.co.uk email.
@@ -0,0 +1,188 @@
1
+ /* Unobtrustive Code Highlighter By Dan Webb 11/2005
2
+ Version: 0.4
3
+
4
+ Usage:
5
+ Add a script tag for this script and any stylesets you need to use
6
+ to the page in question, add correct class names to CODE elements,
7
+ define CSS styles for elements. That's it!
8
+
9
+ Known to work on:
10
+ IE 5.5+ PC
11
+ Firefox/Mozilla PC/Mac
12
+ Opera 7.23 + PC
13
+ Safari 2
14
+
15
+ Known to degrade gracefully on:
16
+ IE5.0 PC
17
+
18
+ Note: IE5.0 fails due to the use of lookahead in some stylesets. To avoid script errors
19
+ in older browsers use expressions that use lookahead in string format when defining stylesets.
20
+
21
+ This script is inspired by star-light by entirely cunning Dean Edwards
22
+ http://dean.edwards.name/star-light/.
23
+ */
24
+
25
+ // replace callback support for safari.
26
+ if ("a".replace(/a/, function() {return "b"}) != "b") (function(){
27
+ var default_replace = String.prototype.replace;
28
+ String.prototype.replace = function(search,replace){
29
+ // replace is not function
30
+ if(typeof replace != "function"){
31
+ return default_replace.apply(this,arguments)
32
+ }
33
+ var str = "" + this;
34
+ var callback = replace;
35
+ // search string is not RegExp
36
+ if(!(search instanceof RegExp)){
37
+ var idx = str.indexOf(search);
38
+ return (
39
+ idx == -1 ? str :
40
+ default_replace.apply(str,[search,callback(search, idx, str)])
41
+ )
42
+ }
43
+ var reg = search;
44
+ var result = [];
45
+ var lastidx = reg.lastIndex;
46
+ var re;
47
+ while((re = reg.exec(str)) != null){
48
+ var idx = re.index;
49
+ var args = re.concat(idx, str);
50
+ result.push(
51
+ str.slice(lastidx,idx),
52
+ callback.apply(null,args).toString()
53
+ );
54
+ if(!reg.global){
55
+ lastidx += RegExp.lastMatch.length;
56
+ break
57
+ }else{
58
+ lastidx = reg.lastIndex;
59
+ }
60
+ }
61
+ result.push(str.slice(lastidx));
62
+ return result.join("")
63
+ }
64
+ })();
65
+
66
+ var CodeHighlighter = { styleSets : new Array };
67
+
68
+ CodeHighlighter.addStyle = function(name, rules) {
69
+ // using push test to disallow older browsers from adding styleSets
70
+ if ([].push) this.styleSets.push({
71
+ name : name,
72
+ rules : rules,
73
+ ignoreCase : arguments[2] || false
74
+ })
75
+
76
+ function setEvent() {
77
+ // set highlighter to run on load (use LowPro if present)
78
+ if (typeof Event != 'undefined' && typeof Event.onReady == 'function')
79
+ return Event.onReady(CodeHighlighter.init.bind(CodeHighlighter));
80
+
81
+ var old = window.onload;
82
+
83
+ if (typeof window.onload != 'function') {
84
+ window.onload = function() { CodeHighlighter.init() };
85
+ } else {
86
+ window.onload = function() {
87
+ old();
88
+ CodeHighlighter.init();
89
+ }
90
+ }
91
+ }
92
+
93
+ // only set the event when the first style is added
94
+ if (this.styleSets.length==1) setEvent();
95
+ }
96
+
97
+ CodeHighlighter.init = function() {
98
+ if (!document.getElementsByTagName) return;
99
+ if ("a".replace(/a/, function() {return "b"}) != "b") return; // throw out Safari versions that don't support replace function
100
+ // throw out older browsers
101
+
102
+ var codeEls = document.getElementsByTagName("CODE");
103
+ // collect array of all pre elements
104
+ codeEls.filter = function(f) {
105
+ var a = new Array;
106
+ for (var i = 0; i < this.length; i++) if (f(this[i])) a[a.length] = this[i];
107
+ return a;
108
+ }
109
+
110
+ var rules = new Array;
111
+ rules.toString = function() {
112
+ // joins regexes into one big parallel regex
113
+ var exps = new Array;
114
+ for (var i = 0; i < this.length; i++) exps.push(this[i].exp);
115
+ return exps.join("|");
116
+ }
117
+
118
+ function addRule(className, rule) {
119
+ // add a replace rule
120
+ var exp = (typeof rule.exp != "string")?String(rule.exp).substr(1, String(rule.exp).length-2):rule.exp;
121
+ // converts regex rules to strings and chops of the slashes
122
+ rules.push({
123
+ className : className,
124
+ exp : "(" + exp + ")",
125
+ length : (exp.match(/(^|[^\\])\([^?]/g) || "").length + 1, // number of subexps in rule
126
+ replacement : rule.replacement || null
127
+ });
128
+ }
129
+
130
+ function parse(text, ignoreCase) {
131
+ // main text parsing and replacement
132
+ return text.replace(new RegExp(rules, (ignoreCase)?"gi":"g"), function() {
133
+ var i = 0, j = 1, rule;
134
+ while (rule = rules[i++]) {
135
+ if (arguments[j]) {
136
+ // if no custom replacement defined do the simple replacement
137
+ if (!rule.replacement) return "<span class=\"" + rule.className + "\">" + arguments[0] + "</span>";
138
+ else {
139
+ // replace $0 with the className then do normal replaces
140
+ var str = rule.replacement.replace("$0", rule.className);
141
+ for (var k = 1; k <= rule.length - 1; k++) str = str.replace("$" + k, arguments[j + k]);
142
+ return str;
143
+ }
144
+ } else j+= rule.length;
145
+ }
146
+ });
147
+ }
148
+
149
+ function highlightCode(styleSet) {
150
+ // clear rules array
151
+ var parsed, clsRx = new RegExp("(\\s|^)" + styleSet.name + "(\\s|$)");
152
+ rules.length = 0;
153
+
154
+ // get stylable elements by filtering out all code elements without the correct className
155
+ var stylableEls = codeEls.filter(function(item) { return clsRx.test(item.className) });
156
+
157
+ // add style rules to parser
158
+ for (var className in styleSet.rules) addRule(className, styleSet.rules[className]);
159
+
160
+
161
+ // replace for all elements
162
+ for (var i = 0; i < stylableEls.length; i++) {
163
+ // EVIL hack to fix IE whitespace badness if it's inside a <pre>
164
+ if (/MSIE/.test(navigator.appVersion) && stylableEls[i].parentNode.nodeName == 'PRE') {
165
+ stylableEls[i] = stylableEls[i].parentNode;
166
+
167
+ parsed = stylableEls[i].innerHTML.replace(/(<code[^>]*>)([^<]*)<\/code>/i, function() {
168
+ return arguments[1] + parse(arguments[2], styleSet.ignoreCase) + "</code>"
169
+ });
170
+ parsed = parsed.replace(/\n( *)/g, function() {
171
+ var spaces = "";
172
+ for (var i = 0; i < arguments[1].length; i++) spaces+= "&nbsp;";
173
+ return "\n" + spaces;
174
+ });
175
+ parsed = parsed.replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;");
176
+ parsed = parsed.replace(/\n(<\/\w+>)?/g, "<br />$1").replace(/<br \/>[\n\r\s]*<br \/>/g, "<p><br></p>");
177
+
178
+ } else parsed = parse(stylableEls[i].innerHTML, styleSet.ignoreCase);
179
+
180
+ stylableEls[i].innerHTML = parsed;
181
+ }
182
+ }
183
+
184
+ // run highlighter on all stylesets
185
+ for (var i=0; i < this.styleSets.length; i++) {
186
+ highlightCode(this.styleSets[i]);
187
+ }
188
+ }