4info 1.0.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.
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