blythedunham-sms_on_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/Manifest +101 -0
  3. data/README +163 -0
  4. data/README.rdoc +211 -0
  5. data/Rakefile +76 -0
  6. data/app/controllers/admin/sms_on_rails/base_controller.rb +11 -0
  7. data/app/controllers/admin/sms_on_rails/drafts_controller.rb +75 -0
  8. data/app/controllers/admin/sms_on_rails/outbounds_controller.rb +117 -0
  9. data/app/controllers/admin/sms_on_rails/phone_carriers_controller.rb +85 -0
  10. data/app/controllers/admin/sms_on_rails/phone_numbers_controller.rb +101 -0
  11. data/app/controllers/sms_on_rails/creation_support.rb +99 -0
  12. data/app/controllers/sms_on_rails_controller.rb +14 -0
  13. data/app/helpers/admin/sms_on_rails/drafts_helper.rb +2 -0
  14. data/app/helpers/admin/sms_on_rails/phone_carriers_helper.rb +2 -0
  15. data/app/helpers/sms_on_rails/phone_numbers_helper.rb +9 -0
  16. data/app/helpers/sms_on_rails/sms_helper.rb +44 -0
  17. data/app/models/sms_on_rails/draft.rb +9 -0
  18. data/app/models/sms_on_rails/outbound.rb +17 -0
  19. data/app/models/sms_on_rails/phone_carrier.rb +14 -0
  20. data/app/models/sms_on_rails/phone_number.rb +8 -0
  21. data/app/views/admin/sms_on_rails/base/index.html.erb +5 -0
  22. data/app/views/admin/sms_on_rails/drafts/_show.html.erb +34 -0
  23. data/app/views/admin/sms_on_rails/drafts/edit.html.erb +36 -0
  24. data/app/views/admin/sms_on_rails/drafts/index.html.erb +32 -0
  25. data/app/views/admin/sms_on_rails/drafts/new.html.erb +34 -0
  26. data/app/views/admin/sms_on_rails/drafts/send_sms.html.erb +3 -0
  27. data/app/views/admin/sms_on_rails/drafts/show.html.erb +4 -0
  28. data/app/views/admin/sms_on_rails/outbounds/edit.html.erb +68 -0
  29. data/app/views/admin/sms_on_rails/outbounds/index.html.erb +37 -0
  30. data/app/views/admin/sms_on_rails/outbounds/new.html.erb +54 -0
  31. data/app/views/admin/sms_on_rails/outbounds/show.html.erb +69 -0
  32. data/app/views/admin/sms_on_rails/phone_carriers/edit.html.erb +24 -0
  33. data/app/views/admin/sms_on_rails/phone_carriers/index.html.erb +24 -0
  34. data/app/views/admin/sms_on_rails/phone_carriers/new.html.erb +22 -0
  35. data/app/views/admin/sms_on_rails/phone_carriers/show.html.erb +24 -0
  36. data/app/views/admin/sms_on_rails/phone_numbers/edit.html.erb +33 -0
  37. data/app/views/admin/sms_on_rails/phone_numbers/index.html.erb +28 -0
  38. data/app/views/admin/sms_on_rails/phone_numbers/new.html.erb +31 -0
  39. data/app/views/admin/sms_on_rails/phone_numbers/show.html.erb +32 -0
  40. data/app/views/layouts/sms_on_rails/basic.html.erb +26 -0
  41. data/app/views/sms_on_rails/_phone_carrier_form_item.html.erb +6 -0
  42. data/app/views/sms_on_rails/_send_sms.html.erb +33 -0
  43. data/app/views/sms_on_rails/index.html.erb +8 -0
  44. data/app/views/sms_on_rails/send_sms.html.erb +3 -0
  45. data/app/views/sms_on_rails/show.html.erb +29 -0
  46. data/config/routes.rb +19 -0
  47. data/db/data/fixtures/sms_phone_carriers.yml +110 -0
  48. data/db/migrate/sms_on_rails_carrier_tables.rb +9 -0
  49. data/db/migrate/sms_on_rails_model_tables.rb +48 -0
  50. data/db/migrate/sms_on_rails_phone_number_tables.rb +11 -0
  51. data/db/seed_data.rb +16 -0
  52. data/generators/sms_on_rails/USAGE +31 -0
  53. data/generators/sms_on_rails/commands/inserts.rb +63 -0
  54. data/generators/sms_on_rails/commands/timestamps.rb +33 -0
  55. data/generators/sms_on_rails/runners/add_all_models.rb +6 -0
  56. data/generators/sms_on_rails/runners/dependencies.rb +1 -0
  57. data/generators/sms_on_rails/runners/remove_all_models.rb +5 -0
  58. data/generators/sms_on_rails/runners/sms_on_rails_routes.rb +14 -0
  59. data/generators/sms_on_rails/sms_on_rails_generator.rb +255 -0
  60. data/generators/sms_on_rails/templates/configuration/clickatell.rb +6 -0
  61. data/generators/sms_on_rails/templates/configuration/email_gateway.rb +7 -0
  62. data/generators/sms_on_rails/templates/migrate/schema_migration.rb +15 -0
  63. data/generators/sms_on_rails/templates/migrate/sms_on_rails_update_phone_numbers.rb +40 -0
  64. data/generators/sms_on_rails/templates/phone_number_collision.rb +2 -0
  65. data/init.rb +3 -0
  66. data/install.rb +1 -0
  67. data/lib/sms_on_rails.rb +8 -0
  68. data/lib/sms_on_rails/activerecord_extensions/acts_as_deliverable.rb +92 -0
  69. data/lib/sms_on_rails/activerecord_extensions/acts_as_substitutable.rb +80 -0
  70. data/lib/sms_on_rails/activerecord_extensions/has_a_sms_service_provider.rb +101 -0
  71. data/lib/sms_on_rails/activerecord_extensions/lockable_record.rb +186 -0
  72. data/lib/sms_on_rails/all_models.rb +3 -0
  73. data/lib/sms_on_rails/model_support/draft.rb +178 -0
  74. data/lib/sms_on_rails/model_support/outbound.rb +136 -0
  75. data/lib/sms_on_rails/model_support/phone_carrier.rb +77 -0
  76. data/lib/sms_on_rails/model_support/phone_number.rb +248 -0
  77. data/lib/sms_on_rails/model_support/phone_number_associations.rb +13 -0
  78. data/lib/sms_on_rails/schema_helper.rb +51 -0
  79. data/lib/sms_on_rails/service_providers/base.rb +222 -0
  80. data/lib/sms_on_rails/service_providers/clickatell.rb +52 -0
  81. data/lib/sms_on_rails/service_providers/dummy.rb +19 -0
  82. data/lib/sms_on_rails/service_providers/email_gateway.rb +68 -0
  83. data/lib/sms_on_rails/service_providers/email_gateway_support/errors.rb +20 -0
  84. data/lib/sms_on_rails/service_providers/email_gateway_support/sms_mailer.rb +21 -0
  85. data/lib/sms_on_rails/service_providers/email_gateway_support/sms_mailer/sms_through_gateway.erb +6 -0
  86. data/lib/sms_on_rails/util/sms_error.rb +12 -0
  87. data/lib/smsonrails.rb +1 -0
  88. data/public/images/sms_on_rails/railsYoDawg.jpg +0 -0
  89. data/public/stylesheets/sms_on_rails.css +137 -0
  90. data/sms_on_rails.gemspec +32 -0
  91. data/tasks/sms_on_rails_tasks.rake +67 -0
  92. data/test/active_record_extensions/delivery_and_locking_test.rb +84 -0
  93. data/test/models/draft_test.rb +72 -0
  94. data/test/models/outbound_test.rb +89 -0
  95. data/test/models/phone_number_test.rb +131 -0
  96. data/test/run.rb +18 -0
  97. data/test/service_providers/abstract_test_support.rb +104 -0
  98. data/test/service_providers/clickatell_test.rb +39 -0
  99. data/test/service_providers/email_gateway_test.rb +30 -0
  100. data/test/test_helper.rb +24 -0
  101. data/uninstall.rb +1 -0
  102. metadata +187 -0
