sms_carrier 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +86 -0
- data/Rakefile +23 -0
- data/lib/sms_carrier.rb +22 -0
- data/lib/sms_carrier/base.rb +292 -0
- data/lib/sms_carrier/delivery_job.rb +13 -0
- data/lib/sms_carrier/delivery_methods.rb +67 -0
- data/lib/sms_carrier/log_subscriber.rb +33 -0
- data/lib/sms_carrier/message_delivery.rb +93 -0
- data/lib/sms_carrier/railtie.rb +45 -0
- data/lib/sms_carrier/sms.rb +126 -0
- data/lib/sms_carrier/test_carrier.rb +21 -0
- data/lib/sms_carrier/test_case.rb +85 -0
- data/lib/sms_carrier/test_helper.rb +66 -0
- data/lib/sms_carrier/version.rb +3 -0
- data/sms_carrier.gemspec +27 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8856860fad36aa1fa2eef28c277106e3c3e8a361
|
4
|
+
data.tar.gz: 261bcc32724a7d9c75eae9217208800a1117881f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0f5dccb9d50b80a849099fb1df1dc732ae78a6ca50c4d7e750e725472ef1debe2b149845b8cd46e78d881b017473df20445e8d9979d35fe4910e017d81ec39d5
|
7
|
+
data.tar.gz: 0a144ff33491e6ae9bd781010eacb4cbf76e347b670a886b5c9102e5dd755b8c31a3365ccef590b1f925378bae702129e3970ae112fb669350afdfcb8da85363
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Chen Yi-Cyuan
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# SMS Carrier
|
2
|
+
|
3
|
+
[![Build Status](https://api.travis-ci.org/emn178/sms_carrier.png)](https://travis-ci.org/emn178/sms_carrier)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/emn178/sms_carrier/badge.svg?branch=master)](https://coveralls.io/r/emn178/sms_carrier?branch=master)
|
5
|
+
|
6
|
+
SMS Carrier is a framework for designing SMS service layers. These layers are used to consolidate code for sending out confirmation token, and any other use case that requires a written notification to either a person or another system.
|
7
|
+
|
8
|
+
SMS Carrier is in essence a wrapper around Action Controller. It provides a way to make SMSes using templates in the same way that Action Controller renders views using templates.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'sms_carrier'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
gem install sms_carrier
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
### Sending SMSes
|
28
|
+
The framework works by initializing any instance variables you want to be available in the SMS template, followed by a call to sms to deliver the SMS.
|
29
|
+
|
30
|
+
This can be as simple as:
|
31
|
+
```Ruby
|
32
|
+
class Notifier < SmsCarrier::Base
|
33
|
+
default from: '+886987654321'
|
34
|
+
|
35
|
+
def welcome(recipient, token)
|
36
|
+
@token = token
|
37
|
+
sms(to: recipient)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
The body of the SMS is created by using an Action View template (regular ERB) that has the instance variables that are declared in the carrier action.
|
42
|
+
|
43
|
+
So the corresponding body template for the method above could look like this:
|
44
|
+
```
|
45
|
+
Your token is <%= @token %>, please confirm your phone number
|
46
|
+
```
|
47
|
+
If the token was given as “1234”, the SMS generated would look like this:
|
48
|
+
```
|
49
|
+
Your token is 1234, please confirm your phone number
|
50
|
+
```
|
51
|
+
In order to send SMSes, you simply call the method and then call deliver_now on the return value.
|
52
|
+
|
53
|
+
Calling the method returns a Sms object:
|
54
|
+
```Ruby
|
55
|
+
message = Notifier.welcome("1234") # => Returns a SmsCarrier::Sms object
|
56
|
+
message.deliver_now # => delivers the SMS
|
57
|
+
```
|
58
|
+
Or you can just chain the methods together like:
|
59
|
+
```Ruby
|
60
|
+
Notifier.welcome("1234").deliver_now # Creates the SMS and sends it immediately
|
61
|
+
```
|
62
|
+
Or you can send SMS without carrier and template:
|
63
|
+
```Ruby
|
64
|
+
SmsCarrier::Base.sms(from: "+886987654321", to: "+886912345678", body: "Your token is #{@token}").deliver_now
|
65
|
+
```
|
66
|
+
|
67
|
+
### Setting defaults
|
68
|
+
It is possible to set default values that will be used in every method in your SMS Carrier class. To implement this functionality, you just call the public class method default which you get for free from SmsCarrier::Base. This method accepts a Hash as the parameter. You can use any of the options, SMS messages have, like :from as the key. You can also pass in a string as the key, like “Content-Type”, but SMS Carrier does this out of the box for you, so you won't need to worry about that. Finally, it is also possible to pass in a Proc that will get evaluated when it is needed.
|
69
|
+
|
70
|
+
Note that every value you set with this method will get overwritten if you use the same key in your carrier method.
|
71
|
+
|
72
|
+
Example:
|
73
|
+
```Ruby
|
74
|
+
class AuthenticationCarrier < SmsCarrier::Base
|
75
|
+
default from: "+886987654321", body: Proc.new { "SMS was generated at #{Time.now}" }
|
76
|
+
.....
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
## License
|
81
|
+
|
82
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
83
|
+
|
84
|
+
## Contact
|
85
|
+
The project's website is located at https://github.com/emn178/sms_carrier
|
86
|
+
Author: emn178@gmail.com
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
desc "Default Task"
|
6
|
+
task default: [ :test ]
|
7
|
+
|
8
|
+
# Run the unit tests
|
9
|
+
Rake::TestTask.new { |t|
|
10
|
+
t.libs << "test"
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.warning = true
|
13
|
+
t.verbose = true
|
14
|
+
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
|
15
|
+
}
|
16
|
+
|
17
|
+
namespace :test do
|
18
|
+
task :isolated do
|
19
|
+
Dir.glob("test/**/*_test.rb").all? do |file|
|
20
|
+
sh(Gem.ruby, '-w', '-Ilib:test', file)
|
21
|
+
end or raise "Failures"
|
22
|
+
end
|
23
|
+
end
|
data/lib/sms_carrier.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'abstract_controller'
|
2
|
+
require 'sms_carrier/version'
|
3
|
+
|
4
|
+
# Common Active Support usage in SmsCarrier
|
5
|
+
require 'active_support/rails'
|
6
|
+
require 'active_support/core_ext/class'
|
7
|
+
require 'active_support/core_ext/module/attr_internal'
|
8
|
+
require 'active_support/core_ext/string/inflections'
|
9
|
+
require 'active_support/lazy_load_hooks'
|
10
|
+
|
11
|
+
module SmsCarrier
|
12
|
+
extend ::ActiveSupport::Autoload
|
13
|
+
|
14
|
+
autoload :Base
|
15
|
+
autoload :DeliveryMethods
|
16
|
+
autoload :TestCase
|
17
|
+
autoload :TestHelper
|
18
|
+
autoload :MessageDelivery
|
19
|
+
autoload :DeliveryJob
|
20
|
+
end
|
21
|
+
|
22
|
+
require "sms_carrier/railtie" if defined? ::Rails
|
@@ -0,0 +1,292 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
require 'active_support/core_ext/hash/except'
|
3
|
+
require 'active_support/core_ext/module/anonymous'
|
4
|
+
|
5
|
+
require 'sms_carrier/sms'
|
6
|
+
require 'sms_carrier/log_subscriber'
|
7
|
+
|
8
|
+
module SmsCarrier
|
9
|
+
class Base < AbstractController::Base
|
10
|
+
include DeliveryMethods
|
11
|
+
|
12
|
+
abstract!
|
13
|
+
|
14
|
+
include AbstractController::Rendering
|
15
|
+
|
16
|
+
include AbstractController::Logger
|
17
|
+
include AbstractController::Helpers
|
18
|
+
include AbstractController::Translation
|
19
|
+
include AbstractController::AssetPaths
|
20
|
+
include AbstractController::Callbacks
|
21
|
+
|
22
|
+
include ActionView::Layouts
|
23
|
+
|
24
|
+
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]
|
25
|
+
|
26
|
+
def _protected_ivars # :nodoc:
|
27
|
+
PROTECTED_IVARS
|
28
|
+
end
|
29
|
+
|
30
|
+
private_class_method :new #:nodoc:
|
31
|
+
|
32
|
+
class_attribute :default_params
|
33
|
+
self.default_params = {}.freeze
|
34
|
+
|
35
|
+
class << self
|
36
|
+
# Register one or more Observers which will be notified when SMS is delivered.
|
37
|
+
def register_observers(*observers)
|
38
|
+
observers.flatten.compact.each { |observer| register_observer(observer) }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Register one or more Interceptors which will be called before SMS is sent.
|
42
|
+
def register_interceptors(*interceptors)
|
43
|
+
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Register an Observer which will be notified when SMS is delivered.
|
47
|
+
# Either a class, string or symbol can be passed in as the Observer.
|
48
|
+
# If a string or symbol is passed in it will be camelized and constantized.
|
49
|
+
def register_observer(observer)
|
50
|
+
delivery_observer = case observer
|
51
|
+
when String, Symbol
|
52
|
+
observer.to_s.camelize.constantize
|
53
|
+
else
|
54
|
+
observer
|
55
|
+
end
|
56
|
+
|
57
|
+
Sms.register_observer(delivery_observer)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Register an Interceptor which will be called before SMS is sent.
|
61
|
+
# Either a class, string or symbol can be passed in as the Interceptor.
|
62
|
+
# If a string or symbol is passed in it will be camelized and constantized.
|
63
|
+
def register_interceptor(interceptor)
|
64
|
+
delivery_interceptor = case interceptor
|
65
|
+
when String, Symbol
|
66
|
+
interceptor.to_s.camelize.constantize
|
67
|
+
else
|
68
|
+
interceptor
|
69
|
+
end
|
70
|
+
|
71
|
+
Sms.register_interceptor(delivery_interceptor)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns the name of current carrier.
|
75
|
+
# If this is an anonymous carrier, this method will return +anonymous+ instead.
|
76
|
+
def carrier_name
|
77
|
+
@carrier_name ||= anonymous? ? "anonymous" : name.underscore
|
78
|
+
end
|
79
|
+
# Allows to set the name of current carrier.
|
80
|
+
attr_writer :carrier_name
|
81
|
+
alias :controller_path :carrier_name
|
82
|
+
|
83
|
+
# Sets the defaults through app configuration:
|
84
|
+
#
|
85
|
+
# config.sms_carrier.default(from: "+886987654321")
|
86
|
+
#
|
87
|
+
# Aliased by ::default_options=
|
88
|
+
def default(value = nil)
|
89
|
+
self.default_params = default_params.merge(value).freeze if value
|
90
|
+
default_params
|
91
|
+
end
|
92
|
+
# Allows to set defaults through app configuration:
|
93
|
+
#
|
94
|
+
# config.sms_carrier.default_options = { from: "+886987654321" }
|
95
|
+
alias :default_options= :default
|
96
|
+
|
97
|
+
# Wraps an SMS delivery inside of <tt>ActiveSupport::Notifications</tt> instrumentation.
|
98
|
+
#
|
99
|
+
# This method is actually called by the <tt>Sms</tt> object itself
|
100
|
+
# through a callback when you call <tt>:deliver</tt> on the <tt>Sms</tt>,
|
101
|
+
# calling +deliver_sms+ directly and passing a <tt>Sms</tt> will do
|
102
|
+
# nothing except tell the logger you sent the SMS.
|
103
|
+
def deliver_sms(sms) #:nodoc:
|
104
|
+
ActiveSupport::Notifications.instrument("deliver.sms_carrier") do |payload|
|
105
|
+
set_payload_for_sms(payload, sms)
|
106
|
+
yield # Let Sms do the delivery actions
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def respond_to?(method, include_private = false) #:nodoc:
|
111
|
+
super || action_methods.include?(method.to_s)
|
112
|
+
end
|
113
|
+
|
114
|
+
protected
|
115
|
+
|
116
|
+
def set_payload_for_sms(payload, sms) #:nodoc:
|
117
|
+
payload[:carrier] = name
|
118
|
+
payload[:to] = sms.to
|
119
|
+
payload[:from] = sms.from
|
120
|
+
payload[:sms] = sms.body
|
121
|
+
end
|
122
|
+
|
123
|
+
def method_missing(method_name, *args) #:nodoc:
|
124
|
+
if action_methods.include?(method_name.to_s)
|
125
|
+
MessageDelivery.new(self, method_name, *args)
|
126
|
+
else
|
127
|
+
super
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
attr_internal :message
|
133
|
+
|
134
|
+
# Instantiate a new carrier object. If +method_name+ is not +nil+, the carrier
|
135
|
+
# will be initialized according to the named method. If not, the carrier will
|
136
|
+
# remain uninitialized (useful when you only need to invoke the "receive"
|
137
|
+
# method, for instance).
|
138
|
+
def initialize(method_name=nil, *args)
|
139
|
+
super()
|
140
|
+
@_sms_was_called = false
|
141
|
+
@_message = Sms.new
|
142
|
+
process(method_name, *args) if method_name
|
143
|
+
end
|
144
|
+
|
145
|
+
def process(method_name, *args) #:nodoc:
|
146
|
+
payload = {
|
147
|
+
carrier: self.class.name,
|
148
|
+
action: method_name
|
149
|
+
}
|
150
|
+
|
151
|
+
ActiveSupport::Notifications.instrument("process.sms_carrier", payload) do
|
152
|
+
super
|
153
|
+
@_message = NullMessage.new unless @_sms_was_called
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class NullMessage #:nodoc:
|
158
|
+
def body; '' end
|
159
|
+
|
160
|
+
def respond_to?(string, include_all=false)
|
161
|
+
true
|
162
|
+
end
|
163
|
+
|
164
|
+
def method_missing(*args)
|
165
|
+
nil
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Returns the name of the carrier object.
|
170
|
+
def carrier_name
|
171
|
+
self.class.carrier_name
|
172
|
+
end
|
173
|
+
|
174
|
+
# Allows you to pass random and unusual options to the new <tt>SmsCarrier::Sms</tt>
|
175
|
+
# object which will add them to itself.
|
176
|
+
#
|
177
|
+
# options['X-Special-Domain-Specific-Option'] = "SecretValue"
|
178
|
+
#
|
179
|
+
# The resulting <tt>SmsCarrier::Sms</tt> will have the following in its option:
|
180
|
+
#
|
181
|
+
# X-Special-Domain-Specific-Option: SecretValue
|
182
|
+
# def options
|
183
|
+
# @_message.options
|
184
|
+
# end
|
185
|
+
|
186
|
+
def options(args = nil)
|
187
|
+
if args
|
188
|
+
@_message.options.merge!(args)
|
189
|
+
else
|
190
|
+
@_message
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# The main method that creates the message and renders the SMS templates. There are
|
195
|
+
# two ways to call this method, with a block, or without a block.
|
196
|
+
#
|
197
|
+
# It accepts a headers hash. This hash allows you to specify
|
198
|
+
# the most used headers in an SMS message, these are:
|
199
|
+
#
|
200
|
+
# * +:to+ - Who the message is destined for, can be a string of addresses, or an array
|
201
|
+
# of addresses.
|
202
|
+
# * +:from+ - Who the message is from
|
203
|
+
#
|
204
|
+
# You can set default values for any of the above headers (except +:date+)
|
205
|
+
# by using the ::default class method:
|
206
|
+
#
|
207
|
+
# class Notifier < SmsCarrier::Base
|
208
|
+
# default from: '+886987654321'
|
209
|
+
# end
|
210
|
+
#
|
211
|
+
# If you do not pass a block to the +sms+ method, it will find all
|
212
|
+
# templates in the view paths using by default the carrier name and the
|
213
|
+
# method name that it is being called from, it will then create parts for
|
214
|
+
# each of these templates intelligently, making educated guesses on correct
|
215
|
+
# content type and sequence, and return a fully prepared <tt>Sms</tt>
|
216
|
+
# ready to call <tt>:deliver</tt> on to send.
|
217
|
+
#
|
218
|
+
# For example:
|
219
|
+
#
|
220
|
+
# class Notifier < SmsCarrier::Base
|
221
|
+
# default from: 'no-reply@test.lindsaar.net'
|
222
|
+
#
|
223
|
+
# def welcome
|
224
|
+
# sms(to: 'mikel@test.lindsaar.net')
|
225
|
+
# end
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# Will look for all templates at "app/views/notifier" with name "welcome".
|
229
|
+
# If no welcome template exists, it will raise an ActionView::MissingTemplate error.
|
230
|
+
#
|
231
|
+
# However, those can be customized:
|
232
|
+
#
|
233
|
+
# sms(template_path: 'notifications', template_name: 'another')
|
234
|
+
#
|
235
|
+
# And now it will look for all templates at "app/views/notifications" with name "another".
|
236
|
+
#
|
237
|
+
# You can even render plain text directly without using a template:
|
238
|
+
#
|
239
|
+
# sms(to: '+886987654321', body: 'Hello Mikel!')
|
240
|
+
#
|
241
|
+
def sms(options = {})
|
242
|
+
return @_message if @_sms_was_called && options.blank?
|
243
|
+
|
244
|
+
m = @_message
|
245
|
+
|
246
|
+
# Call all the procs (if any)
|
247
|
+
default_values = {}
|
248
|
+
self.class.default.each do |k,v|
|
249
|
+
default_values[k] = v.is_a?(Proc) ? instance_eval(&v) : v
|
250
|
+
end
|
251
|
+
|
252
|
+
# Handle defaults
|
253
|
+
options = options.reverse_merge(default_values)
|
254
|
+
|
255
|
+
# Set configure delivery behavior
|
256
|
+
wrap_delivery_behavior!(options.delete(:delivery_method), options.delete(:delivery_method_options))
|
257
|
+
|
258
|
+
# Assign all options except body, template_name, and template_path
|
259
|
+
assignable = options.except(:body, :template_name, :template_path)
|
260
|
+
assignable.each { |k, v| m[k] = v }
|
261
|
+
|
262
|
+
# Render the templates and blocks
|
263
|
+
m.body = response(options)
|
264
|
+
@_sms_was_called = true
|
265
|
+
|
266
|
+
m
|
267
|
+
end
|
268
|
+
|
269
|
+
def response(options) #:nodoc:
|
270
|
+
if options[:body]
|
271
|
+
return options.delete(:body)
|
272
|
+
else
|
273
|
+
templates_path = options.delete(:template_path) || self.class.carrier_name
|
274
|
+
templates_name = options.delete(:template_name) || action_name
|
275
|
+
|
276
|
+
template = lookup_context.find(templates_name, templates_path)
|
277
|
+
if template.nil?
|
278
|
+
raise ActionView::MissingTemplate.new(templates_path, templates_name, templates_path, false, 'carrier')
|
279
|
+
else
|
280
|
+
return render(template: template)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# SMS do not support relative path links.
|
286
|
+
def self.supports_path?
|
287
|
+
false
|
288
|
+
end
|
289
|
+
|
290
|
+
ActiveSupport.run_load_hooks(:sms_carrier, self)
|
291
|
+
end
|
292
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'active_job'
|
2
|
+
|
3
|
+
module SmsCarrier
|
4
|
+
# The <tt>SmsCarrier::DeliveryJob</tt> class is used when you
|
5
|
+
# want to send SMSes outside of the request-response cycle.
|
6
|
+
class DeliveryJob < ActiveJob::Base # :nodoc:
|
7
|
+
queue_as { SmsCarrier::Base.deliver_later_queue_name }
|
8
|
+
|
9
|
+
def perform(sms, sms_method, delivery_method, *args) #:nodoc:
|
10
|
+
sms.constantize.public_send(sms_method, *args).send(delivery_method)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'sms_carrier/test_carrier'
|
2
|
+
|
3
|
+
module SmsCarrier
|
4
|
+
# This module handles everything related to SMS delivery, from registering
|
5
|
+
# new delivery methods to configuring the SMS object to be sent.
|
6
|
+
module DeliveryMethods
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :delivery_methods, :delivery_method
|
11
|
+
|
12
|
+
# Do not make this inheritable, because we always want it to propagate
|
13
|
+
cattr_accessor :raise_delivery_errors
|
14
|
+
self.raise_delivery_errors = true
|
15
|
+
|
16
|
+
cattr_accessor :perform_deliveries
|
17
|
+
self.perform_deliveries = true
|
18
|
+
|
19
|
+
cattr_accessor :deliver_later_queue_name
|
20
|
+
self.deliver_later_queue_name = :carriers
|
21
|
+
|
22
|
+
self.delivery_methods = {}.freeze
|
23
|
+
self.delivery_method = :test
|
24
|
+
|
25
|
+
add_delivery_method :test, TestCarrier
|
26
|
+
end
|
27
|
+
|
28
|
+
# Helpers for creating and wrapping delivery behavior, used by DeliveryMethods.
|
29
|
+
module ClassMethods
|
30
|
+
# Provides a list of SMSes that have been delivered by TestCarrier
|
31
|
+
delegate :deliveries, :deliveries=, to: TestCarrier
|
32
|
+
|
33
|
+
# Adds a new delivery method through the given class using the given
|
34
|
+
# symbol as alias and the default options supplied.
|
35
|
+
def add_delivery_method(symbol, klass, default_options = {})
|
36
|
+
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
|
37
|
+
send(:"#{symbol}_settings=", default_options)
|
38
|
+
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
|
39
|
+
end
|
40
|
+
|
41
|
+
def wrap_delivery_behavior(sms, method = nil, options = nil) # :nodoc:
|
42
|
+
method ||= delivery_method
|
43
|
+
sms.delivery_handler = self
|
44
|
+
|
45
|
+
case method
|
46
|
+
when NilClass
|
47
|
+
raise "Delivery method cannot be nil"
|
48
|
+
when Symbol
|
49
|
+
if klass = delivery_methods[method]
|
50
|
+
sms.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {}))
|
51
|
+
else
|
52
|
+
raise "Invalid delivery method #{method.inspect}"
|
53
|
+
end
|
54
|
+
else
|
55
|
+
sms.delivery_method(method)
|
56
|
+
end
|
57
|
+
|
58
|
+
sms.perform_deliveries = perform_deliveries
|
59
|
+
sms.raise_delivery_errors = raise_delivery_errors
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def wrap_delivery_behavior!(*args) # :nodoc:
|
64
|
+
self.class.wrap_delivery_behavior(message, *args)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'active_support/log_subscriber'
|
2
|
+
|
3
|
+
module SmsCarrier
|
4
|
+
# Implements the ActiveSupport::LogSubscriber for logging notifications when
|
5
|
+
# sms is delivered or received.
|
6
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
7
|
+
# An SMS was delivered.
|
8
|
+
def deliver(event)
|
9
|
+
info do
|
10
|
+
recipients = Array(event.payload[:to]).join(', ')
|
11
|
+
"\nSent SMS to #{recipients} (#{event.duration.round(1)}ms)"
|
12
|
+
end
|
13
|
+
|
14
|
+
debug { event.payload[:sms] }
|
15
|
+
end
|
16
|
+
|
17
|
+
# An SMS was generated.
|
18
|
+
def process(event)
|
19
|
+
debug do
|
20
|
+
carrier = event.payload[:carrier]
|
21
|
+
action = event.payload[:action]
|
22
|
+
"\n#{carrier}##{action}: processed outbound SMS in #{event.duration.round(1)}ms"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Use the logger configured for SmsCarrier::Base.
|
27
|
+
def logger
|
28
|
+
SmsCarrier::Base.logger
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
SmsCarrier::LogSubscriber.attach_to :sms_carrier
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module SmsCarrier
|
4
|
+
# The <tt>SmsCarrier::MessageDelivery</tt> class is used by
|
5
|
+
# <tt>SmsCarrier::Base</tt> when creating a new carrier.
|
6
|
+
# <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy
|
7
|
+
# created <tt>Sms</tt>. You can get direct access to the
|
8
|
+
# <tt>Sms</tt>, deliver the SMS or schedule the SMS to be sent
|
9
|
+
# through Active Job.
|
10
|
+
#
|
11
|
+
# Notifier.welcome(User.first) # an SmsCarrier::MessageDelivery object
|
12
|
+
# Notifier.welcome(User.first).deliver_now # sends the email
|
13
|
+
# Notifier.welcome(User.first).deliver_later # enqueue email delivery as a job through Active Job
|
14
|
+
# Notifier.welcome(User.first).message # a Sms object
|
15
|
+
class MessageDelivery < Delegator
|
16
|
+
def initialize(carrier, sms_method, *args) #:nodoc:
|
17
|
+
@carrier = carrier
|
18
|
+
@sms_method = sms_method
|
19
|
+
@args = args
|
20
|
+
end
|
21
|
+
|
22
|
+
def __getobj__ #:nodoc:
|
23
|
+
@obj ||= @carrier.send(:new, @sms_method, *@args).message
|
24
|
+
end
|
25
|
+
|
26
|
+
def __setobj__(obj) #:nodoc:
|
27
|
+
@obj = obj
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the Message object
|
31
|
+
def message
|
32
|
+
__getobj__
|
33
|
+
end
|
34
|
+
|
35
|
+
# Enqueues the SMS to be delivered through Active Job. When the
|
36
|
+
# job runs it will send the SMS using +deliver_now!+. That means
|
37
|
+
# that the message will be sent bypassing checking +perform_deliveries+
|
38
|
+
# and +raise_delivery_errors+, so use with caution.
|
39
|
+
#
|
40
|
+
# Notifier.welcome(User.first).deliver_later!
|
41
|
+
# Notifier.welcome(User.first).deliver_later!(wait: 1.hour)
|
42
|
+
# Notifier.welcome(User.first).deliver_later!(wait_until: 10.hours.from_now)
|
43
|
+
#
|
44
|
+
# Options:
|
45
|
+
#
|
46
|
+
# * <tt>:wait</tt> - Enqueue the SMS to be delivered with a delay
|
47
|
+
# * <tt>:wait_until</tt> - Enqueue the SMS to be delivered at (after) a specific date / time
|
48
|
+
# * <tt>:queue</tt> - Enqueue the SMS on the specified queue
|
49
|
+
def deliver_later!(options={})
|
50
|
+
enqueue_delivery :deliver_now!, options
|
51
|
+
end
|
52
|
+
|
53
|
+
# Enqueues the SMS to be delivered through Active Job. When the
|
54
|
+
# job runs it will send the SMS using +deliver_now+.
|
55
|
+
#
|
56
|
+
# Notifier.welcome(User.first).deliver_later
|
57
|
+
# Notifier.welcome(User.first).deliver_later(wait: 1.hour)
|
58
|
+
# Notifier.welcome(User.first).deliver_later(wait_until: 10.hours.from_now)
|
59
|
+
#
|
60
|
+
# Options:
|
61
|
+
#
|
62
|
+
# * <tt>:wait</tt> - Enqueue the SMS to be delivered with a delay.
|
63
|
+
# * <tt>:wait_until</tt> - Enqueue the SMS to be delivered at (after) a specific date / time.
|
64
|
+
# * <tt>:queue</tt> - Enqueue the SMS on the specified queue.
|
65
|
+
def deliver_later(options={})
|
66
|
+
enqueue_delivery :deliver_now, options
|
67
|
+
end
|
68
|
+
|
69
|
+
# Delivers an SMS without checking +perform_deliveries+ and +raise_delivery_errors+,
|
70
|
+
# so use with caution.
|
71
|
+
#
|
72
|
+
# Notifier.welcome(User.first).deliver_now!
|
73
|
+
#
|
74
|
+
def deliver_now!
|
75
|
+
message.deliver!
|
76
|
+
end
|
77
|
+
|
78
|
+
# Delivers an SMS:
|
79
|
+
#
|
80
|
+
# Notifier.welcome(User.first).deliver_now
|
81
|
+
#
|
82
|
+
def deliver_now
|
83
|
+
message.deliver
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def enqueue_delivery(delivery_method, options={})
|
89
|
+
args = @carrier.name, @sms_method.to_s, delivery_method.to_s, *@args
|
90
|
+
SmsCarrier::DeliveryJob.set(options).perform_later(*args)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'active_job/railtie'
|
2
|
+
require "sms_carrier"
|
3
|
+
require "rails"
|
4
|
+
require "abstract_controller/railties/routes_helpers"
|
5
|
+
|
6
|
+
module SmsCarrier
|
7
|
+
class Railtie < Rails::Railtie # :nodoc:
|
8
|
+
config.sms_carrier = ActiveSupport::OrderedOptions.new
|
9
|
+
config.eager_load_namespaces << SmsCarrier
|
10
|
+
|
11
|
+
initializer "sms_carrier.logger" do
|
12
|
+
ActiveSupport.on_load(:sms_carrier) { self.logger ||= Rails.logger }
|
13
|
+
end
|
14
|
+
|
15
|
+
initializer "sms_carrier.set_configs" do |app|
|
16
|
+
paths = app.config.paths
|
17
|
+
options = app.config.sms_carrier
|
18
|
+
|
19
|
+
options.assets_dir ||= paths["public"].first
|
20
|
+
options.javascripts_dir ||= paths["public/javascripts"].first
|
21
|
+
options.stylesheets_dir ||= paths["public/stylesheets"].first
|
22
|
+
|
23
|
+
# make sure readers methods get compiled
|
24
|
+
options.asset_host ||= app.config.asset_host
|
25
|
+
options.relative_url_root ||= app.config.relative_url_root
|
26
|
+
|
27
|
+
ActiveSupport.on_load(:sms_carrier) do
|
28
|
+
include AbstractController::UrlFor
|
29
|
+
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes, false)
|
30
|
+
include app.routes.mounted_helpers
|
31
|
+
|
32
|
+
register_interceptors(options.delete(:interceptors))
|
33
|
+
register_observers(options.delete(:observers))
|
34
|
+
|
35
|
+
options.each { |k,v| send("#{k}=", v) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
initializer "sms_carrier.compile_config_methods" do
|
40
|
+
ActiveSupport.on_load(:sms_carrier) do
|
41
|
+
config.compile_methods! if config.respond_to?(:compile_methods!)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module SmsCarrier
|
2
|
+
class Sms
|
3
|
+
attr_accessor :body, :from, :to, :options, :perform_deliveries, :raise_delivery_errors, :delivery_handler
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@options = {}
|
7
|
+
@perform_deliveries = true
|
8
|
+
@raise_delivery_errors = true
|
9
|
+
@to = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](name)
|
13
|
+
options[name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(name, value)
|
17
|
+
if name.to_s == 'body'
|
18
|
+
self.body = value
|
19
|
+
elsif name.to_s == 'from'
|
20
|
+
self.from = value
|
21
|
+
elsif name.to_s == 'to'
|
22
|
+
self.to = value
|
23
|
+
else
|
24
|
+
options[name] = value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to( val = nil )
|
29
|
+
if val.nil?
|
30
|
+
@to
|
31
|
+
elsif val.is_a? Array
|
32
|
+
@to = @to + val
|
33
|
+
elsif !@to.include? val
|
34
|
+
@to << val
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def to=( val )
|
39
|
+
to(val)
|
40
|
+
end
|
41
|
+
|
42
|
+
def inform_observers
|
43
|
+
Sms.inform_observers(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def inform_interceptors
|
47
|
+
Sms.inform_interceptors(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def deliver
|
51
|
+
inform_interceptors
|
52
|
+
if delivery_handler
|
53
|
+
delivery_handler.deliver_sms(self) { do_delivery }
|
54
|
+
else
|
55
|
+
do_delivery
|
56
|
+
end
|
57
|
+
inform_observers
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def deliver!
|
62
|
+
inform_interceptors
|
63
|
+
response = delivery_method.deliver!(self)
|
64
|
+
inform_observers
|
65
|
+
delivery_method.settings[:return_response] ? response : self
|
66
|
+
end
|
67
|
+
|
68
|
+
def delivery_method(method = nil, settings = {})
|
69
|
+
unless method
|
70
|
+
@delivery_method
|
71
|
+
else
|
72
|
+
@delivery_method = method.new(settings)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
buffer = ''
|
78
|
+
options.each do |key, value|
|
79
|
+
buffer += "#{key}: #{value}\n"
|
80
|
+
end
|
81
|
+
buffer += "From: #{from}\n"
|
82
|
+
buffer += "To: #{to}\n"
|
83
|
+
buffer += "Body: #{body}\n"
|
84
|
+
buffer
|
85
|
+
end
|
86
|
+
|
87
|
+
@@delivery_notification_observers = []
|
88
|
+
@@delivery_interceptors = []
|
89
|
+
|
90
|
+
def self.register_observer(observer)
|
91
|
+
unless @@delivery_notification_observers.include?(observer)
|
92
|
+
@@delivery_notification_observers << observer
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.register_interceptor(interceptor)
|
97
|
+
unless @@delivery_interceptors.include?(interceptor)
|
98
|
+
@@delivery_interceptors << interceptor
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.inform_observers(sms)
|
103
|
+
@@delivery_notification_observers.each do |observer|
|
104
|
+
observer.delivered_sms(sms)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.inform_interceptors(sms)
|
109
|
+
@@delivery_interceptors.each do |interceptor|
|
110
|
+
interceptor.delivering_sms(sms)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def do_delivery
|
117
|
+
begin
|
118
|
+
if perform_deliveries
|
119
|
+
delivery_method.deliver!(self)
|
120
|
+
end
|
121
|
+
rescue => e
|
122
|
+
raise e if raise_delivery_errors
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SmsCarrier
|
2
|
+
class TestCarrier
|
3
|
+
attr_accessor :settings
|
4
|
+
|
5
|
+
def initialize(settings)
|
6
|
+
self.settings = settings
|
7
|
+
end
|
8
|
+
|
9
|
+
def deliver!(sms)
|
10
|
+
TestCarrier.deliveries << sms
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.deliveries
|
14
|
+
@@deliveries ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.deliveries=(val)
|
18
|
+
@@deliveries = val
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'active_support/test_case'
|
2
|
+
|
3
|
+
module SmsCarrier
|
4
|
+
class NonInferrableCarrierError < ::StandardError
|
5
|
+
def initialize(name)
|
6
|
+
super "Unable to determine the carrier to test from #{name}. " +
|
7
|
+
"You'll need to specify it using tests YourCarrier in your " +
|
8
|
+
"test case definition"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class TestCase < ActiveSupport::TestCase
|
13
|
+
module Behavior
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
include ActiveSupport::Testing::ConstantLookup
|
17
|
+
include TestHelper
|
18
|
+
|
19
|
+
included do
|
20
|
+
class_attribute :_carrier_class
|
21
|
+
setup :initialize_test_deliveries
|
22
|
+
setup :set_expected_sms
|
23
|
+
teardown :restore_test_deliveries
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def tests(carrier)
|
28
|
+
case carrier
|
29
|
+
when String, Symbol
|
30
|
+
self._carrier_class = carrier.to_s.camelize.constantize
|
31
|
+
when Module
|
32
|
+
self._carrier_class = carrier
|
33
|
+
else
|
34
|
+
raise NonInferrableCarrierError.new(carrier)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def carrier_class
|
39
|
+
if carrier = self._carrier_class
|
40
|
+
carrier
|
41
|
+
else
|
42
|
+
tests determine_default_carrier(name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def determine_default_carrier(name)
|
47
|
+
carrier = determine_constant_from_test_name(name) do |constant|
|
48
|
+
Class === constant && constant < SmsCarrier::Base
|
49
|
+
end
|
50
|
+
raise NonInferrableCarrierError.new(name) if carrier.nil?
|
51
|
+
carrier
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def initialize_test_deliveries # :nodoc:
|
58
|
+
set_delivery_method :test
|
59
|
+
@old_perform_deliveries = SmsCarrier::Base.perform_deliveries
|
60
|
+
SmsCarrier::Base.perform_deliveries = true
|
61
|
+
end
|
62
|
+
|
63
|
+
def restore_test_deliveries # :nodoc:
|
64
|
+
restore_delivery_method
|
65
|
+
SmsCarrier::Base.perform_deliveries = @old_perform_deliveries
|
66
|
+
SmsCarrier::Base.deliveries.clear
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_delivery_method(method) # :nodoc:
|
70
|
+
@old_delivery_method = SmsCarrier::Base.delivery_method
|
71
|
+
SmsCarrier::Base.delivery_method = method
|
72
|
+
end
|
73
|
+
|
74
|
+
def restore_delivery_method # :nodoc:
|
75
|
+
SmsCarrier::Base.delivery_method = @old_delivery_method
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_expected_sms # :nodoc:
|
79
|
+
@expected = Sms.new
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
include Behavior
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'active_job'
|
2
|
+
|
3
|
+
module SmsCarrier
|
4
|
+
# Provides helper methods for testing SmsCarrier, including #assert_smses
|
5
|
+
# and #assert_no_smses.
|
6
|
+
module TestHelper
|
7
|
+
include ActiveJob::TestHelper
|
8
|
+
|
9
|
+
# Asserts that the number of SMSes sent matches the given number.
|
10
|
+
#
|
11
|
+
# def test_smses
|
12
|
+
# assert_smses 0
|
13
|
+
# ContactCarrier.welcome.deliver_now
|
14
|
+
# assert_smses 1
|
15
|
+
# ContactCarrier.welcome.deliver_now
|
16
|
+
# assert_smses 2
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# If a block is passed, that block should cause the specified number of
|
20
|
+
# SMSes to be sent.
|
21
|
+
#
|
22
|
+
# def test_smses_again
|
23
|
+
# assert_smses 1 do
|
24
|
+
# ContactCarrier.welcome.deliver_now
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# assert_smses 2 do
|
28
|
+
# ContactCarrier.welcome.deliver_now
|
29
|
+
# ContactCarrier.welcome.deliver_now
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
def assert_smses(number)
|
33
|
+
if block_given?
|
34
|
+
original_count = SmsCarrier::Base.deliveries.size
|
35
|
+
yield
|
36
|
+
new_count = SmsCarrier::Base.deliveries.size
|
37
|
+
assert_equal number, new_count - original_count, "#{number} SMSes expected, but #{new_count - original_count} were sent"
|
38
|
+
else
|
39
|
+
assert_equal number, SmsCarrier::Base.deliveries.size
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Assert that no SMSes have been sent.
|
44
|
+
#
|
45
|
+
# def test_smses
|
46
|
+
# assert_no_smses
|
47
|
+
# ContactCarrier.welcome.deliver_now
|
48
|
+
# assert_smses 1
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# If a block is passed, that block should not cause any SMSes to be sent.
|
52
|
+
#
|
53
|
+
# def test_smses_again
|
54
|
+
# assert_no_smses do
|
55
|
+
# # No SMSes should be sent from this block
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# Note: This assertion is simply a shortcut for:
|
60
|
+
#
|
61
|
+
# assert_smses 0
|
62
|
+
def assert_no_smses(&block)
|
63
|
+
assert_smses 0, &block
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/sms_carrier.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sms_carrier/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sms_carrier"
|
8
|
+
spec.version = SmsCarrier::VERSION
|
9
|
+
spec.authors = ["Chen Yi-Cyuan"]
|
10
|
+
spec.email = ["emn178@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{SMS composition and delivery framework.}
|
13
|
+
spec.description = %q{SMS on Rails. Compose, deliver and test SMSes using the familiar controller/view pattern.}
|
14
|
+
spec.homepage = "https://github.com/emn178/sms_carrier"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "actionpack", '>= 4.2.0'
|
21
|
+
spec.add_dependency 'actionview', '>= 4.2.0'
|
22
|
+
spec.add_dependency "activejob", '>= 4.2.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "mocha"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sms_carrier
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chen Yi-Cyuan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: actionview
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 4.2.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activejob
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 4.2.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 4.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mocha
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: SMS on Rails. Compose, deliver and test SMSes using the familiar controller/view
|
98
|
+
pattern.
|
99
|
+
email:
|
100
|
+
- emn178@gmail.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- ".rspec"
|
107
|
+
- ".travis.yml"
|
108
|
+
- CHANGELOG.md
|
109
|
+
- Gemfile
|
110
|
+
- LICENSE.txt
|
111
|
+
- README.md
|
112
|
+
- Rakefile
|
113
|
+
- lib/sms_carrier.rb
|
114
|
+
- lib/sms_carrier/base.rb
|
115
|
+
- lib/sms_carrier/delivery_job.rb
|
116
|
+
- lib/sms_carrier/delivery_methods.rb
|
117
|
+
- lib/sms_carrier/log_subscriber.rb
|
118
|
+
- lib/sms_carrier/message_delivery.rb
|
119
|
+
- lib/sms_carrier/railtie.rb
|
120
|
+
- lib/sms_carrier/sms.rb
|
121
|
+
- lib/sms_carrier/test_carrier.rb
|
122
|
+
- lib/sms_carrier/test_case.rb
|
123
|
+
- lib/sms_carrier/test_helper.rb
|
124
|
+
- lib/sms_carrier/version.rb
|
125
|
+
- sms_carrier.gemspec
|
126
|
+
homepage: https://github.com/emn178/sms_carrier
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
metadata: {}
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubyforge_project:
|
146
|
+
rubygems_version: 2.4.8
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: SMS composition and delivery framework.
|
150
|
+
test_files: []
|