4info 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,59 @@
1
+ 4info
2
+ =====
3
+
4
+ Connect to the 4info SMS gateway
5
+
6
+ If you're using 4info.com as your SMS gateway this gem will give you a painless API.
7
+
8
+ USAGE
9
+ =====
10
+
11
+ Include FourInfo::Contactable into your User class or whatever you're using to represent an entity with a phone number.
12
+
13
+ class User < ActiveRecord::Base
14
+ include FourInfo::Contactable
15
+ end
16
+
17
+ You can also specify which attributes you'd like to use instead of the defaults
18
+
19
+ class User < ActiveRecord::Base
20
+ include FourInfo::Contactable
21
+
22
+ sms_phone_number_column :mobile_number
23
+ sms_blocked_column :is_sms_blocked
24
+ sms_confirmation_code_column :the_sms_confirmation_code
25
+ sms_confirmation_attempted_column :when_was_the_sms_confirmation_attempted
26
+ sms_confirmed_column :is_the_mobile_number_confirmed
27
+
28
+ # Defaults to the name on the left (minus the word '_column')
29
+ end
30
+
31
+ You can manage the user's SMS state like so:
32
+
33
+ @user = User.create(:sms_phone_number => '5552223333')
34
+ @user.confirm_sms!
35
+ # then ask the user for the confirmation code and
36
+ # compare it to @user.sms_confirmation_code
37
+ @user.update_attributes(:sms_confirmed => true)
38
+ @user.send_sms!("Hi! This is a text message.")
39
+ # Then maybe the user will reply with 'BLOCK' by accident
40
+ @user.unblock_sms!
41
+
42
+
43
+ There's also a controller module that allows you to super-easily create a controller
44
+ that receives data from 4info.com
45
+
46
+ class SMSController < ApplicationController
47
+ include FourInfo::Controller
48
+
49
+ sms_contactable User # or whichever class you included FourInfo::Contactable into
50
+ end
51
+
52
+ Now anything posted to the index (or create, if you've hooked this up RESTfully) action
53
+ will automatically work. If 4info.com sends you a response from a user the user record
54
+ will be found by their phone number and, if User has a 'receive_sms' method defined,
55
+ the record will receive the message that's just been sent.
56
+
57
+ That's it. Patches welcome, forks celebrated.
58
+
59
+ Copyright (c) 2010 Jack Danger Canty (http://jåck.com/), released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "4info"
8
+ gem.summary = %Q{Send and receive SMS messages via 4info.com}
9
+ gem.description = %Q{A complete Ruby API for handling SMS messages via 4info.com}
10
+ gem.email = "gitcommit@6brand.com"
11
+ gem.homepage = "http://github.com/JackDanger/4info"
12
+ gem.authors = ["Jack Danger Canty"]
13
+ gem.add_dependency "hpricot", ">= 0"
14
+ gem.add_dependency "haml", ">= 0"
15
+ gem.add_development_dependency "shoulda", ">= 0"
16
+ gem.add_development_dependency "mocha", ">= 0"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ require 'rake/testtask'
25
+ desc "Test 4info"
26
+ Rake::TestTask.new(:test) do |test|
27
+ test.libs << 'lib' << 'test'
28
+ test.pattern = 'test/**/*_test.rb'
29
+ test.verbose = true
30
+ end
31
+
32
+ begin
33
+ require 'rcov/rcovtask'
34
+ Rcov::RcovTask.new do |test|
35
+ test.libs << 'test'
36
+ test.pattern = 'test/**/test_*.rb'
37
+ test.verbose = true
38
+ end
39
+ rescue LoadError
40
+ task :rcov do
41
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
42
+ end
43
+ end
44
+
45
+ task :test => :check_dependencies
46
+
47
+ task :default => :test
48
+
49
+ require 'rake/rdoctask'
50
+ Rake::RDocTask.new do |rdoc|
51
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "inline_styles #{version}"
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ # Include hook code here
2
+
3
+ require 'four_info'
@@ -0,0 +1,83 @@
1
+ module FourInfo
2
+ module Contactable
3
+
4
+ Attributes = [ :sms_phone_number,
5
+ :sms_blocked,
6
+ :sms_confirmation_code,
7
+ :sms_confirmation_attempted,
8
+ :sms_confirmed ]
9
+
10
+ def self.included(model)
11
+ gem 'haml'
12
+ require 'haml'
13
+ require 'net/http'
14
+
15
+ Attributes.each do |attribute|
16
+ # add a method for setting or retrieving
17
+ # which column should be used for which attribute
18
+ #
19
+ # :sms_phone_number_column defaults to :sms_phone_number, etc.
20
+ model.instance_eval "
21
+ def #{attribute}_column(value = nil)
22
+ @#{attribute}_column ||= :#{attribute}
23
+ @#{attribute}_column = value if value
24
+ @#{attribute}_column
25
+ end
26
+ "
27
+ # provide a helper method to access the right value
28
+ # no matter which column it's stored in
29
+ #
30
+ # e.g.: @user.four_info_sms_confirmed
31
+ # => @user.send(User.sms_confirmed_column)
32
+ model.class_eval "
33
+ def four_info_#{attribute}(value = nil)
34
+ value ?
35
+ send(self.class.#{attribute}_column.to_s+'=', value) :
36
+ send(self.class.#{attribute}_column)
37
+ end
38
+ alias_method :four_info_#{attribute}?, :four_info_#{attribute}
39
+ alias_method :four_info_#{attribute}=, :four_info_#{attribute}
40
+ "
41
+ end
42
+ end
43
+
44
+ def send_sms!(msg, allow_multiple = false)
45
+ if msg.to_s.size > 160 && !allow_multiple
46
+ raise ArgumentError, "SMS Message is too long. Either specify that you want multiple messages or shorten the string."
47
+ end
48
+ return false if msg.to_s.strip.blank? || four_info_sms_blocked? || !four_info_sms_confirmed?
49
+
50
+ msg.to_s.scan(/.{1,160}/m).map do |text|
51
+ FourInfo::Request.new.deliver_message(text, four_info_sms_phone_number).success?
52
+ end
53
+ end
54
+
55
+ def confirm_sms!
56
+ return false if four_info_sms_blocked?
57
+ return true if four_info_sms_confirmed?
58
+ return false if four_info_sms_phone_number.blank?
59
+
60
+ response = FourInfo::Request.new.confirm(four_info_sms_phone_number)
61
+ if response.success?
62
+ self.four_info_sms_confirmation_code = response.confirmation_code
63
+ self.four_info_sms_confirmation_attempted = Time.now
64
+ save
65
+ else
66
+ # "Confirmation Failed: #{response['message'].inspect}"
67
+ false
68
+ end
69
+ end
70
+
71
+ def unblock_sms!
72
+ return false unless four_info_sms_blocked?
73
+
74
+ response = FourInfo::Request.new.unblock(four_info_sms_phone_number)
75
+ if response.success?
76
+ four_info_sms_blocked 'false'
77
+ save!
78
+ else
79
+ false
80
+ end
81
+ end
82
+ end
83
+ end
data/lib/controller.rb ADDED
@@ -0,0 +1,62 @@
1
+ module FourInfo
2
+ module Controller
3
+
4
+ def self.included(controller)
5
+ controller.instance_eval do
6
+ # the user should specify which class gets contacted
7
+ def sms_contactable(klass)
8
+ @@contactable_class = klass
9
+ end
10
+ end
11
+ end
12
+
13
+ # the likely default
14
+ def index
15
+ recieve_xml
16
+ end
17
+
18
+ # in case this is hooked up as a RESTful route
19
+ def create
20
+ recieve_xml
21
+ end
22
+
23
+ protected
24
+
25
+ def recieve_xml
26
+
27
+ unless defined?(@@contactable_class)
28
+ raise RuntimeError, "Please define your user class in the FourInfo controller via the 'sms_contactable' method"
29
+ end
30
+
31
+ request = params[:request]
32
+ render :text => 'unknown format', :status => 500 and return unless request
33
+ case request['type']
34
+ when 'BLOCK'
35
+ @contactable = find_contactable(request[:block][:recipient][:id])
36
+ @contactable.four_info_sms_blocked = true
37
+ @contactable.save!
38
+ when 'MESSAGE'
39
+ @contactable = find_contactable(request[:message][:sender][:id])
40
+ if @contactable.respond_to?(:receive_sms)
41
+ @contactable.receive_sms(request[:message][:text])
42
+ else
43
+ warn "An SMS message was received but #{@@contactable_class.name.inspect} doesn't have a receive_sms method!"
44
+ end
45
+ end
46
+ render :text => 'OK', :status => 200
47
+ end
48
+
49
+ def find_contactable(id)
50
+ [id, id.sub(/^\+/,''), id.sub(/^\+1/,'')].each do |possible_phone_number|
51
+ found = @@contactable_class.find(
52
+ :first,
53
+ :conditions =>
54
+ { @@contactable_class.sms_phone_number_column => possible_phone_number }
55
+ )
56
+ return found if found
57
+ end
58
+ # rescue => error
59
+ # render :text => error.inspect
60
+ end
61
+ end
62
+ end
data/lib/four_info.rb ADDED
@@ -0,0 +1,35 @@
1
+ module FourInfo
2
+ Gateway = URI.parse 'http://gateway.4info.net/msg'
3
+
4
+ class << self
5
+ def mode
6
+ @@mode ||= :live
7
+ end
8
+ def mode=(new_mode)
9
+ @@mode = new_mode
10
+ end
11
+
12
+ def numerize(numberish)
13
+ numberish.to_s.scan(/\d+/).join
14
+ end
15
+
16
+ def internationalize(given_number)
17
+ number = numerize(given_number)
18
+ case number.size
19
+ when 10
20
+ "+1#{number}"
21
+ when 11
22
+ "+#{number}"
23
+ when 12
24
+ number =~ /\+\d(11)/ ? number : nil
25
+ else
26
+ nil
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require File.join(File.dirname(__FILE__), 'contactable')
33
+ require File.join(File.dirname(__FILE__), 'controller')
34
+ require File.join(File.dirname(__FILE__), 'request')
35
+ require File.join(File.dirname(__FILE__), 'response')
data/lib/request.rb ADDED
@@ -0,0 +1,76 @@
1
+ module FourInfo
2
+ class Request
3
+
4
+ # Haml templates for XML
5
+ @@templates = Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), 'templates', '*.haml')))
6
+
7
+ # YML config files
8
+ @@test_mode_config_file = File.join(File.dirname(__FILE__), '..', 'test', 'sms.yml')
9
+ @@likely_config_files = [
10
+ File.join(File.dirname(__FILE__), '..', 'sms.yml'),
11
+ defined?(Rails) ? File.join(Rails.root, 'config', 'sms.yml') : '',
12
+ File.join('config', 'sms.yml'),
13
+ 'sms.yml'
14
+ ]
15
+
16
+ attr_accessor :config
17
+ attr_accessor :number
18
+ attr_accessor :message
19
+
20
+ def initialize
21
+ config_file = :test == FourInfo.mode ?
22
+ @@test_mode_config_file :
23
+ @@likely_config_files.detect {|f| File.exist?(f) }
24
+
25
+ raise "Missing config File! Please add sms.yml to ./config or the 4info directory" unless config_file
26
+
27
+ @config = YAML.load(File.read(config_file))['4info'].with_indifferent_access
28
+ end
29
+
30
+ def deliver_message(message, number)
31
+ self.number = FourInfo.internationalize(number)
32
+ self.message = message
33
+
34
+ xml = template(:deliver).render(self)
35
+ Response.new(perform(xml))
36
+ end
37
+
38
+ def confirm(number)
39
+ self.number = FourInfo.internationalize(number)
40
+
41
+ xml = template(:confirm).render(self)
42
+ Response.new(perform(xml))
43
+ end
44
+
45
+ def unblock(number)
46
+ self.number = FourInfo.internationalize(number)
47
+
48
+ xml = template(:unblock).render(self)
49
+ Response.new(perform(xml))
50
+ end
51
+
52
+ protected
53
+
54
+ def template(name)
55
+ file = @@templates.detect {|t| File.basename(t).chomp('.haml').to_sym == name.to_sym }
56
+ raise ArgumentError, "Missing 4Info template: #{name}" unless file
57
+ Haml::Engine.new(File.read(file))
58
+ end
59
+
60
+ def perform(body)
61
+ STDOUT.puts('in perform')
62
+ start do |http|
63
+ http.post(FourInfo::Gateway.path, body).read_body
64
+ end
65
+ end
66
+
67
+ def start
68
+ net = config[:proxy].blank? ?
69
+ Net::HTTP :
70
+ Net::HTTP::Proxy(*config[:proxy].split(":"))
71
+ net.start(FourInfo::Gateway.host, FourInfo::Gateway.port) do |http|
72
+ yield http
73
+ end
74
+ end
75
+ end
76
+ end
data/lib/response.rb ADDED
@@ -0,0 +1,22 @@
1
+ module FourInfo
2
+ class Response
3
+ def initialize(xml)
4
+ gem 'hpricot'
5
+ require 'hpricot'
6
+ @body = Hpricot.parse(xml)
7
+ end
8
+
9
+ def [](name)
10
+ nodes = (@body/name)
11
+ 1 == nodes.size ? nodes.first : nodes
12
+ end
13
+
14
+ def success?
15
+ 'Success' == self['message'].inner_text
16
+ end
17
+
18
+ def confirmation_code
19
+ self['confCode'].inner_text
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ !!! xml 1.0
2
+ %request{ :clientId => config[:client_id], :clientKey => config[:client_key], :type => "VALIDATION" }
3
+ %validation
4
+ %recipient
5
+ %type= 5
6
+ %id= number
@@ -0,0 +1,7 @@
1
+ !!! xml 1.0
2
+ %request{ :clientId => config[:client_id], :clientKey => config[:client_key], :type => "MESSAGE" }
3
+ %message
4
+ %recipient
5
+ %type= 5
6
+ %id= number
7
+ %text= message
@@ -0,0 +1,6 @@
1
+ !!! xml 1.0
2
+ %request{ :clientId => config[:client_id], :clientKey => config[:client_key], :type => "UNBLOCK" }
3
+ %unblock
4
+ %recipient
5
+ %type= 5
6
+ %id= number
data/test/.gitignore ADDED
@@ -0,0 +1 @@
1
+ debug.log
data/test/database.yml ADDED
@@ -0,0 +1,18 @@
1
+ sqlite:
2
+ :adapter: sqlite
3
+ :database: plugin.sqlite.db
4
+ sqlite3:
5
+ :adapter: sqlite3
6
+ :database: ":memory:"
7
+ postgresql:
8
+ :adapter: postgresql
9
+ :username: postgres
10
+ :password: postgres
11
+ :database: plugin_test
12
+ :min_messages: ERROR
13
+ mysql:
14
+ :adapter: mysql
15
+ :host: localhost
16
+ :username: rails
17
+ :password:
18
+ :database: plugin_test
@@ -0,0 +1,200 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class FourInfoContactableTest < ActiveSupport::TestCase
4
+
5
+ ValidationError = '<?xml version="1.0" encoding="UTF-8"?>
6
+ <response>
7
+ <status>
8
+ <id>4</id>
9
+ <message>Validation Error</message>
10
+ </status>
11
+ </response>'
12
+ ValidationSuccess = '<?xml version=”1.0” ?>
13
+ <response>
14
+ <requestId>F81D4FAE-7DEC-11D0-A765-00A0C91E6BF6</requestId>
15
+ <confCode>123abc</confCode>
16
+ <status>
17
+ <id>1</id>
18
+ <message>Success</message>
19
+ </status>
20
+ </response>'
21
+ SendMsgSuccess = '<?xml version="1.0" ?>
22
+ <response>
23
+ <requestId>F81D4FAE-7DEC-11D0-A765-00A0C91E6BF6</requestId>
24
+ <status>
25
+ <id>1</id>
26
+ <message>Success</message>
27
+ </status>
28
+ </response>'
29
+ UnblockSuccess = '<?xml version=”1.0” ?>
30
+ <response>
31
+ <status>
32
+ <id>1</id>
33
+ <message>Success</message>
34
+ </status>
35
+ </response>'
36
+
37
+ context "contactable class" do
38
+ setup {
39
+ @klass = Class.new
40
+ @klass.send :include, FourInfo::Contactable
41
+ }
42
+ FourInfo::Contactable::Attributes.each do |attribute|
43
+ should "begin with appropriate default for #{attribute}_column" do
44
+ assert_equal attribute, @klass.send("#{attribute}_column")
45
+ end
46
+ should "allow setting #{attribute}_column" do
47
+ new_column_name = :custom_column
48
+ @klass.send "#{attribute}_column", new_column_name
49
+ assert_equal new_column_name, @klass.send("#{attribute}_column")
50
+ end
51
+ end
52
+ end
53
+
54
+ context "contactable instance" do
55
+ setup {
56
+ User.delete_all
57
+ @user = User.create! :sms_phone_number => '555-555-5555'
58
+ }
59
+
60
+ context "when phone number is blank" do
61
+ setup { @user.sms_phone_number = nil}
62
+ context "confirming phone number" do
63
+ setup { @user.confirm_sms! }
64
+ should_not_change "any attributes" do
65
+ @user.attributes.inspect
66
+ end
67
+ end
68
+ context "sending message" do
69
+ setup {
70
+ FourInfo::Request.any_instance.stubs(:perform).returns(SendMsgSuccess)
71
+ @worked = @user.send_sms!('message')
72
+ }
73
+ should "not work" do assert !@worked end
74
+ should_not_change "any attributes" do
75
+ @user.attributes.inspect
76
+ end
77
+ end
78
+ end
79
+
80
+ context "when phone number exists" do
81
+ setup { @user.sms_phone_number = "206-555-5555"}
82
+ context "confirming phone number" do
83
+ setup {
84
+ FourInfo::Request.any_instance.stubs(:perform).returns(ValidationSuccess)
85
+ @worked = @user.confirm_sms!
86
+ }
87
+ should "work" do assert @worked end
88
+ should "save confirmation number in proper attribute" do
89
+ assert @user.four_info_sms_confirmation_code
90
+ end
91
+ should_change "stored code" do
92
+ @user.four_info_sms_confirmation_code
93
+ end
94
+ should "not change sms_confirmed? to true" do
95
+ assert !@user.four_info_sms_confirmed?
96
+ end
97
+ end
98
+ context "confirming phone number when the confirmation fails for some reason" do
99
+ setup {
100
+ FourInfo::Request.any_instance.stubs(:perform).returns(ValidationError)
101
+ @worked = @user.confirm_sms!
102
+ }
103
+ should "not work" do assert !@worked end
104
+ should "not save confirmation number" do
105
+ assert @user.four_info_sms_confirmation_code.blank?
106
+ end
107
+ should_not_change "stored code" do
108
+ @user.four_info_sms_confirmation_code
109
+ end
110
+ end
111
+ end
112
+
113
+ context "when the number is not confirmed" do
114
+ context "sending a message" do
115
+ setup {
116
+ FourInfo::Request.any_instance.stubs(:perform).returns(SendMsgSuccess)
117
+ @result = @user.send_sms!('message')
118
+ }
119
+ should "send send no messages" do
120
+ assert_equal false, @result
121
+ end
122
+ end
123
+ end
124
+ context "when the number is blocked" do
125
+ setup {
126
+ @user.four_info_sms_blocked = true
127
+ @user.save!
128
+ }
129
+ context "sending a message" do
130
+ setup { @result = @user.send_sms!('message') }
131
+ should "send nothing" do
132
+ assert_equal false, @result
133
+ end
134
+ end
135
+ end
136
+ context "when the number is confirmed" do
137
+ setup {
138
+ FourInfo::Request.any_instance.stubs(:perform).returns(SendMsgSuccess)
139
+ @user.update_attributes!(User.sms_confirmed_column => true)
140
+ }
141
+ context "sending a message" do
142
+ setup { @result = @user.send_sms!('message') }
143
+ should "send send exactly one message messages" do
144
+ assert_equal [true], @result
145
+ end
146
+ end
147
+ context "sending a blank message" do
148
+ setup { @result = @user.send_sms!('') }
149
+ should "send send zero messages" do
150
+ assert_equal false, @result
151
+ end
152
+ end
153
+ context "sending a huge message" do
154
+ context "without the allow_multiple flag" do
155
+ should "raise an error" do
156
+ assert_raises ArgumentError do
157
+ @user.send_sms!("A"*200)
158
+ end
159
+ end
160
+ end
161
+ context "with the allow_multiple flag" do
162
+ setup { @result = @user.send_sms!("A"*200, true) }
163
+ should "send multiple messages" do
164
+ assert_equal [true, true], @result
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ context "when the number is not blocked" do
171
+ setup {
172
+ FourInfo::Request.any_instance.expects(:perform).never
173
+ }
174
+ context "unblocking" do
175
+ setup { @worked = @user.unblock_sms! }
176
+ should "not do anything" do
177
+ assert !@worked
178
+ end
179
+ should_not_change "any attributes" do
180
+ @user.attributes.inspect
181
+ end
182
+ end
183
+ end
184
+ context "when the number is blocked" do
185
+ setup {
186
+ FourInfo::Request.any_instance.stubs(:perform).returns(UnblockSuccess)
187
+ @user.update_attributes!(:sms_blocked => true)
188
+ }
189
+ context "unblocking" do
190
+ setup { @worked = @user.unblock_sms! }
191
+ should "work" do
192
+ assert @worked
193
+ end
194
+ should_change "blocked attribute" do
195
+ @user.reload.sms_blocked
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,74 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+ require 'action_pack'
3
+ require 'action_controller'
4
+ require 'shoulda/action_controller'
5
+ require 'shoulda/action_controller/macros'
6
+ require 'shoulda/action_controller/matchers'
7
+
8
+ class FourInfoController < ActionController::Base
9
+ include FourInfo::Controller
10
+
11
+ sms_contactable User
12
+ end
13
+ ActionController::Routing::Routes.draw do |map|
14
+ map.route '*:url', :controller => 'four_info', :action => :index
15
+ end
16
+
17
+ class UserWithSMSReceiving < User
18
+ def receive_sms(message)
19
+ end
20
+ end
21
+
22
+ class FourInfoControllerTest < ActionController::TestCase
23
+
24
+ context "with a user" do
25
+ setup {
26
+ User.delete_all
27
+ @user = User.create! :sms_phone_number => '3334445555'
28
+ }
29
+ context "receiving BLOCK" do
30
+ setup {
31
+ post :index,
32
+ # this is what an xml request will parse to:
33
+ "request"=>{"block"=>{"recipient"=>{"property"=>{"name"=>"CARRIER", "value"=>"3"}, "id"=>"+1#{@user.sms_phone_number}", "type"=>"5"}}, "type" => "BLOCK"}
34
+ }
35
+ should_respond_with :success
36
+ should "block user" do
37
+ assert @user.reload.four_info_sms_blocked?
38
+ end
39
+ should_change "user block status" do
40
+ @user.reload.four_info_sms_blocked?
41
+ end
42
+ end
43
+ context "receiving MESSAGE" do
44
+ setup {
45
+ # this is what an xml request will parse to:
46
+ @receive_params = {"request"=>{"message"=>{"id" => "ABCDEFG", "recipient"=>{"type"=> "6", "id"=>"12345"}, "sender" => {"type" => "5", "id" => "+1#{@user.sms_phone_number}", "property" => {"name" => "CARRIER", "value" => "5"}}, "text" => "This is a text message."}, "type" => "MESSAGE"}}
47
+ }
48
+ context "when the user is not set up to receive" do
49
+ setup {
50
+ @user.expects(:receive_sms).with("This is a text message.").never
51
+ post :index,
52
+ @receive_params
53
+ }
54
+ should_respond_with :success
55
+ should "not block user" do
56
+ assert !@user.reload.four_info_sms_blocked?
57
+ end
58
+ end
59
+ context "when the user is set up to receive" do
60
+ setup {
61
+ User.delete_all
62
+ @new_user = UserWithSMSReceiving.create!(:sms_phone_number => @user.sms_phone_number)
63
+ UserWithSMSReceiving.any_instance.expects(:receive_sms).with("This is a text message.").once
64
+ post :index,
65
+ @receive_params
66
+ }
67
+ should_respond_with :success
68
+ should "not block user" do
69
+ assert !@new_user.reload.four_info_sms_blocked?
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,39 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class FourInfoModuleTest < ActiveSupport::TestCase
4
+
5
+ context "standardizing numbers" do
6
+ context "to digits" do
7
+ should "remove all but integers" do
8
+ assert_equal '12345', FourInfo.numerize('1-2-3-4-5')
9
+ assert_equal '12345', FourInfo.numerize('1 2 3 4 5')
10
+ assert_equal '12345', FourInfo.numerize('1,2(3)4.5')
11
+ assert_equal '12345', FourInfo.numerize('1,2(3)4.5')
12
+ end
13
+ end
14
+ context "to international format" do
15
+ should "add a '+' to all 11 digit numbers" do
16
+ assert_equal '+12345678901', FourInfo.internationalize('12345678901')
17
+ assert_equal '+72345678901', FourInfo.internationalize('72345678901')
18
+ end
19
+ should "add a '+1' to any 10 digit number" do
20
+ assert_equal '+12345678901', FourInfo.internationalize('2345678901')
21
+ assert_equal '+17345678901', FourInfo.internationalize('7345678901')
22
+ end
23
+ should "leave 12 digit numbers unchanged" do
24
+ [ '+' + ('3'*11),
25
+ '+' + ('8'*11),
26
+ '+' + ('4'*11) ].each do |number|
27
+ assert_equal number, FourInfo.internationalize(number)
28
+ end
29
+ end
30
+ should "return nil for all bad numbers" do
31
+ assert_equal nil, FourInfo.internationalize(nil)
32
+ assert_equal nil, FourInfo.internationalize('nil')
33
+ assert_equal nil, FourInfo.internationalize('1234')
34
+ assert_equal nil, FourInfo.internationalize('11111111111111111111111')
35
+ assert_equal nil, FourInfo.internationalize('what?')
36
+ end
37
+ end
38
+ end
39
+ end
data/test/sms.yml ADDED
@@ -0,0 +1,3 @@
1
+ 4info:
2
+ client_id: 1
3
+ client_key: ABCDEFG
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ gem 'test-unit'
3
+ require 'test/unit'
4
+ require 'mocha'
5
+ require 'active_support'
6
+ require 'active_record'
7
+ require 'active_support/test_case'
8
+ require 'shoulda'
9
+ require File.join(File.dirname(__FILE__), "..", 'lib', 'four_info')
10
+
11
+ FourInfo.mode = :test
12
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
13
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
14
+ ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
15
+
16
+ ActiveRecord::Schema.define(:version => 1) do
17
+ create_table :users do |t|
18
+ t.column :sms_phone_number, :string
19
+ t.column :sms_confirmed, :boolean
20
+ t.column :sms_blocked, :boolean
21
+ t.column :sms_confirmation_attempted, :datetime
22
+ t.column :sms_confirmation_code, :string
23
+
24
+ t.column :type, :string
25
+ end
26
+ end
27
+
28
+ class User < ActiveRecord::Base
29
+ include FourInfo::Contactable
30
+ end
31
+
32
+ # kill all network access
33
+ # module FourInfo
34
+ # class Request
35
+ # def start
36
+ # Response.new(:success => true,
37
+ # :confirmation_code => 'FAKE')
38
+ # end
39
+ # end
40
+ # end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: 4info
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jack Danger Canty
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-10 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hpricot
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: haml
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: shoulda
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: mocha
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description: A complete Ruby API for handling SMS messages via 4info.com
56
+ email: gitcommit@6brand.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README
63
+ files:
64
+ - MIT-LICENSE
65
+ - README
66
+ - Rakefile
67
+ - VERSION
68
+ - init.rb
69
+ - lib/contactable.rb
70
+ - lib/controller.rb
71
+ - lib/four_info.rb
72
+ - lib/request.rb
73
+ - lib/response.rb
74
+ - lib/templates/confirm.haml
75
+ - lib/templates/deliver.haml
76
+ - lib/templates/unblock.haml
77
+ - test/.gitignore
78
+ - test/database.yml
79
+ - test/four_info_contactable_test.rb
80
+ - test/four_info_controller_test.rb
81
+ - test/four_info_module_test.rb
82
+ - test/sms.yml
83
+ - test/test_helper.rb
84
+ has_rdoc: true
85
+ homepage: http://github.com/JackDanger/4info
86
+ licenses: []
87
+
88
+ post_install_message:
89
+ rdoc_options:
90
+ - --charset=UTF-8
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: "0"
98
+ version:
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: "0"
104
+ version:
105
+ requirements: []
106
+
107
+ rubyforge_project:
108
+ rubygems_version: 1.3.5
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Send and receive SMS messages via 4info.com
112
+ test_files:
113
+ - test/four_info_contactable_test.rb
114
+ - test/four_info_controller_test.rb
115
+ - test/four_info_module_test.rb
116
+ - test/test_helper.rb