@@ -0,0 +1,13 @@
1
+ module SmsOnRails
2
+ module ModelSupport
3
+ module PhoneNumberAssociations
4
+ def self.included(base)
5
+ base.send :has_many, :outbounds, :class_name => 'SmsOnRails::Outbound',
6
+ :foreign_key => 'sms_phone_number_id', :dependent => :delete_all
7
+ base.send :belongs_to, :carrier, :class_name => 'SmsOnRails::PhoneCarrier',
8
+ :foreign_key => :carrier_id
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,51 @@
1
+ module SmsOnRails
2
+ class SchemaHelper
3
+ class << self
4
+ def create(*files)
5
+ each_file(*files) {|file, options| file_to_string(file, options) }
6
+ end
7
+
8
+ def drop(*files)
9
+ each_file(*files) {|file, options| drop_tables(file, options)}
10
+ end
11
+
12
+ def each_file(*files, &block)
13
+ options = parse_options(files)
14
+ files.inject('') {|str, f| str << yield(f, options); str }
15
+ end
16
+
17
+ def schema(command, *files)
18
+ str = "ActiveRecord::Schema.define do\n"
19
+ str << self.send(command, *files)
20
+ str << "\nend"
21
+ str
22
+ end
23
+
24
+ def file_to_string(file, options={})
25
+ File.read(File.join(File.dirname(__FILE__), "../../db/migrate/#{file}.rb"))
26
+ end
27
+
28
+ def drop_tables(file, options={})
29
+ str = "\n"
30
+ data = file_to_string(file)
31
+ data.scan(/create_table\s+[":]([^\W]*)/) do
32
+ table_name = $1.dup
33
+ str << safe_code(str, options) { |code| code << " drop_table :#{table_name}\n" }
34
+ end
35
+ str
36
+ end
37
+
38
+ def safe_code(str, options, &block)
39
+ str = ''
40
+ str << " begin\n " if options[:safe]
41
+ yield str
42
+ str << " rescue Exception => e\n end\n" if options[:safe]
43
+ str
44
+ end
45
+
46
+ def parse_options(files)
47
+ options = files.last.is_a?(Hash) ? files.pop : {}
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,222 @@
1
+ # This is the base class for service providers from which all others inherit
2
+ # All service providers are singletons and can be accessed by their instance
3
+ #
4
+ # To send a message without validation
5
+ # SmsOnRails::ServiceProviders::<Provider>.instance.send_message '12065551234', 'my message', options
6
+ #
7
+ # To send a message with validation use send_to_phone_number with a string or SmsOnRails::PhoneNumber instance
8
+ # SmsOnRails::ServiceProviders<Provider>.instance.send_to_phone_number(number, message, options)
9
+ #
10
+ # To send an sms(with validation)
11
+ # SmsOnRails::ServiceProviders<Provider>.instance.send_sms(sms, options)
12
+ # However, it is preferred to use the locking mechanism to prevent double messages from being sent
13
+ # sms.deliver!
14
+ #
15
+ module SmsOnRails
16
+ module ServiceProviders
17
+ class Base
18
+ include Singleton
19
+
20
+ #the configuration options for the service provider
21
+ class_inheritable_hash :config
22
+ self.config ||= {}
23
+
24
+ #special deliver options specific to the service provider
25
+ class_inheritable_accessor :delivery_options
26
+ self.delivery_options ||= {}
27
+
28
+ #the default service provider
29
+ cattr_accessor :default_service_provider
30
+
31
+ #a unique id by which each service provider is identified
32
+ class_inheritable_accessor :provider_id, :instance_writer => false
33
+
34
+ #the logger -> ActiveRecord::Base.logger
35
+ cattr_accessor :logger
36
+ @@logger ||= ActiveRecord::Base.logger
37
+
38
+
39
+ def requires_carrier?; false; end
40
+
41
+ # Deliver all the sms messages marked with this as its sms service provider
42
+ def deliver
43
+ Outbound.deliver(delivery_options.merge(:conditions => ['sms_service_provider_id = ?', self.provider_id]))
44
+ end
45
+
46
+ [:config, :name, :provider_id, :logger, :human_name].each{|method| class_eval "def #{method}; self.class.#{method}; end"}
47
+
48
+
49
+ # Sends a message to a phone number directly to the provider.
50
+ # No validations are performed
51
+ # * +phone_number+ the exact string text to send
52
+ # * +message+ - the string message to send
53
+ # * +options+ - additional options to be used by the provider
54
+ #
55
+ def send_message(phone_number, message, options={})
56
+ raise SmsOnRails::SmsError.new("Override send_message in subclass #{self.class}")
57
+ end
58
+
59
+
60
+ # Sends a message to a phone number active record object or string
61
+ # and performs validation on the phone_number
62
+ #
63
+ # * +phone_number+ can be either a phone number active record obj or a string text. Strings are converted to phone_number objects
64
+ # * +message+ - the message to send
65
+ # * +options+ - additional options to be used by the provider
66
+ #
67
+ # ===Options
68
+ # <tt>:skip_validation</tt> - skips validation and sends the message directly to the
69
+ # provider
70
+ def send_to_phone(phone_obj, message, options={})
71
+
72
+ if options[:skip_validation]
73
+ phone_text = phone_obj.is_a?(ActiveRecord::Base) ? format_phone_number(phone_number) : phone_obj
74
+ return send_message(phone_text, message, options)
75
+ end
76
+
77
+ phone_number = phone_obj.is_a?(ActiveRecord::Base) ? phone_obj :
78
+ find_or_create_phone_number(phone_obj, options)
79
+
80
+ assert_message_options!(phone_number, message, options)
81
+ send_message(format_phone_number(phone_number), message, options)
82
+ end
83
+
84
+ # Send an Sms with validation
85
+ # +sms+ is an sms active record object that responds to phone_number and full_message
86
+ #
87
+ # Refer to send_to_phone for more infomation on validation
88
+ def send_sms(sms, options={})
89
+ send_to_phone(sms.phone_number, sms.actual_message, options)
90
+ end
91
+
92
+ protected
93
+
94
+ #can override in subclass if different finder should be used
95
+ def find_or_create_phone_number(phone_text, options={})
96
+ phone = phone_klass.find_and_create_by_number(phone_text)
97
+ phone.assign_carrier(options[:carrier]) if options[:carrier]
98
+ phone
99
+ end
100
+
101
+ #override this if the expected format differs in subclass
102
+ def format_phone_number(phone_number)
103
+ phone_number.digits
104
+ end
105
+
106
+ # Raise an exception if the white list is being used (only sends to people on this list)
107
+ # and this phone number is not on the list
108
+ def check_white_list!(phone_number)
109
+ if self.class.config[:white_list] && !phone_number.white_list?
110
+ raise SmsOnRails::SmsError.new("Phone number #{phone_number.human_display} is not in white list")
111
+ end
112
+ end
113
+
114
+ # Raise an exception if this phone_number is marked as do not send
115
+ def check_do_not_send!(phone_number)
116
+ if phone_number.do_not_send?
117
+ raise SmsOnRails::SmsError.new("Phone number #{phone_number.human_display} do not send is set:#{phone_number.do_not_send}")
118
+ end
119
+ end
120
+
121
+ # Raise exception if invalid data is entered
122
+ def assert_message_options!(phone_number, message, options)
123
+ raise SmsOnRails::SmsError.new("Invalid or undefined phone number: #{phone_number.human_display}") unless phone_number && phone_number.valid?
124
+ raise SmsOnRails::SmsError.new("No message specified") unless message
125
+ raise SmsOnRails::SmsError.new("Message is too long. #{message}") if message.length > self.class.max_characters
126
+ check_white_list!(phone_number)
127
+ check_do_not_send!(phone_number)
128
+ end
129
+
130
+ # return the appropriate exception class
131
+ # Return a fatal error if the block returns true and a non fatal if the
132
+ # block returns false
133
+ def sms_error_class(&block)#:nodoc
134
+ if yield
135
+ SmsOnRails::FatalSmsError
136
+ else
137
+ SmsOnRails::SmsError
138
+ end
139
+ end
140
+
141
+ # The class used for phone numbers
142
+ # can be overwritten and specified in environment.rb
143
+ def phone_klass
144
+ config[:phone_klass]||SmsOnRails::PhoneNumber
145
+ end
146
+
147
+ class << self
148
+
149
+ def set_default_service_provider(provider)
150
+ return if $gems_rake_task
151
+ provider_instance = get_service_provider(provider)
152
+ raise SmsOnRails::FatalSmsError.new("Cannot set default provider to #{provider}. Check that support for #{provider} exists.") unless provider_instance
153
+ self.default_service_provider = provider_instance
154
+ end
155
+
156
+ def max_characters
157
+ self.config[:max_characters]||140
158
+ end
159
+
160
+ # Name of service provider (downcase no spaces)
161
+ def name
162
+ @name ||= self.to_s.demodulize.underscore
163
+ end
164
+
165
+ # Human name of provider
166
+ def human_name
167
+ @human_name ||= self.to_s.demodulize.titleize
168
+ end
169
+
170
+ # Hash map of provider_id to the provider
171
+ def provider_map
172
+ @provider_map ||= provider_list.inject({}) do |map, klass|
173
+ map[klass.provider_id] = klass unless klass.nil?
174
+ map
175
+ end
176
+ end
177
+
178
+ #List of all providers available
179
+ def provider_list
180
+ @provider_list ||= subclasses.inject([]) do |list, klass_name|
181
+ klass = klass_name.constantize
182
+ begin
183
+ list << klass.instance
184
+ rescue LoadError => exc
185
+ logger.error "#{klass} load error: #{exc}"
186
+ end
187
+ list
188
+ end
189
+ end
190
+
191
+ # Locate the service provider by provider id
192
+ # Example: SmsOnRails::ServiceProviders::Base.provider_by_id 1
193
+ def provider_by_id(provider_id)
194
+ provider_map[provider_id.to_i] if provider_id.to_i > 0
195
+ end
196
+
197
+ # Locate the service provider by name or symbol
198
+ # Example: SmsOnRails::ServiceProviders::Base.provider_by_name :dummy
199
+ def provider_by_name(provider_name)
200
+ key = provider_name.to_s.downcase
201
+ provider_list.detect{|p| key == p.name || provider_name == p.human_name}
202
+ end
203
+
204
+ # Locate the service provider object by provider_id, string, symbol, or ServiceProvider object
205
+ # Defaults to the default service provider
206
+ # SmsOnRails::ServiceProviders::Base.get_service_provider :dummy
207
+ # SmsOnRails::ServiceProviders::Base.get_service_provider 1
208
+ # SmsOnRails::ServiceProviders::Base.get_service_provider SmsOnRails::ServiceProviders::Dummy
209
+ def get_service_provider(provider)
210
+ case provider.class.to_s
211
+ when 'Fixnum' then provider_by_id(provider)
212
+ when 'String' then (provider.to_i > 0 ? provider_by_id(provider) : provider_by_name(provider))
213
+ when 'Symbol' then provider_by_name(provider)
214
+ when 'NilClass' then nil
215
+ else
216
+ provider
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,52 @@
1
+ module SmsOnRails
2
+ module ServiceProviders
3
+ class Clickatell < Base
4
+ self.provider_id = 1
5
+
6
+ FATAL_ERROR_CODES = %w(105 114) unless defined?(FATAL_ERROR_CODES)
7
+
8
+ def initialize
9
+ begin
10
+ require 'clickatell'
11
+ rescue LoadError => exc
12
+ raise LoadError.new(exc.to_s + " Please make sure the clickatell gem is installed.")
13
+ end
14
+ super
15
+ end
16
+
17
+ def ping
18
+ result = invoke_clickatell{ api.ping(nil) }
19
+ result.is_a?(Net::HTTPOK)
20
+ end
21
+
22
+ def authenticate
23
+ invoke_clickatell{ api }
24
+ end
25
+
26
+ #Send a message without validation
27
+ # * phone_number - phone number string digits
28
+ # * message - the message text
29
+ # * options - anything else
30
+ def send_message(phone_number, message, options={})
31
+ unique_id = invoke_clickatell{ api.send_message(phone_number, message) }
32
+ {:unique_id => unique_id}
33
+ end
34
+
35
+ protected
36
+
37
+ #Get the api key
38
+ def api#:nodoc:
39
+ @@api ||= ::Clickatell::API.authenticate(config[:api_id], config[:user_name], config[:password])
40
+ end
41
+
42
+ # wrap this method around all clickatell calls
43
+ # if anything other than a 105 is thrown, reraise FatalSmsError
44
+ def invoke_clickatell(&block)#:nodoc
45
+ yield
46
+
47
+ rescue ::Clickatell::API::Error => cae
48
+ raise sms_error_class{ !(FATAL_ERROR_CODES.include?(cae.code)) }.new("Clickatell Error:#{cae.code}:#{cae.message}", cae.code)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ module SmsOnRails
2
+ module ServiceProviders
3
+ class Dummy < Base
4
+
5
+ self.provider_id = 2
6
+
7
+ def ping
8
+ true
9
+ end
10
+
11
+ def send_message(phone_number, message, options={})
12
+ response = {:unique_id => "#{$$}:#{Time.now.strftime('%Y%m%d%H%M%SZ')}.#{rand(1000)}"}
13
+ response
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ SmsOnRails::ServiceProviders::Base.default_service_provider ||= SmsOnRails::ServiceProviders::Dummy.instance
@@ -0,0 +1,68 @@
1
+ #Require all the email gateway support files
2
+ Dir.glob(File.dirname(__FILE__) + '/email_gateway_support/*.rb').each {|f| require f }
3
+
4
+ module SmsOnRails
5
+ module ServiceProviders
6
+ class EmailGateway < Base
7
+ self.provider_id = 3
8
+
9
+ def ping; true; end
10
+
11
+ def mailer_klass; self.class.mailer_klass; end
12
+
13
+ def requires_carrier?; true; end
14
+
15
+ # Email Gateway Send message
16
+ #
17
+ # send_message('2065551234@txt.att.net', 'My message')
18
+ # send_message('12065551234', 'my message', :carrier => 'Verizon')
19
+ # send_message(['12065551234', '4125556667'], 'my message', :carrier => 'Verizon')
20
+ #
21
+ # === Params
22
+ # * +phone_number+ - can be one of the following
23
+ # sms_email_address. Example: '2065551234@txt.att.net'
24
+ # array of sms_email_addresses: ['2065551234@txt.att.net', '2065552234@txt.att.net']
25
+ # * +message+ - the text message to send
26
+ #
27
+ # ===Options
28
+ # * <tt>:sender</tt> - email address of the sender overrides default
29
+ # * <tt>:bcc</tt> - email_address or array of email_addresses to blind carbon copy
30
+ def send_message(phone_text, message, options={})
31
+ mailer_klass.deliver_sms_through_gateway(phone_text, message, self.class.config.merge(options))
32
+ {}
33
+ end
34
+
35
+ protected
36
+
37
+ #the formate for email is the sms email address
38
+ def format_phone_number(phone_number)
39
+ phone_number.sms_email_address
40
+ end
41
+
42
+ # additional check to make sure the sms_email_address is valid
43
+ def assert_message_options!(phone_number, message, options)
44
+ super(phone_number, message, options)
45
+ unless phone_number.carrier || phone_number.sms_email_address
46
+ raise EmailGatewayInvalidSms.phone_error(phone_number)
47
+ end
48
+ end
49
+
50
+ # override to append the carrier if specified as part of the phone number
51
+ # or as a separate option
52
+ def find_or_create_phone_number(phone_text, options={})
53
+ phone = phone_klass.find_by_sms_email_address(phone_text)
54
+ phone ||= super(phone_text, options)
55
+ phone.assign_carrier(options[:carrier]) if options[:carrier]
56
+ phone
57
+ end
58
+
59
+ class << self
60
+ # The mailer class to use that can be specified in the config options for :mailer_klass
61
+ def mailer_klass#:nodoc:
62
+ @mailer_klass ||= config[:mailer_klass]||SmsOnRails::ServiceProviders::EmailGatewaySupport::SmsMailer
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,20 @@
1
+ module SmsOnRails
2
+ module ServiceProviders
3
+ class EmailGatewayInvalidSms < SmsOnRails::SmsError
4
+ def self.phone_error(phone_number)
5
+ new(self.phone_message(phone_number))
6
+ end
7
+ def self.phone_message(phone_number)
8
+ msg = "Invalid phone number #{phone_number.human_display if phone_number}. "
9
+ msg << "Please specify the digits and the option :carrier, "
10
+ msg << "or specify the full email address like 2065551234@txt.att.net"
11
+ end
12
+ end
13
+
14
+ class EmailGatewayInvalidCarrier < EmailGatewayInvalidSms
15
+ def self.phone_message(phone_number)
16
+ msg = "The carrier for #{phone_number.human_display if phone_number} is invalid."
17
+ end
18
+ end
19
+ end
20
+ end