voltron-notify 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6cdac4cb0e2b131d7b6a088ea7a952cc422a1263
4
- data.tar.gz: 9d41eb541fe1686afdca60334d0525583489ebbe
3
+ metadata.gz: 698c826b9ee01c01f1daea0727c9f9a858bd53f9
4
+ data.tar.gz: f2f0cb75ae14e44a046c6fc5faf590a1efbdb02c
5
5
  SHA512:
6
- metadata.gz: 679e4b2d86d9901e23c7df5db41e01cefebd8ef34d2cba4182f7130093d948c61fe09f49fe59c61f2c3d5707c98614e4fee8c5fbdefca95409c6e1480f61dc62
7
- data.tar.gz: a3758d441c5b7cf86332fff0e784f187d5211b7cec6c2797000f8350740e27458f36092cb6df62ec2b5ec309990e387dcc9fb578739c9fcde2b85e5d24498284
6
+ metadata.gz: d44cbe91644c6cfcc2bb082ed1f1244969a87ed69eb1abfb1033ed968cfde3f5810c1319ad4d9295fce5a17c5b5a3fc953e7ade7c6f8bc5d51922606e7769ada
7
+ data.tar.gz: 87ab64f59e3fb1b486eb869df10263625f112198792b5d4b84981873e42442a65c854d1e3e142f1450bef3c606b9f820bc67cf6c2529f34a8b9fd9a6b0491959
data/.travis.yml CHANGED
@@ -3,4 +3,6 @@ language: ruby
3
3
  rvm:
4
4
  - 2.2.3
5
5
  - 2.3.1
6
+ - 2.3.3
7
+ - 2.4.1
6
8
  before_install: gem install bundler -v 1.12.5
data/Gemfile CHANGED
@@ -1,6 +1,5 @@
1
1
  source 'https://rubygems.org'
2
- source 'http://gem.minow.io'
2
+ source 'https://gem.minow.io'
3
3
 
4
4
  # Specify your gem's dependencies in voltron-notify.gemspec
5
- gem 'colorize'
6
5
  gemspec
data/README.md CHANGED
@@ -1,9 +1,11 @@
1
+ [![Code Climate](https://codeclimate.com/github/ehainer/voltron-notify.png)](https://codeclimate.com/github/ehainer/voltron-notify)
1
2
  [![Build Status](https://travis-ci.org/ehainer/voltron-notify.svg?branch=master)](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 (if not exists already) and add the notify config:
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
- `notifyable` will create a notifications association on whatever model it is called on. Once done, you can utilize Voltron Notify like so:
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" }, { param_one: "Hi there", param_two: "" }
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 '&lt;module&gt;/&lt;mailer&gt;'. 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
- ```ruby
94
- Rails.application.routes.draw do
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
- # The controller that will handle the notification update
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
- # The action that will perform the update
111
- action: 'update' #
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
 
@@ -1,7 +1,7 @@
1
1
  class Voltron::SmsJob < ActiveJob::Base
2
2
 
3
3
  def perform(sms)
4
- sms.deliver_now
4
+ sms.send(:send_now)
5
5
  end
6
6
 
7
7
  end
@@ -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,v| PERMITTED_ATTRIBUTES.include?(k.to_sym) })
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 &block if block_given?
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 &block if block_given?
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 :deliver_now, unless: :use_queue?
7
+ before_create :send_now, if: Proc.new { |n| !n.send(:use_queue?) || n.immediate }
8
8
 
9
- after_create :deliver_later, if: :use_queue?
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
- def setup
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
- # Wrap entire request in array, for consistency
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
- # Wrap entire response in array, for consistency
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.exists?(path)
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 :deliver_now, unless: :use_queue?
13
+ before_create :send_now, unless: :use_queue?
14
14
 
15
- after_create :deliver_later, if: :use_queue?
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
- def setup
28
- @request = []
29
- @response = []
30
- end
29
+ attr_accessor :created
31
30
 
32
31
  def request
33
- # Ensure returned object is an array of request hashes, for consistency
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
- # Ensure returned object is an array of response hashes, for consistency
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
- @request = request
57
- @response = response
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
- def deliver_later
78
- job = Voltron::SmsJob.set(wait: Voltron.config.notify.delay).perform_later self
79
- @request << job
80
- after_deliver
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.to_s} && rails generate voltron:install")
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 |k, v|
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, :delay
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Voltron
2
2
  module Notify
3
- VERSION = '0.1.9'.freeze
3
+ VERSION = '0.2.0'.freeze
4
4
  end
5
5
  end
@@ -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.0'
27
- spec.add_development_dependency 'bundler', '>= 1.12'
28
- spec.add_development_dependency 'rake', '>= 10.0'
29
- spec.add_development_dependency 'rspec', '>= 3.0'
30
- spec.add_development_dependency 'rspec-rails', '>= 3.4'
31
- spec.add_development_dependency 'sqlite3', '>= 1.2'
32
- spec.add_development_dependency 'factory_girl_rails', '>= 4.7'
33
- spec.add_development_dependency 'letter_opener', '>= 1.0.0'
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.1.9
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-08 00:00:00.000000000 Z
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.0
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.0
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: '10.0'
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: '10.0'
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.0.0
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.0.0
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