voltron-notify 0.1.9 → 0.2.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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/Gemfile +1 -2
- data/README.md +76 -19
- data/app/jobs/voltron/sms_job.rb +1 -1
- data/app/models/voltron/notification.rb +17 -12
- data/app/models/voltron/notification/email_notification.rb +74 -36
- data/app/models/voltron/notification/sms_notification.rb +83 -55
- data/app/views/voltron/notification_mailer/notify.text.erb +1 -0
- data/lib/generators/voltron/notify/install_generator.rb +3 -5
- data/lib/voltron/config/notify.rb +1 -2
- data/lib/voltron/notify.rb +3 -1
- data/lib/voltron/notify/version.rb +1 -1
- data/voltron-notify.gemspec +10 -9
- metadata +45 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 698c826b9ee01c01f1daea0727c9f9a858bd53f9
|
4
|
+
data.tar.gz: f2f0cb75ae14e44a046c6fc5faf590a1efbdb02c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d44cbe91644c6cfcc2bb082ed1f1244969a87ed69eb1abfb1033ed968cfde3f5810c1319ad4d9295fce5a17c5b5a3fc953e7ade7c6f8bc5d51922606e7769ada
|
7
|
+
data.tar.gz: 87ab64f59e3fb1b486eb869df10263625f112198792b5d4b84981873e42442a65c854d1e3e142f1450bef3c606b9f820bc67cf6c2529f34a8b9fd9a6b0491959
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
[](https://codeclimate.com/github/ehainer/voltron-notify)
|
1
2
|
[](https://travis-ci.org/ehainer/voltron-notify)
|
2
3
|
|
3
4
|
# Voltron::Notify
|
4
5
|
|
5
6
|
Voltron Notify is an attempt to join Twilio's SMS api with Rails' default mailer functionality into one single method.
|
6
7
|
|
8
|
+
|
7
9
|
## Installation
|
8
10
|
|
9
11
|
Add this line to your application's Gemfile:
|
@@ -20,7 +22,7 @@ Or install it yourself as:
|
|
20
22
|
|
21
23
|
$ gem install voltron-notify
|
22
24
|
|
23
|
-
Then run the following to create the voltron.rb initializer
|
25
|
+
Then run the following to create the voltron.rb initializer and add the notify config:
|
24
26
|
|
25
27
|
$ rails g voltron:notify:install
|
26
28
|
|
@@ -31,12 +33,25 @@ Once installed and configured, add `notifyable` at the top of any model you wish
|
|
31
33
|
```ruby
|
32
34
|
class User < ActiveRecord::Base
|
33
35
|
|
34
|
-
notifyable
|
36
|
+
notifyable [args]
|
37
|
+
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
`notifyable` will create a `notifications` association on whatever model it is called on. The one optional argument should be a hash with default SMS/Email ActiveJob options. See "ActiveJob Integration" below for more info on how/when the default options will be used.
|
42
|
+
|
43
|
+
Defined default options should look like so:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class User < ActiveRecord::Base
|
47
|
+
|
48
|
+
notifyable sms: { wait: 10.minutes, queue: 'sms' },
|
49
|
+
email: { wait_until: 10.minutes.from_now, queue: 'mailers' }
|
35
50
|
|
36
51
|
end
|
37
52
|
```
|
38
53
|
|
39
|
-
|
54
|
+
Once done, you can utilize Voltron Notify like so:
|
40
55
|
|
41
56
|
```ruby
|
42
57
|
@user = User.find(1)
|
@@ -49,7 +64,7 @@ end
|
|
49
64
|
|
50
65
|
# First argument is email subject, remaining arguments can consist of [:to, :from] or any other param you'd like,
|
51
66
|
# they will all be converted to @variables for use in the mailer template
|
52
|
-
n.email "This is the mail subject", { to: "info@example.com"
|
67
|
+
n.email "This is the mail subject", { to: "info@example.com", param_one: "Hi there", param_two: "" }
|
53
68
|
end
|
54
69
|
```
|
55
70
|
|
@@ -86,40 +101,82 @@ Optionally, you may pass a block to the `sms` or `email` methods that allows for
|
|
86
101
|
end
|
87
102
|
```
|
88
103
|
|
104
|
+
In the case of the methods `mailer`, `method`, `arguments`, and `template`, below is each's purpose and default values
|
105
|
+
|
106
|
+
| Method | Default | Comment |
|
107
|
+
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
108
|
+
| mailer | Voltron::NotificationMailer | Defines what mailer class should be used to handle the sending of email notifications. Can be defined as the actual class name or a string, even in the format '<module>/<mailer>'. It is eventually converted to a string anyways, converted to a valid format with [classify](https://apidock.com/rails/v4.2.7/String/classify) and then instantiated with [constantize](https://apidock.com/rails/String/constantize) |
|
109
|
+
| method | :notify | Specifies what method within the defined mailer should be called. Can be a string or symbol |
|
110
|
+
| arguments | nil | Accepts an unlimited number of arguments that will be passed directly through to your mailer method Can be anything you want, so long as +mailer+.+method+() will understand it. |
|
111
|
+
| template | nil, but due to ActionMailer's default behavior, assuming +mailer+ and +method+ are not modified, it will look for `app/views/voltron/notification_mailer/notify.<format>.erb` | Overrides the default mailer template by parsing a single string argument into separate [template_path](http://guides.rubyonrails.org/action_mailer_basics.html#mailer-views) and [template_name](http://guides.rubyonrails.org/action_mailer_basics.html#mailer-views) arguments for the +mail+ method. Note that this argument should be the path relative to your applications `app/views` directory, and that it strips any file extensions. So, in the case of a view located at `app/views/my_mailer/sub_dir/special_template.html.erb` you can specify the path `my_mailer/sub_dir/special_template`. Depending on what format email you've chosen to send it will look for `special_template.<format>.erb` |
|
89
112
|
Note that both SMS and Email notifications have validations on the :to/:from fields, the email subject, and the SMS body text. Since `notifications` is an association, any errors in the actual notification content will bubble up, possibly preventing the `notifyable` model from saving. For that reason, it may be more logical to instead use a @notifyable.notifications.build / @notifyable.save syntax to properly handle errors that may occur.
|
90
113
|
|
91
|
-
Also supported are Twilio status update callbacks for SMS notifications. To enable, you can add the following to your `routes.rb` file
|
92
114
|
|
93
|
-
|
94
|
-
|
115
|
+
## ActiveJob Integration
|
116
|
+
|
117
|
+
Voltron Notify supports sending both email (via deliver_later) and SMS (via Voltron::SmsJob and perform_later). To have all notifications be handled by ActiveJob in conjunction with Sidekiq/Resque/whatever you need only set the config value `Voltron.config.notify.use_queue` to `true`. If ActiveJob is configured properly notifications will send that way instead. You may also optionally set the delay for each notification by setting the value of `Voltron.config.notify.delay` to any time value (i.e. 5.minutes, 3.months, 0.seconds)
|
118
|
+
|
119
|
+
If the value of `Voltron.config.notify.use_queue` is `true`, additional methods for sending SMS/Email can be used to further control the ActiveJob params.
|
120
|
+
|
121
|
+
For the `email` method:
|
122
|
+
|
123
|
+
| Queue Specific Methods Available | Accepts Arguments? | Behavior | Default Behavior If Not Manually Called |
|
124
|
+
|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
125
|
+
| deliver_now | No | Same as [deliver_now](https://apidock.com/rails/v4.2.7/ActionMailer/MessageDelivery/deliver_now), except this will not occur until the parent notification association is saved | Yes, if `Voltron.config.notify.use_queue` is *not* truthy. No otherwise |
|
126
|
+
| deliver_now! | No | Same as [deliver_now!](https://apidock.com/rails/ActionMailer/MessageDelivery/deliver_now%21), except this will not occur until the parent notification association is saved | No |
|
127
|
+
| deliver_later | Yes, same as what [deliver_later](https://apidock.com/rails/v4.2.7/ActionMailer/MessageDelivery/deliver_later) would accept. These arguments will come from the defaults specified when `notifyable` is called in the model. Default arguments are always overridden by the same options defined in this methods arguments. See documentation of `notifyable` and it's accepted arguments above. | Same as [deliver_later](https://apidock.com/rails/v4.2.7/ActionMailer/MessageDelivery/deliver_later), except this will not occur until the parent notification association is saved | Yes, if `Voltron.config.notify.use_queue` is truthy. No otherwise |
|
128
|
+
| deliver_later! | Yes, same as what [deliver_later!](https://apidock.com/rails/v4.2.7/ActionMailer/MessageDelivery/deliver_later%21) would accept. These arguments will come from the defaults specified when `notifyable` is called in the model. Default arguments are always overridden by the same options defined in this methods arguments. See documentation of `notifyable` and it's accepted arguments above. | Same as [deliver_later!](https://apidock.com/rails/v4.2.7/ActionMailer/MessageDelivery/deliver_later%21), except this will not occur until the parent notification association is saved | No |
|
95
129
|
|
96
|
-
allow_notification_update(options = {})
|
97
130
|
|
131
|
+
For the `sms` method:
|
132
|
+
|
133
|
+
| Queue Specific Methods Available | Accepts Arguments? | Behavior | Default Behavior If Not Manually Called |
|
134
|
+
|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
135
|
+
| deliver_now | No | When associated notification object is saved, SMS will be sent immediately (via ActiveJob's [perform_now](https://apidock.com/rails/v4.2.1/ActiveJob/Execution/ClassMethods/perform_now)), in a blocking way, aka - rails will wait until SMS is sent before continuing execution. | Yes, if `Voltron.config.notify.use_queue` is *not* truthy. No otherwise |
|
136
|
+
| deliver_later | Yes, same as what [set](https://apidock.com/rails/ActiveJob/Core/ClassMethods/set) would accept. These arguments will come from the defaults specified when `notifyable` is called in the model. Default arguments are always overridden by the same options defined in this methods arguments. See documentation of `notifyable` and it's accepted arguments above. | When associated notification object is saved, ActiveJob's [perform_later](https://apidock.com/rails/ActiveJob/Enqueuing/ClassMethods/perform_later) is called. | Yes, if `Voltron.config.notify.use_queue` is truthy. No otherwise |
|
137
|
+
|
138
|
+
|
139
|
+
Example usage:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
@user = User.find(1)
|
143
|
+
|
144
|
+
@user.notifications.build do |n|
|
145
|
+
n.sms("Immediate Message").deliver_now # Will deliver the SMS as soon as the notification is saved
|
146
|
+
n.sms("Delayed Message").deliver_later(queue: 'sms', wait_until: 10.minutes.from_now) # Will deliver the SMS via +perform_now+ with ActiveJob
|
147
|
+
n.email("Delayed Mail Subject", { param_one: "Hi there", param_two: "" }).deliver_later(wait: 5.minutes)
|
98
148
|
end
|
149
|
+
|
150
|
+
@user.save # Will finally perform the actual actions defined. Basically, +deliver_*+ does nothing until the notification is saved.
|
99
151
|
```
|
100
152
|
|
101
|
-
By default, the default options for notification updates are as follows:
|
102
153
|
|
103
|
-
|
104
|
-
# The default url path that Twilio will POST updates to. Can be anything you want so long as it's a valid URL path
|
105
|
-
path: '/notification/update'
|
154
|
+
## Updating SMS Notifications
|
106
155
|
|
107
|
-
|
108
|
-
controller: 'voltron/notification'
|
156
|
+
Also supported are Twilio status update callbacks for SMS notifications. To enable, you can add the following to your `routes.rb` file
|
109
157
|
|
110
|
-
|
111
|
-
|
158
|
+
```ruby
|
159
|
+
Rails.application.routes.draw do
|
160
|
+
|
161
|
+
allow_notification_update(options={})
|
162
|
+
|
163
|
+
end
|
112
164
|
```
|
113
165
|
|
166
|
+
Without specifying, the default options for notification updates are as follows:
|
167
|
+
|
168
|
+
| Option | Default | Comment |
|
169
|
+
|------------|----------------------|---------------------------------------------------------------------------------------------------------------------------|
|
170
|
+
| path | /notification/update | The default url path that Twilio will POST updates to. Can be anything you want so long as it's a valid URL path |
|
171
|
+
| controller | voltron/notification | The controller that will handle the notification update (in this case `app/controllers/voltron/notification_controller.rb`) |
|
172
|
+
| action | update | The controller action (method) that will perform the update |
|
173
|
+
|
114
174
|
If the value of `controller` or `action` are modified, it is assumed that whatever they point to will handle SMS notification updates. See the description column for "StatusCallback" parameter [here](https://www.twilio.com/docs/api/rest/sending-messages) for information on what Twilio will POST to the callback url. Or, take a look at this gems `app/controller/voltron/notification_controller.rb` file to see what it does by default.
|
115
175
|
|
116
176
|
In order for `allow_notification_update` to generate the correct callback url, please ensure the value of `Voltron.config.base_url` is a valid host name. By default it will attempt to obtain this information from the `:host` parameter of `Rails.application.config.action_controller.default_url_options` but if specified in the Voltron initializer that will
|
117
177
|
|
118
178
|
Note that `allow_notification_update` does nothing if running on a host matching `/localhost|127\.0\.0\.1/i` Since Twilio can't reach locally running apps to POST to, the app will not even provide Twilio with the callback url to try it. If you have a local app host named Twilio will try and POST to it, but will obviously fail for the reasons previously stated. Basically, this feature only works on remotely accessible hosts.
|
119
179
|
|
120
|
-
## Integration with ActiveJob
|
121
|
-
|
122
|
-
Voltron Notify supports sending both email (via deliver_later) and SMS (via Voltron::SmsJob and perform_later). To have all notifications be handled by ActiveJob in conjunction with Sidekiq/Resque/whatever you need only set the config value `Voltron.config.notify.use_queue` to `true`. If ActiveJob is configured properly notifications will send that way instead. You may also optionally set the delay for each notification by setting the value of `Voltron.config.notify.delay` to any time value (i.e. 5.minutes, 3.months, 0.seconds)
|
123
180
|
|
124
181
|
## Development
|
125
182
|
|
data/app/jobs/voltron/sms_job.rb
CHANGED
@@ -18,7 +18,7 @@ module Voltron
|
|
18
18
|
params = { subject: subject, notifyable.class.name.downcase => notifyable }.compact.merge(**args)
|
19
19
|
|
20
20
|
# Build the options hash from the provided arguments
|
21
|
-
options = { subject: subject }.merge(**args.select { |k,
|
21
|
+
options = { subject: subject }.merge(**args.select { |k, _| PERMITTED_ATTRIBUTES.include?(k.to_sym) })
|
22
22
|
|
23
23
|
# Build a new SMS notification object
|
24
24
|
notification_email = email_notifications.build(options)
|
@@ -27,7 +27,10 @@ module Voltron
|
|
27
27
|
notification_email.vars = params
|
28
28
|
|
29
29
|
# If a block is provided, allow calls to methods like `attach`
|
30
|
-
notification_email.instance_exec
|
30
|
+
notification_email.instance_exec(&block) if block_given?
|
31
|
+
|
32
|
+
# Return the email notification instance
|
33
|
+
notification_email
|
31
34
|
end
|
32
35
|
|
33
36
|
def sms(message, **args, &block)
|
@@ -38,7 +41,10 @@ module Voltron
|
|
38
41
|
notification_sms = sms_notifications.build(options)
|
39
42
|
|
40
43
|
# If a block is provided, allow calls to methods like `attach`
|
41
|
-
notification_sms.instance_exec
|
44
|
+
notification_sms.instance_exec(&block) if block_given?
|
45
|
+
|
46
|
+
# Return the SMS notification instance
|
47
|
+
notification_sms
|
42
48
|
end
|
43
49
|
|
44
50
|
# Called from the before_validation callback within `notifyable`
|
@@ -50,18 +56,17 @@ module Voltron
|
|
50
56
|
sms_notifications.each { |n| n.to ||= phone }
|
51
57
|
end
|
52
58
|
|
59
|
+
def self.format_output_of(json)
|
60
|
+
# Ensure returned object is an array of response hashes, for consistency
|
61
|
+
out = Array.wrap((JSON.parse(json) rescue nil)).compact
|
62
|
+
out.map { |h| h.with_indifferent_access }
|
63
|
+
end
|
64
|
+
|
53
65
|
private
|
54
66
|
|
55
67
|
def validate
|
56
|
-
# Add SMS related errors to self
|
57
|
-
sms_notifications.each do |n|
|
58
|
-
unless n.valid?
|
59
|
-
n.errors.full_messages.each { |msg| self.errors.add(:base, msg) }
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Add Email related errors to self
|
64
|
-
email_notifications.each do |n|
|
68
|
+
# Add SMS/Email related errors to self
|
69
|
+
(sms_notifications.to_a + email_notifications.to_a).each do |n|
|
65
70
|
unless n.valid?
|
66
71
|
n.errors.full_messages.each { |msg| self.errors.add(:base, msg) }
|
67
72
|
end
|
@@ -4,9 +4,11 @@ class Voltron::Notification::EmailNotification < ActiveRecord::Base
|
|
4
4
|
|
5
5
|
after_initialize :setup
|
6
6
|
|
7
|
-
before_create :
|
7
|
+
before_create :send_now, if: Proc.new { |n| !n.send(:use_queue?) || n.immediate }
|
8
8
|
|
9
|
-
|
9
|
+
# We have a separate check for +created+ because we trigger +save+ within this callback,
|
10
|
+
# and there are known issues of recursion when that is the case. See: https://github.com/rails/rails/issues/14493
|
11
|
+
after_commit :send_later, on: :create, if: Proc.new { |n| n.send(:use_queue?) && !n.created }
|
10
12
|
|
11
13
|
validates_presence_of :to, message: I18n.t('voltron.notification.email.to_blank')
|
12
14
|
|
@@ -14,41 +16,14 @@ class Voltron::Notification::EmailNotification < ActiveRecord::Base
|
|
14
16
|
|
15
17
|
attr_accessor :vars, :attachments
|
16
18
|
|
17
|
-
|
18
|
-
@request = []
|
19
|
-
@response = []
|
20
|
-
@vars ||= {}
|
21
|
-
@attachments ||= {}
|
22
|
-
@mailer_arguments = nil
|
23
|
-
self.mailer_class ||= Voltron.config.notify.default_mailer
|
24
|
-
self.mailer_method ||= Voltron.config.notify.default_method
|
25
|
-
template(Voltron.config.notify.default_template)
|
26
|
-
end
|
19
|
+
attr_accessor :created, :immediate
|
27
20
|
|
28
21
|
def request
|
29
|
-
|
30
|
-
Array.wrap({ request: (JSON.parse(request_json) rescue {}) }.with_indifferent_access[:request])
|
22
|
+
Voltron::Notification.format_output_of(request_json)
|
31
23
|
end
|
32
24
|
|
33
25
|
def response
|
34
|
-
|
35
|
-
Array.wrap({ response: (JSON.parse(response_json) rescue {}) }.with_indifferent_access[:response])
|
36
|
-
end
|
37
|
-
|
38
|
-
def after_deliver
|
39
|
-
self.request_json = @request.to_json
|
40
|
-
self.response_json = @response.to_json
|
41
|
-
end
|
42
|
-
|
43
|
-
def deliver_now
|
44
|
-
mail.deliver_now
|
45
|
-
@response << ActionMailer::Base.deliveries.last
|
46
|
-
after_deliver
|
47
|
-
end
|
48
|
-
|
49
|
-
def deliver_later
|
50
|
-
@response << mail.deliver_later(wait: Voltron.config.notify.delay)
|
51
|
-
after_deliver
|
26
|
+
Voltron::Notification.format_output_of(response_json)
|
52
27
|
end
|
53
28
|
|
54
29
|
def attach(file, name = nil)
|
@@ -58,7 +33,7 @@ class Voltron::Notification::EmailNotification < ActiveRecord::Base
|
|
58
33
|
if file.is_a?(File)
|
59
34
|
path = file.path
|
60
35
|
file.close
|
61
|
-
elsif !File.
|
36
|
+
elsif !File.exist?(path)
|
62
37
|
path = Voltron.asset.find(path)
|
63
38
|
end
|
64
39
|
|
@@ -78,13 +53,76 @@ class Voltron::Notification::EmailNotification < ActiveRecord::Base
|
|
78
53
|
end
|
79
54
|
|
80
55
|
def template(fullpath)
|
81
|
-
parts = fullpath.split(
|
56
|
+
parts = fullpath.split('/')
|
82
57
|
self.template_name = parts.pop.sub(/\.(html|text)\..*$/, '')
|
83
58
|
self.template_path = parts.join('/')
|
84
59
|
end
|
85
60
|
|
61
|
+
def deliver_now
|
62
|
+
@delivery_method = :deliver_now
|
63
|
+
@immediate = true
|
64
|
+
end
|
65
|
+
|
66
|
+
def deliver_now!
|
67
|
+
@delivery_method = :deliver_now!
|
68
|
+
@immediate = true
|
69
|
+
end
|
70
|
+
|
71
|
+
def deliver_later(options={})
|
72
|
+
@mail_options = options
|
73
|
+
@delivery_method = :deliver_later
|
74
|
+
end
|
75
|
+
|
76
|
+
def deliver_later!(options={})
|
77
|
+
@mail_options = options
|
78
|
+
@delivery_method = :deliver_later!
|
79
|
+
end
|
80
|
+
|
86
81
|
private
|
87
82
|
|
83
|
+
def send_now
|
84
|
+
mail.send(delivery_method)
|
85
|
+
@response << ActionMailer::Base.deliveries.last
|
86
|
+
after_deliver
|
87
|
+
end
|
88
|
+
|
89
|
+
def send_later
|
90
|
+
@response << mail.send(delivery_method, default_options.merge(mail_options))
|
91
|
+
after_deliver
|
92
|
+
end
|
93
|
+
|
94
|
+
def setup
|
95
|
+
@request = []
|
96
|
+
@response = []
|
97
|
+
@vars ||= {}
|
98
|
+
@attachments ||= {}
|
99
|
+
@mailer_arguments = nil
|
100
|
+
self.mailer_class ||= Voltron.config.notify.default_mailer
|
101
|
+
self.mailer_method ||= Voltron.config.notify.default_method
|
102
|
+
template(Voltron.config.notify.default_template)
|
103
|
+
end
|
104
|
+
|
105
|
+
def mail_options
|
106
|
+
@mail_options ||= {}
|
107
|
+
end
|
108
|
+
|
109
|
+
def delivery_method
|
110
|
+
@delivery_method ||= (use_queue? ? :deliver_later : :deliver_now)
|
111
|
+
end
|
112
|
+
|
113
|
+
def default_options
|
114
|
+
notification.notifyable.class.instance_variable_get('@_notification_defaults').try(:[], :email) || {}
|
115
|
+
end
|
116
|
+
|
117
|
+
def after_deliver
|
118
|
+
@created = true
|
119
|
+
@immediate = nil
|
120
|
+
@mail_options = nil
|
121
|
+
@delivery_method = nil
|
122
|
+
self.request_json = @request.to_json
|
123
|
+
self.response_json = @response.to_json
|
124
|
+
end
|
125
|
+
|
88
126
|
def use_queue?
|
89
127
|
Voltron.config.notify.use_queue
|
90
128
|
end
|
@@ -93,10 +131,10 @@ class Voltron::Notification::EmailNotification < ActiveRecord::Base
|
|
93
131
|
# If no mailer arguments, use default order of arguments as defined in Voltron::NotificationMailer.notify
|
94
132
|
if @mailer_arguments.blank?
|
95
133
|
@request << { to: to, from: from, subject: subject, template_path: template_path, template_name: template_name }.compact.merge(vars: vars, attachments: attachments)
|
96
|
-
mailer.send method, { to: to, from: from, subject: subject, template_path: template_path, template_name: template_name }.compact, vars, attachments
|
134
|
+
@outgoing = mailer.send method, { to: to, from: from, subject: subject, template_path: template_path, template_name: template_name }.compact, vars, attachments
|
97
135
|
else
|
98
136
|
@request << @mailer_arguments.compact
|
99
|
-
mailer.send method, *@mailer_arguments.compact
|
137
|
+
@outgoing = mailer.send method, *@mailer_arguments.compact
|
100
138
|
end
|
101
139
|
end
|
102
140
|
|
@@ -10,9 +10,11 @@ class Voltron::Notification::SmsNotification < ActiveRecord::Base
|
|
10
10
|
|
11
11
|
after_initialize :setup
|
12
12
|
|
13
|
-
before_create :
|
13
|
+
before_create :send_now, unless: :use_queue?
|
14
14
|
|
15
|
-
|
15
|
+
# We have a separate check for +created+ because we trigger +save+ within this callback,
|
16
|
+
# and there are known issues of recursion when that is the case. See: https://github.com/rails/rails/issues/14493
|
17
|
+
after_commit :send_later, on: :create, if: Proc.new { |n| n.send(:use_queue?) && !n.created }
|
16
18
|
|
17
19
|
validates :status, presence: false, inclusion: { in: %w( accepted queued sending sent delivered received failed undelivered unknown ), message: I18n.t('voltron.notification.sms.status_invalid') }, on: :update
|
18
20
|
|
@@ -24,60 +26,26 @@ class Voltron::Notification::SmsNotification < ActiveRecord::Base
|
|
24
26
|
|
25
27
|
validate :valid_phone_number
|
26
28
|
|
27
|
-
|
28
|
-
@request = []
|
29
|
-
@response = []
|
30
|
-
end
|
29
|
+
attr_accessor :created
|
31
30
|
|
32
31
|
def request
|
33
|
-
|
34
|
-
out = Array.wrap((JSON.parse(request_json) rescue nil)).compact
|
35
|
-
out.map { |h| h.with_indifferent_access }
|
32
|
+
Voltron::Notification.format_output_of(request_json)
|
36
33
|
end
|
37
34
|
|
38
35
|
def response
|
39
|
-
|
40
|
-
out = Array.wrap((JSON.parse(response_json) rescue nil)).compact
|
41
|
-
out.map { |h| h.with_indifferent_access }
|
42
|
-
end
|
43
|
-
|
44
|
-
def after_deliver
|
45
|
-
self.request_json = @request.to_json
|
46
|
-
self.response_json = @response.to_json
|
47
|
-
self.sid = response.first.try(:[], :sid)
|
48
|
-
self.status = response.first.try(:[], :status) || 'unknown'
|
49
|
-
|
50
|
-
# if use_queue?, meaning if this was sent via ActiveJob, we need to save ourself
|
51
|
-
# since we got to here within after_create, meaning setting the attributes alone won't cut it
|
52
|
-
self.save if use_queue?
|
36
|
+
Voltron::Notification.format_output_of(response_json)
|
53
37
|
end
|
54
38
|
|
39
|
+
# Establish that we will perform the job immediately. Will cause +send_now+ to be called instead when saved
|
55
40
|
def deliver_now
|
56
|
-
@
|
57
|
-
@
|
58
|
-
|
59
|
-
all_attachments = attachments.map(&:attachment)
|
60
|
-
|
61
|
-
# If sending more than 1 attachment, iterate through all but one attachment and send each without a body...
|
62
|
-
if all_attachments.count > 1
|
63
|
-
begin
|
64
|
-
client.messages.create({ from: from_formatted, to: to_formatted, media_url: all_attachments.shift, status_callback: callback_url }.compact)
|
65
|
-
@request << Rack::Utils.parse_nested_query(client.last_request.body)
|
66
|
-
@response << JSON.parse(client.last_response.body)
|
67
|
-
end until all_attachments.count == 1
|
68
|
-
end
|
69
|
-
|
70
|
-
# ... Then send the last attachment (if any) with the actual text body. This way we're not sending multiple SMS's with same body
|
71
|
-
client.messages.create({ from: from_formatted, to: to_formatted, body: message, media_url: all_attachments.shift, status_callback: callback_url }.compact)
|
72
|
-
@request << Rack::Utils.parse_nested_query(client.last_request.body)
|
73
|
-
@response << JSON.parse(client.last_response.body)
|
74
|
-
after_deliver
|
41
|
+
@job_options = {}
|
42
|
+
@job_method = :perform_now
|
75
43
|
end
|
76
44
|
|
77
|
-
|
78
|
-
|
79
|
-
@
|
80
|
-
|
45
|
+
# Establish that the job should be enqueued, and set the options
|
46
|
+
def deliver_later(options={})
|
47
|
+
@job_options = options
|
48
|
+
@job_method = :perform_later
|
81
49
|
end
|
82
50
|
|
83
51
|
def attach(*urls)
|
@@ -92,7 +60,6 @@ class Voltron::Notification::SmsNotification < ActiveRecord::Base
|
|
92
60
|
|
93
61
|
def valid_phone?
|
94
62
|
begin
|
95
|
-
return true if to.blank? # Handle a blank `to` separately in the errors method below
|
96
63
|
to_formatted
|
97
64
|
true
|
98
65
|
rescue => e
|
@@ -101,15 +68,76 @@ class Voltron::Notification::SmsNotification < ActiveRecord::Base
|
|
101
68
|
end
|
102
69
|
end
|
103
70
|
|
104
|
-
def callback_url
|
105
|
-
url = try(:update_voltron_notification_url, host: Voltron.config.base_url).to_s
|
106
|
-
# Don't allow local or blank urls
|
107
|
-
return nil if url.include?('localhost') || url.include?('127.0.0.1') || url.blank?
|
108
|
-
url
|
109
|
-
end
|
110
|
-
|
111
71
|
private
|
112
72
|
|
73
|
+
# Sends the SMS message
|
74
|
+
def send_now
|
75
|
+
@request = request
|
76
|
+
@response = response
|
77
|
+
|
78
|
+
all_attachments = attachments.map(&:attachment)
|
79
|
+
|
80
|
+
# If sending more than 1 attachment, iterate through all but one attachment and send each without a body...
|
81
|
+
if all_attachments.count > 1
|
82
|
+
loop do
|
83
|
+
break if all_attachments.count == 1
|
84
|
+
client.messages.create({ from: from_formatted, to: to_formatted, media_url: all_attachments.shift, status_callback: callback_url }.compact)
|
85
|
+
@request << Rack::Utils.parse_nested_query(client.last_request.body)
|
86
|
+
@response << JSON.parse(client.last_response.body)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# ... Then send the last attachment (if any) with the actual text body. This way we're not sending multiple SMS's with same body
|
91
|
+
client.messages.create({ from: from_formatted, to: to_formatted, body: message, media_url: all_attachments.shift, status_callback: callback_url }.compact)
|
92
|
+
@request << Rack::Utils.parse_nested_query(client.last_request.body)
|
93
|
+
@response << JSON.parse(client.last_response.body)
|
94
|
+
after_deliver
|
95
|
+
end
|
96
|
+
|
97
|
+
# Enqueue a job to deliver the SMS message, with options defined by calls to +deliver_later+
|
98
|
+
def send_later
|
99
|
+
@request << Voltron::SmsJob.set(default_options.merge(job_options)).send(job_method, self)
|
100
|
+
after_deliver
|
101
|
+
end
|
102
|
+
|
103
|
+
def setup
|
104
|
+
@request = []
|
105
|
+
@response = []
|
106
|
+
end
|
107
|
+
|
108
|
+
def job_options
|
109
|
+
@job_options ||= {}
|
110
|
+
end
|
111
|
+
|
112
|
+
def job_method
|
113
|
+
@job_method ||= :perform_later
|
114
|
+
end
|
115
|
+
|
116
|
+
def default_options
|
117
|
+
notification.notifyable.class.instance_variable_get('@_notification_defaults').try(:[], :sms) || {}
|
118
|
+
end
|
119
|
+
|
120
|
+
def after_deliver
|
121
|
+
@created = true
|
122
|
+
@job_options = nil
|
123
|
+
@job_method = nil
|
124
|
+
self.request_json = @request.to_json
|
125
|
+
self.response_json = @response.to_json
|
126
|
+
self.sid = response.first.try(:[], :sid)
|
127
|
+
self.status = response.first.try(:[], :status) || 'unknown'
|
128
|
+
|
129
|
+
# if use_queue?, meaning if this was sent via ActiveJob, we need to save ourself
|
130
|
+
# since we got to here within after_create, meaning setting the attributes alone won't cut it
|
131
|
+
self.save if use_queue?
|
132
|
+
end
|
133
|
+
|
134
|
+
def callback_url
|
135
|
+
url = try(:update_voltron_notification_url, host: Voltron.config.base_url).to_s
|
136
|
+
# Don't allow local or blank urls
|
137
|
+
return nil if url.include?('localhost') || url.include?('127.0.0.1') || url.blank?
|
138
|
+
url
|
139
|
+
end
|
140
|
+
|
113
141
|
def valid_phone_number
|
114
142
|
errors.add :to, I18n.t('voltron.notification.sms.invalid_phone', number: to) unless valid_phone?
|
115
143
|
end
|
@@ -119,7 +147,7 @@ class Voltron::Notification::SmsNotification < ActiveRecord::Base
|
|
119
147
|
end
|
120
148
|
|
121
149
|
def to_formatted
|
122
|
-
format to
|
150
|
+
format to || notification.notifyable.try(:phone)
|
123
151
|
end
|
124
152
|
|
125
153
|
def from_formatted
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= @body %>
|
@@ -14,7 +14,7 @@ module Voltron
|
|
14
14
|
voltron_initialzer_path = Rails.root.join('config', 'initializers', 'voltron.rb')
|
15
15
|
|
16
16
|
unless File.exist? voltron_initialzer_path
|
17
|
-
unless system("cd #{Rails.root
|
17
|
+
unless system("cd #{Rails.root} && rails generate voltron:install")
|
18
18
|
puts 'Voltron initializer does not exist. Please ensure you have the \'voltron\' gem installed and run `rails g voltron:install` to create it'
|
19
19
|
return false
|
20
20
|
end
|
@@ -32,9 +32,6 @@ module Voltron
|
|
32
32
|
# A queue is still only used if configured via config.active_job.queue_adapter
|
33
33
|
# config.notify.use_queue = false
|
34
34
|
|
35
|
-
# How long to delay sending email/sms messages. Use this in conjunction with config.notify.use_queue
|
36
|
-
# config.notify.delay = 0.seconds
|
37
|
-
|
38
35
|
# Twilio account id number
|
39
36
|
# config.notify.sms_account_sid = ''
|
40
37
|
|
@@ -74,13 +71,14 @@ CONTENT
|
|
74
71
|
|
75
72
|
def copy_views
|
76
73
|
copy_file '../../../app/views/voltron/notification_mailer/notify.html.erb', Rails.root.join('app', 'views', 'voltron', 'notification_mailer', 'notify.html.erb')
|
74
|
+
copy_file '../../../app/views/voltron/notification_mailer/notify.text.erb', Rails.root.join('app', 'views', 'voltron', 'notification_mailer', 'notify.text.erb')
|
77
75
|
end
|
78
76
|
|
79
77
|
def copy_locales
|
80
78
|
locale_path = Rails.root.join('config', 'locales', 'voltron.yml')
|
81
79
|
locale = YAML.load_file(locale_path).symbolize_keys rescue {}
|
82
80
|
|
83
|
-
compact_nested = Proc.new do |
|
81
|
+
compact_nested = Proc.new do |_, v|
|
84
82
|
v.respond_to?(:delete_if) ? (v.delete_if(&compact_nested); nil) : v.blank?
|
85
83
|
end
|
86
84
|
|
@@ -7,7 +7,7 @@ module Voltron
|
|
7
7
|
|
8
8
|
class Notify
|
9
9
|
|
10
|
-
attr_accessor :use_queue
|
10
|
+
attr_accessor :use_queue
|
11
11
|
|
12
12
|
# SMS config settings
|
13
13
|
attr_accessor :sms_account_sid, :sms_auth_token, :sms_from
|
@@ -17,7 +17,6 @@ module Voltron
|
|
17
17
|
|
18
18
|
def initialize
|
19
19
|
@use_queue ||= false
|
20
|
-
@delay ||= 0.seconds
|
21
20
|
@email_from ||= 'no-reply@example.com'
|
22
21
|
@default_mailer = Voltron::NotificationMailer
|
23
22
|
@default_method = :notify
|
data/lib/voltron/notify.rb
CHANGED
@@ -8,9 +8,11 @@ module Voltron
|
|
8
8
|
|
9
9
|
LOG_COLOR = :light_yellow
|
10
10
|
|
11
|
-
def notifyable
|
11
|
+
def notifyable(defaults={})
|
12
12
|
include InstanceMethods
|
13
13
|
|
14
|
+
@_notification_defaults = defaults.with_indifferent_access
|
15
|
+
|
14
16
|
before_validation :validate_notifications
|
15
17
|
|
16
18
|
after_validation :clean_notification_validation
|
data/voltron-notify.gemspec
CHANGED
@@ -21,15 +21,16 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_dependency 'rails', '>= 4.2'
|
22
22
|
spec.add_dependency 'twilio-ruby', '~> 4.11'
|
23
23
|
spec.add_dependency 'rack', '>= 1.6'
|
24
|
-
spec.add_dependency 'voltron', '>= 0.2.1'
|
24
|
+
spec.add_dependency 'voltron', '~> 0.2', '>= 0.2.1'
|
25
25
|
|
26
|
-
spec.add_development_dependency 'simplecov', '0.11
|
27
|
-
spec.add_development_dependency '
|
28
|
-
spec.add_development_dependency '
|
29
|
-
spec.add_development_dependency '
|
30
|
-
spec.add_development_dependency 'rspec
|
31
|
-
spec.add_development_dependency '
|
32
|
-
spec.add_development_dependency '
|
33
|
-
spec.add_development_dependency '
|
26
|
+
spec.add_development_dependency 'simplecov', '~> 0.11'
|
27
|
+
spec.add_development_dependency 'codeclimate-test-reporter', '~> 1.0'
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.12'
|
29
|
+
spec.add_development_dependency 'rake', '~> 11.3'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
31
|
+
spec.add_development_dependency 'rspec-rails', '~> 3.4'
|
32
|
+
spec.add_development_dependency 'sqlite3', '~> 1.2'
|
33
|
+
spec.add_development_dependency 'factory_girl_rails', '~> 4.7'
|
34
|
+
spec.add_development_dependency 'letter_opener', '~> 1.4'
|
34
35
|
spec.add_development_dependency 'sidekiq', '~> 4.2.10'
|
35
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: voltron-notify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Hainer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -56,6 +56,9 @@ dependencies:
|
|
56
56
|
name: voltron
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.2'
|
59
62
|
- - ">="
|
60
63
|
- !ruby/object:Gem::Version
|
61
64
|
version: 0.2.1
|
@@ -63,6 +66,9 @@ dependencies:
|
|
63
66
|
prerelease: false
|
64
67
|
version_requirements: !ruby/object:Gem::Requirement
|
65
68
|
requirements:
|
69
|
+
- - "~>"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0.2'
|
66
72
|
- - ">="
|
67
73
|
- !ruby/object:Gem::Version
|
68
74
|
version: 0.2.1
|
@@ -70,114 +76,128 @@ dependencies:
|
|
70
76
|
name: simplecov
|
71
77
|
requirement: !ruby/object:Gem::Requirement
|
72
78
|
requirements:
|
73
|
-
- -
|
79
|
+
- - "~>"
|
74
80
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.11
|
81
|
+
version: '0.11'
|
76
82
|
type: :development
|
77
83
|
prerelease: false
|
78
84
|
version_requirements: !ruby/object:Gem::Requirement
|
79
85
|
requirements:
|
80
|
-
- -
|
86
|
+
- - "~>"
|
81
87
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.11
|
88
|
+
version: '0.11'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: codeclimate-test-reporter
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.0'
|
83
103
|
- !ruby/object:Gem::Dependency
|
84
104
|
name: bundler
|
85
105
|
requirement: !ruby/object:Gem::Requirement
|
86
106
|
requirements:
|
87
|
-
- - "
|
107
|
+
- - "~>"
|
88
108
|
- !ruby/object:Gem::Version
|
89
109
|
version: '1.12'
|
90
110
|
type: :development
|
91
111
|
prerelease: false
|
92
112
|
version_requirements: !ruby/object:Gem::Requirement
|
93
113
|
requirements:
|
94
|
-
- - "
|
114
|
+
- - "~>"
|
95
115
|
- !ruby/object:Gem::Version
|
96
116
|
version: '1.12'
|
97
117
|
- !ruby/object:Gem::Dependency
|
98
118
|
name: rake
|
99
119
|
requirement: !ruby/object:Gem::Requirement
|
100
120
|
requirements:
|
101
|
-
- - "
|
121
|
+
- - "~>"
|
102
122
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
123
|
+
version: '11.3'
|
104
124
|
type: :development
|
105
125
|
prerelease: false
|
106
126
|
version_requirements: !ruby/object:Gem::Requirement
|
107
127
|
requirements:
|
108
|
-
- - "
|
128
|
+
- - "~>"
|
109
129
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
130
|
+
version: '11.3'
|
111
131
|
- !ruby/object:Gem::Dependency
|
112
132
|
name: rspec
|
113
133
|
requirement: !ruby/object:Gem::Requirement
|
114
134
|
requirements:
|
115
|
-
- - "
|
135
|
+
- - "~>"
|
116
136
|
- !ruby/object:Gem::Version
|
117
137
|
version: '3.0'
|
118
138
|
type: :development
|
119
139
|
prerelease: false
|
120
140
|
version_requirements: !ruby/object:Gem::Requirement
|
121
141
|
requirements:
|
122
|
-
- - "
|
142
|
+
- - "~>"
|
123
143
|
- !ruby/object:Gem::Version
|
124
144
|
version: '3.0'
|
125
145
|
- !ruby/object:Gem::Dependency
|
126
146
|
name: rspec-rails
|
127
147
|
requirement: !ruby/object:Gem::Requirement
|
128
148
|
requirements:
|
129
|
-
- - "
|
149
|
+
- - "~>"
|
130
150
|
- !ruby/object:Gem::Version
|
131
151
|
version: '3.4'
|
132
152
|
type: :development
|
133
153
|
prerelease: false
|
134
154
|
version_requirements: !ruby/object:Gem::Requirement
|
135
155
|
requirements:
|
136
|
-
- - "
|
156
|
+
- - "~>"
|
137
157
|
- !ruby/object:Gem::Version
|
138
158
|
version: '3.4'
|
139
159
|
- !ruby/object:Gem::Dependency
|
140
160
|
name: sqlite3
|
141
161
|
requirement: !ruby/object:Gem::Requirement
|
142
162
|
requirements:
|
143
|
-
- - "
|
163
|
+
- - "~>"
|
144
164
|
- !ruby/object:Gem::Version
|
145
165
|
version: '1.2'
|
146
166
|
type: :development
|
147
167
|
prerelease: false
|
148
168
|
version_requirements: !ruby/object:Gem::Requirement
|
149
169
|
requirements:
|
150
|
-
- - "
|
170
|
+
- - "~>"
|
151
171
|
- !ruby/object:Gem::Version
|
152
172
|
version: '1.2'
|
153
173
|
- !ruby/object:Gem::Dependency
|
154
174
|
name: factory_girl_rails
|
155
175
|
requirement: !ruby/object:Gem::Requirement
|
156
176
|
requirements:
|
157
|
-
- - "
|
177
|
+
- - "~>"
|
158
178
|
- !ruby/object:Gem::Version
|
159
179
|
version: '4.7'
|
160
180
|
type: :development
|
161
181
|
prerelease: false
|
162
182
|
version_requirements: !ruby/object:Gem::Requirement
|
163
183
|
requirements:
|
164
|
-
- - "
|
184
|
+
- - "~>"
|
165
185
|
- !ruby/object:Gem::Version
|
166
186
|
version: '4.7'
|
167
187
|
- !ruby/object:Gem::Dependency
|
168
188
|
name: letter_opener
|
169
189
|
requirement: !ruby/object:Gem::Requirement
|
170
190
|
requirements:
|
171
|
-
- - "
|
191
|
+
- - "~>"
|
172
192
|
- !ruby/object:Gem::Version
|
173
|
-
version: 1.
|
193
|
+
version: '1.4'
|
174
194
|
type: :development
|
175
195
|
prerelease: false
|
176
196
|
version_requirements: !ruby/object:Gem::Requirement
|
177
197
|
requirements:
|
178
|
-
- - "
|
198
|
+
- - "~>"
|
179
199
|
- !ruby/object:Gem::Version
|
180
|
-
version: 1.
|
200
|
+
version: '1.4'
|
181
201
|
- !ruby/object:Gem::Dependency
|
182
202
|
name: sidekiq
|
183
203
|
requirement: !ruby/object:Gem::Requirement
|
@@ -215,6 +235,7 @@ files:
|
|
215
235
|
- app/models/voltron/notification/sms_notification.rb
|
216
236
|
- app/models/voltron/notification/sms_notification/attachment.rb
|
217
237
|
- app/views/voltron/notification_mailer/notify.html.erb
|
238
|
+
- app/views/voltron/notification_mailer/notify.text.erb
|
218
239
|
- bin/console
|
219
240
|
- bin/setup
|
220
241
|
- lib/generators/templates/config/locales/voltron-notification.yml
|