nuntius 1.0.27 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +77 -18
  3. data/Rakefile +16 -39
  4. data/app/controllers/nuntius/admin/campaigns_controller.rb +12 -10
  5. data/app/controllers/nuntius/admin/layouts/attachments_controller.rb +2 -1
  6. data/app/controllers/nuntius/admin/layouts_controller.rb +1 -1
  7. data/app/controllers/nuntius/admin/lists/subscribers_controller.rb +1 -1
  8. data/app/controllers/nuntius/admin/lists_controller.rb +4 -4
  9. data/app/controllers/nuntius/admin/locales_controller.rb +3 -2
  10. data/app/controllers/nuntius/admin/messages_controller.rb +2 -2
  11. data/app/controllers/nuntius/admin/templates_controller.rb +1 -1
  12. data/app/controllers/nuntius/api/events_controller.rb +2 -2
  13. data/app/controllers/nuntius/application_admin_controller.rb +1 -1
  14. data/app/controllers/nuntius/callbacks_controller.rb +4 -4
  15. data/app/controllers/nuntius/dashboard_controller.rb +1 -1
  16. data/app/controllers/nuntius/feedback_controller.rb +1 -2
  17. data/app/controllers/nuntius/inbound_messages/twilio_inbound_smses_controller.rb +7 -7
  18. data/app/controllers/nuntius/messages_controller.rb +1 -1
  19. data/app/controllers/nuntius/subscribers_controller.rb +33 -0
  20. data/app/drops/nuntius/list_drop.rb +7 -0
  21. data/app/drops/nuntius/subscriber_drop.rb +7 -0
  22. data/app/exceptions/nuntius/base_exception.rb +2 -0
  23. data/app/exceptions/nuntius/missing_messenger_exception.rb +2 -0
  24. data/app/helpers/nuntius/application_helper.rb +6 -11
  25. data/app/jobs/nuntius/deliver_inbound_message_job.rb +1 -1
  26. data/app/jobs/nuntius/purge_message_job.rb +2 -2
  27. data/app/jobs/nuntius/retrieve_mail_job.rb +2 -2
  28. data/app/jobs/nuntius/transport_delivery_job.rb +2 -2
  29. data/app/message_boxes/nuntius/base_message_box.rb +46 -23
  30. data/app/messengers/nuntius/base_messenger.rb +13 -12
  31. data/app/models/nuntius/application_record.rb +1 -1
  32. data/app/models/nuntius/attachment.rb +4 -2
  33. data/app/models/nuntius/campaign.rb +4 -51
  34. data/app/models/nuntius/concerns/yamlify.rb +2 -2
  35. data/app/models/nuntius/inbound_message.rb +2 -0
  36. data/app/models/nuntius/list.rb +11 -1
  37. data/app/models/nuntius/message.rb +34 -29
  38. data/app/models/nuntius/subscriber.rb +21 -1
  39. data/app/models/nuntius/template.rb +20 -20
  40. data/app/presenters/application_presenter.rb +1 -1
  41. data/app/presenters/template_presenter.rb +3 -3
  42. data/app/providers/nuntius/apnotic_push_provider.rb +17 -17
  43. data/app/providers/nuntius/base_provider.rb +6 -6
  44. data/app/providers/nuntius/firebase_push_provider.rb +8 -8
  45. data/app/providers/nuntius/houston_push_provider.rb +15 -15
  46. data/app/providers/nuntius/message_bird_sms_provider.rb +7 -7
  47. data/app/providers/nuntius/slack_slack_provider.rb +7 -7
  48. data/app/providers/nuntius/smtp_mail_provider.rb +36 -28
  49. data/app/providers/nuntius/teams_teams_provider.rb +24 -0
  50. data/app/providers/nuntius/twilio_sms_provider.rb +4 -4
  51. data/app/providers/nuntius/twilio_voice_provider.rb +13 -13
  52. data/app/services/nuntius/aws_sns_processor_service.rb +11 -11
  53. data/app/services/nuntius/deliver_campaign_service.rb +65 -0
  54. data/app/services/nuntius/deliver_inbound_message_service.rb +3 -2
  55. data/app/services/nuntius/retrieve_inbound_mail_service.rb +23 -16
  56. data/app/tables/nuntius_campaigns_table.rb +10 -1
  57. data/app/tables/nuntius_layouts_table.rb +4 -4
  58. data/app/tables/nuntius_lists_table.rb +1 -1
  59. data/app/tables/nuntius_locales_table.rb +1 -1
  60. data/app/tables/nuntius_messages_table.rb +5 -5
  61. data/app/tables/nuntius_subscribers_table.rb +3 -2
  62. data/app/tables/nuntius_templates_table.rb +12 -5
  63. data/app/transports/nuntius/base_transport.rb +1 -1
  64. data/app/transports/nuntius/mail_transport.rb +1 -1
  65. data/app/transports/nuntius/push_transport.rb +1 -1
  66. data/app/transports/nuntius/teams_transport.rb +6 -0
  67. data/app/views/nuntius/admin/campaigns/edit.html.slim +12 -16
  68. data/app/views/nuntius/admin/campaigns/index.html.slim +2 -2
  69. data/app/views/nuntius/admin/layouts/attachments/create.json.jbuilder +3 -3
  70. data/app/views/nuntius/admin/layouts/edit.html.slim +18 -19
  71. data/app/views/nuntius/admin/layouts/index.html.slim +2 -2
  72. data/app/views/nuntius/admin/lists/edit.html.slim +19 -9
  73. data/app/views/nuntius/admin/lists/index.html.slim +2 -2
  74. data/app/views/nuntius/admin/lists/subscribers/edit.html.slim +2 -2
  75. data/app/views/nuntius/admin/locales/edit.html.slim +3 -3
  76. data/app/views/nuntius/admin/locales/index.html.slim +2 -2
  77. data/app/views/nuntius/admin/messages/index.html.slim +2 -2
  78. data/app/views/nuntius/admin/messages/show.html.slim +12 -12
  79. data/app/views/nuntius/admin/templates/edit.html.slim +62 -64
  80. data/app/views/nuntius/admin/templates/index.html.slim +2 -2
  81. data/app/views/nuntius/dashboard/show.html.slim +2 -6
  82. data/app/views/nuntius/subscribers/show.html.slim +9 -0
  83. data/config/locales/en.yml +97 -1
  84. data/config/routes.rb +18 -8
  85. data/db/migrate/20190301201541_create_nuntius_templates.rb +5 -5
  86. data/db/migrate/20190301202436_create_nuntius_messages.rb +4 -4
  87. data/db/migrate/20190322114340_create_nuntius_campaigns.rb +2 -2
  88. data/db/migrate/20190322121338_create_nuntius_subscribers.rb +1 -1
  89. data/db/migrate/20190327124407_create_nuntius_layouts.rb +5 -5
  90. data/db/migrate/20190327143921_add_campaign_to_message.rb +1 -1
  91. data/db/migrate/20190417125153_change_message_status_default.rb +2 -2
  92. data/db/migrate/20190825080757_update_nuntius_message_sending_to_sent.rb +2 -2
  93. data/db/migrate/20200220154927_change_nuntius_templates_payload_type.rb +1 -1
  94. data/db/migrate/20200318095339_create_nuntius_attachments.rb +2 -2
  95. data/db/migrate/20201121185718_create_nuntius_inbound_messages.rb +1 -1
  96. data/db/migrate/20240205204719_add_description_to_list.rb +5 -0
  97. data/db/migrate/20240206203019_add_slug_to_nuntius_list.rb +9 -0
  98. data/lib/nuntius/active_record_helpers.rb +4 -4
  99. data/lib/nuntius/configuration.rb +64 -27
  100. data/lib/nuntius/deprecator.rb +2 -0
  101. data/lib/nuntius/devise.rb +1 -1
  102. data/lib/nuntius/engine.rb +13 -27
  103. data/lib/nuntius/i18n_store.rb +6 -5
  104. data/lib/nuntius/liquid/tags/attach_tag.rb +11 -11
  105. data/lib/nuntius/mail_allow_list.rb +2 -2
  106. data/lib/nuntius/nuntiable.rb +3 -1
  107. data/lib/nuntius/transactio.rb +1 -1
  108. data/lib/nuntius/version.rb +1 -1
  109. data/lib/nuntius.rb +13 -27
  110. data/lib/preamble.rb +18 -18
  111. data/lib/tasks/nuntius_tasks.rake +4 -4
  112. metadata +48 -33
  113. data/app/jobs/nuntius/process_inbound_message_job.rb +0 -10
  114. data/app/reportlets/nuntius/application_reportlet.rb +0 -35
  115. data/app/reportlets/nuntius/daily_messages_per_template_reportlet.rb +0 -50
  116. data/config/webpack/development.js +0 -5
  117. data/config/webpack/environment.js +0 -4
  118. data/config/webpack/production.js +0 -5
  119. data/config/webpack/test.js +0 -5
  120. data/config/webpacker.yml +0 -119
@@ -1,8 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Base class for MessageBoxes
4
+ # These allow you to receive messages based on transport/provider and routes
3
5
  module Nuntius
4
6
  # Message boxes process inbound messages
5
7
  class BaseMessageBox
8
+ attr_reader :message
9
+
10
+ def initialize(message)
11
+ @message = message
12
+ end
13
+
14
+ # In case of mail transport mail will return the mail itself, using https://github.com/mikel/mail
15
+ # For other transports it will return nil.
16
+ # @return [Mail::Message]
17
+ def mail
18
+ return nil unless self.class.transport.to_sym == :mail
19
+ return @mail if @mail
20
+
21
+ @mail = Mail.new(message.raw_message.download)
22
+ end
23
+
6
24
  class << self
7
25
  attr_reader :routes
8
26
 
@@ -16,23 +34,26 @@ module Nuntius
16
34
  @transport
17
35
  end
18
36
 
19
- def route(mapping = nil)
37
+ def route(attribute = :to, test = /.*/, to:)
20
38
  @routes ||= {}
21
- @routes = @routes.merge(mapping) if mapping.is_a?(Hash)
39
+ @routes[attribute] = {test: test, to: to}
22
40
  @routes
23
41
  end
24
42
 
25
43
  # Defines the settings
26
- def settings(hash = {})
44
+ def settings(hash = nil)
27
45
  @settings = hash if hash
28
46
  @settings
29
47
  end
30
48
 
31
49
  def deliver(message)
32
50
  klasses = message_box_for(transport: message.transport.to_sym, provider: message.provider.to_sym)
33
- klass, method = message_box_for_route(klasses, message.to)
51
+ klass, method = message_box_for_route(klasses, message)
34
52
 
35
- klass.new(message).send(method) if method
53
+ if method
54
+ klass.new(message).send(method)
55
+ true
56
+ end
36
57
  end
37
58
 
38
59
  def message_box_for(transport: nil, provider: nil)
@@ -42,34 +63,36 @@ module Nuntius
42
63
  result
43
64
  end
44
65
 
45
- def mail
46
- return nil if transport != 'mail' && provider != 'imap'
47
- return @mail if @mail
48
-
49
- @mail = Mail.read(inbound_message.raw_message.download)
50
- end
51
-
52
66
  private
53
67
 
54
68
  def descendants
55
69
  ObjectSpace.each_object(Class).select { |k| k < self }
56
70
  end
57
71
 
58
- def message_box_for_route(message_boxes, recipients)
72
+ def message_box_for_route(message_boxes, message)
59
73
  klass = message_boxes.find do |message_box|
60
- routes = (message_box.routes || {})
61
- routes.any? { |regexp, _method| [*recipients].any? { |recipient| regexp.match(recipient) } }
74
+ routes = message_box.routes || {}
75
+ routes.any? do |attribute, hash|
76
+ value = message.send(attribute)
77
+ if value.is_a? Array
78
+ value.any? { |value_item| hash[:test].match(value_item) }
79
+ else
80
+ hash[:test].match(value)
81
+ end
82
+ end
62
83
  end
63
- method = klass.routes.find { |regexp, _method| [*recipients].any? { |recipient| regexp.match(recipient) } }&.last if klass
64
-
65
- [klass, method] if method
66
- end
67
- end
68
84
 
69
- attr_reader :message
85
+ route = klass&.routes&.find do |attribute, hash|
86
+ value = message.send(attribute)
87
+ if value.is_a? Array
88
+ value.any? { |value_item| hash[:test].match(value_item) }
89
+ else
90
+ hash[:test].match(value)
91
+ end
92
+ end
70
93
 
71
- def initialize(message)
72
- @message = message
94
+ [klass, route.last[:to]] if route
95
+ end
73
96
  end
74
97
  end
75
98
  end
@@ -61,20 +61,21 @@ module Nuntius
61
61
  return obj.keys.first.to_s if obj.is_a?(Hash)
62
62
 
63
63
  plural = obj.is_a?(Array) || obj.is_a?(ActiveRecord::Relation)
64
- list = plural ? obj : [obj]
65
- klass = list.first.class
66
- klass = klass.base_class if klass.respond_to?(:base_class)
67
- name = klass.name.demodulize
68
- name = name.pluralize if plural
64
+ list = plural ? obj : [obj]
65
+ klass = list.first.class
66
+ klass = klass.base_class if klass.respond_to?(:base_class)
67
+ name = klass.name.demodulize
68
+ name = name.pluralize if plural
69
69
  name.underscore
70
70
  end
71
71
 
72
72
  def class_name_for(obj)
73
- if obj.is_a?(Array) || obj.is_a?(ActiveRecord::Relation)
73
+ case obj
74
+ when Array, ActiveRecord::Relation
74
75
  obj.first.class.name.demodulize
75
- elsif obj.is_a?(Hash)
76
- 'Custom'
77
- elsif obj.is_a?(Class)
76
+ when Hash
77
+ "Custom"
78
+ when Class
78
79
  obj.name.demodulize
79
80
  else
80
81
  obj.class.name
@@ -143,7 +144,7 @@ module Nuntius
143
144
  end
144
145
 
145
146
  def timebased_scope(name, &scope_proc)
146
- raise ArgumentError, 'timebased_scope must start with before or after' unless %w[before after].detect { |prefix| name.to_s.start_with?(prefix) }
147
+ raise ArgumentError, "timebased_scope must start with before or after" unless %w[before after].detect { |prefix| name.to_s.start_with?(prefix) }
147
148
 
148
149
  name = name.to_sym
149
150
  timebased_scopes[name] = scope_proc if scope_proc.present?
@@ -183,10 +184,10 @@ module Nuntius
183
184
  def liquid_context
184
185
  assigns = @params || {}
185
186
  instance_variables.reject { |i| %w[@params @object @locale @templates @template_scope].include? i.to_s }.each do |i|
186
- assigns[i.to_s[1..-1]] = instance_variable_get(i)
187
+ assigns[i.to_s[1..]] = instance_variable_get(i)
187
188
  end
188
189
 
189
- context = { liquid_variable_name_for(@object) => (@object.is_a?(Hash) ? @object[@object.keys.first].deep_stringify_keys : @object) }
190
+ context = {liquid_variable_name_for(@object) => (@object.is_a?(Hash) ? @object[@object.keys.first].deep_stringify_keys : @object)}
190
191
  assigns.merge(context)
191
192
  end
192
193
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'concerns/metadata_scoped'
3
+ require_relative "concerns/metadata_scoped"
4
4
 
5
5
  module Nuntius
6
6
  class ApplicationRecord < ActiveRecord::Base
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nuntius
2
4
  class Attachment < ApplicationRecord
3
- has_and_belongs_to_many :messages, class_name: 'Message'
5
+ has_and_belongs_to_many :messages, class_name: "Message"
4
6
 
5
7
  delegate :download, :content_type, :filename, :signed_id, to: :content
6
8
 
7
9
  begin
8
10
  has_one_attached :content, service: Nuntius.config.active_storage_service
9
- rescue StandardError
11
+ rescue
10
12
  nil
11
13
  end
12
14
  end
@@ -8,68 +8,21 @@ module Nuntius
8
8
  accepts_nested_attributes_for :list, reject_if: :all_blank
9
9
 
10
10
  belongs_to :layout, optional: true
11
- has_many :messages, class_name: 'Nuntius::Message'
11
+ has_many :messages, class_name: "Nuntius::Message"
12
12
  validates :name, presence: true
13
13
 
14
14
  state_machine initial: :draft do
15
- after_transition any => any, do: :do_after_transition
16
-
17
15
  event :publish do
18
16
  transition draft: :sending
19
17
  end
18
+
20
19
  event :sent do
21
20
  transition sending: :sent
22
21
  end
23
- end
24
-
25
- def deliver
26
- t = BaseTransport.class_from_name(transport).new
27
- list.subscribers.each do |subscriber|
28
- t.deliver(new_message(subscriber))
29
- end
30
- end
31
-
32
- def new_message(subscriber, assigns = {})
33
- if subscriber.nuntiable
34
- name = Nuntius::BaseMessenger.liquid_variable_name_for(subscriber.nuntiable)
35
- assigns[name] = subscriber.nuntiable
36
- end
37
- message = Nuntius::Message.new(transport: transport, campaign: self, nuntiable: subscriber.nuntiable, metadata: metadata)
38
-
39
- locale_proc = Nuntius::BaseMessenger.messenger_for_obj(subscriber.nuntiable).locale
40
- locale = instance_exec(object, &locale_proc) if locale_proc
41
22
 
42
- message.from = render(:from, assigns, locale)
43
- message.to = if transport == 'mail'
44
- %["#{subscriber.first_name} #{subscriber.last_name}" <#{subscriber.email}>]
45
- elsif transport == 'sms'
46
- subscriber.phone_number
47
- elsif transport == 'voice'
48
- subscriber.phone_number
49
- end
50
-
51
- message.subject = render(:subject, assigns, locale)
52
- message.html = render(:html, assigns, locale, layout: layout&.data)
53
-
54
- message
55
- end
56
-
57
- def translation_scope
58
- scope = %w[nuntius]
59
- scope << layout.name.underscore.tr(' ', '_') if layout
60
- scope.join('.')
61
- end
62
-
63
- private
64
-
65
- def render(attr, assigns, locale, options = {})
66
- I18n.with_locale(locale) do
67
- ::Liquidum.render(send(attr), assigns: assigns.merge(options.merge('campaign' => self)), registers: { 'campaign' => self })
23
+ after_transition(on: :publish) do |campaign, transition|
24
+ DeliverCampaignService.perform(campaign: campaign)
68
25
  end
69
26
  end
70
-
71
- def do_after_transition(transition)
72
- deliver if transition.event == :publish
73
- end
74
27
  end
75
28
  end
@@ -8,11 +8,11 @@ module Nuntius
8
8
  class_methods do
9
9
  def yamlify(attr)
10
10
  define_method(:"#{attr}_yaml=") do |yaml|
11
- write_attribute attr, YAML.safe_load(yaml.gsub("\t", ' '))
11
+ write_attribute attr, YAML.safe_load(yaml.gsub("\t", " "))
12
12
  end
13
13
 
14
14
  define_method(:"#{attr}_yaml") do
15
- return '' if attributes[attr.to_s].blank?
15
+ return "" if attributes[attr.to_s].blank?
16
16
 
17
17
  YAML.dump(attributes[attr.to_s])
18
18
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Nuntius
2
4
  class InboundMessage < ApplicationRecord
3
5
  has_one_attached :raw_message, service: Nuntius.config.active_storage_service
@@ -3,8 +3,18 @@
3
3
  module Nuntius
4
4
  class List < ApplicationRecord
5
5
  include Nuntius::Concerns::MetadataScoped
6
+ include Nuntius::Concerns::Yamlify
7
+
8
+ has_many :subscribers, counter_cache: :subscribers_count, dependent: :delete_all
9
+
10
+ validates :name, presence: true
11
+ validates :slug, presence: true, uniqueness: true
12
+
13
+ yamlify :metadata
6
14
 
7
- has_many :subscribers, counter_cache: :subscribers_count
8
15
  accepts_nested_attributes_for :subscribers, reject_if: :all_blank
16
+
17
+ scope :subscribed_by, ->(nuntiable) { where(id: nuntiable.nuntius_subscriptions.select(:list_id)) }
18
+ scope :not_subscribed_by, ->(nuntiable) { where.not(id: nuntiable.nuntius_subscriptions.select(:list_id)) }
9
19
  end
10
20
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pry"
3
4
  module Nuntius
4
5
  # Stores individual messages to individual recipients
5
6
  #
@@ -13,12 +14,12 @@ module Nuntius
13
14
  class Message < ApplicationRecord
14
15
  include Nuntius::Concerns::MetadataScoped
15
16
 
16
- has_and_belongs_to_many :attachments, class_name: 'Attachment'
17
+ has_and_belongs_to_many :attachments, class_name: "Attachment"
17
18
 
18
19
  belongs_to :campaign, optional: true
19
20
  belongs_to :template, optional: true
20
- belongs_to :parent_message, class_name: 'Message', optional: true
21
- has_many :child_messages, class_name: 'Message', foreign_key: 'parent_message_id', dependent: :destroy
21
+ belongs_to :parent_message, class_name: "Message", optional: true
22
+ has_many :child_messages, class_name: "Message", foreign_key: "parent_message_id", dependent: :destroy
22
23
  belongs_to :nuntiable, polymorphic: true, optional: true
23
24
 
24
25
  validates :transport, presence: true
@@ -32,19 +33,19 @@ module Nuntius
32
33
  # end
33
34
 
34
35
  def pending?
35
- status == 'pending'
36
+ status == "pending"
36
37
  end
37
38
 
38
39
  def sent?
39
- status == 'sent'
40
+ status == "sent"
40
41
  end
41
42
 
42
43
  def blocked?
43
- status == 'blocked'
44
+ status == "blocked"
44
45
  end
45
46
 
46
47
  def delivered?
47
- status == 'delivered'
48
+ status == "delivered"
48
49
  end
49
50
 
50
51
  def delivered_or_blocked?
@@ -52,12 +53,12 @@ module Nuntius
52
53
  end
53
54
 
54
55
  def undelivered?
55
- status == 'undelivered'
56
+ status == "undelivered"
56
57
  end
57
58
 
58
59
  # Removes only pending child messages
59
60
  def cleanup!
60
- Nuntius::Message.where(status: 'pending').where(parent_message: self).destroy_all
61
+ Nuntius::Message.where(status: "pending").where(parent_message: self).destroy_all
61
62
  end
62
63
 
63
64
  def add_attachment(options)
@@ -65,35 +66,39 @@ module Nuntius
65
66
 
66
67
  uri = options[:url] && URI.parse(options[:url])
67
68
 
68
- if uri&.scheme == 'file'
69
+ if uri&.scheme == "file"
70
+ # FIXME: This is a possible security problem
69
71
  attachment[:io] = File.open(uri.path)
70
72
  elsif uri
71
- client = HTTPClient.new
72
- client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
73
- client.ssl_config.set_default_paths unless Gem.win_platform?
74
- response = client.get(options[:url], follow_redirect: true)
75
- content_disposition = response.headers['Content-Disposition'] || ''
73
+ client = Faraday.new(ssl: {verify: false}) do |builder|
74
+ builder.response :follow_redirects
75
+ builder.adapter Faraday.default_adapter
76
+ end
77
+
78
+ response = client.get(options[:url])
79
+ content_disposition = response.headers["Content-Disposition"] || ""
80
+
76
81
  options[:filename] ||= content_disposition[/filename="([^"]+)"/, 1]
77
- attachment[:content_type] = response.content_type
82
+ attachment[:content_type] = response.headers["Content-Type"]
78
83
  attachment[:io] = if response.body.is_a? String
79
- StringIO.new(response.body)
80
- else
81
- # Assume IO object
82
- response.body
83
- end
84
+ StringIO.new(response.body)
85
+ else
86
+ # Assume IO object
87
+ response.body
88
+ end
84
89
  elsif options[:content].respond_to?(:read)
85
90
  attachment[:content_type] = options[:content_type]
86
91
  attachment[:io] = options[:content]
87
92
  else
88
- raise 'Cannot add attachment without url or content'
93
+ raise "Cannot add attachment without url or content"
89
94
  end
90
95
 
91
96
  # Set the filename
92
- attachment[:filename] = options[:filename] || uri.path.split('/').last || 'attachment'
97
+ attachment[:filename] = options[:filename] || uri.path.split("/").last || "attachment"
93
98
 
94
99
  # (Try to) add file extension if it is missing
95
- file_extension = File.extname(attachment[:filename]).delete('.')
96
- attachment[:filename] += ".#{Mime::Type.lookup(attachment[:content_type].split(';').first).to_sym}" if file_extension.blank? && attachment[:content_type]
100
+ file_extension = File.extname(attachment[:filename]).delete(".")
101
+ attachment[:filename] += ".#{Mime::Type.lookup(attachment[:content_type].split(";").first).to_sym}" if file_extension.blank? && attachment[:content_type]
97
102
 
98
103
  # Fix content type if file extension known but content type blank
99
104
  attachment[:content_type] ||= Mime::Type.lookup_by_extension(file_extension)&.to_s if file_extension
@@ -103,17 +108,17 @@ module Nuntius
103
108
  zio.put_next_entry attachment[:file_name]
104
109
  zio.write attachment[:io].read
105
110
  end
106
- attachment[:content_type] = 'application/zip'
111
+ attachment[:content_type] = "application/zip"
107
112
  attachment[:io] = zip_stream
108
113
  end
109
114
 
110
115
  nuntius_attachment = Nuntius::Attachment.new
111
116
  nuntius_attachment.content.attach(io: attachment[:io],
112
- filename: attachment[:filename],
113
- content_type: attachment[:content_type])
117
+ filename: attachment[:filename],
118
+ content_type: attachment[:content_type])
114
119
 
115
120
  attachments.push(nuntius_attachment)
116
- rescue StandardError => e
121
+ rescue => e
117
122
  Nuntius.config.logger.error "Message: Could not attach #{attachment[:filename]} #{e.message}"
118
123
  end
119
124
 
@@ -5,8 +5,28 @@ module Nuntius
5
5
  belongs_to :list
6
6
  belongs_to :nuntiable, polymorphic: true, optional: true
7
7
 
8
+ scope :subscribed, -> { where(unsubscribed_at: nil) }
9
+
8
10
  def name
9
- [first_name, last_name].join(' ')
11
+ [first_name, last_name].compact.join(" ").presence || email
12
+ end
13
+
14
+ def first_name
15
+ return nuntiable.first_name if nuntiable.respond_to?(:first_name)
16
+
17
+ super
18
+ end
19
+
20
+ def last_name
21
+ return nuntiable.last_name if nuntiable.respond_to?(:last_name)
22
+
23
+ super
24
+ end
25
+
26
+ def email
27
+ return nuntiable.email if nuntiable.respond_to?(:email)
28
+
29
+ super
10
30
  end
11
31
  end
12
32
  end
@@ -6,9 +6,9 @@ module Nuntius
6
6
  include Nuntius::Concerns::Yamlify
7
7
 
8
8
  belongs_to :layout, optional: true
9
- has_many :messages, class_name: 'Nuntius::Message', dependent: :nullify
9
+ has_many :messages, class_name: "Nuntius::Message", dependent: :nullify
10
10
 
11
- LIQUID_TAGS = /{%(?:(?!%}).)*%}|{{(?:(?!}}).)*}}/.freeze
11
+ LIQUID_TAGS = /{%(?:(?!%}).)*%}|{{(?:(?!}}).)*}}/
12
12
 
13
13
  validates :description, presence: true
14
14
  validates :from, liquid: true
@@ -18,8 +18,8 @@ module Nuntius
18
18
  validates :text, liquid: true
19
19
 
20
20
  validates :event, presence: true
21
- validates :event, format: { with: /.+#.+/ }, if: ->(t) { t.klass == 'Custom' }
22
- validates :interval, format: { allow_blank: true, with: /\A[1-9][0-9]*\s(month|week|day|hour|minute)s?\z/ }
21
+ validates :event, format: {with: /.+#.+/}, if: ->(t) { t.klass == "Custom" }
22
+ validates :interval, format: {allow_blank: true, with: /\A[1-9][0-9]*\s(month|week|day|hour|minute)s?\z/}
23
23
 
24
24
  yamlify :metadata
25
25
 
@@ -31,10 +31,10 @@ module Nuntius
31
31
  locale = instance_exec(object, &locale_proc) if locale_proc
32
32
  locale = params[:locale].to_sym if params[:locale]
33
33
 
34
- options = { registers: { 'template' => self, 'message' => message } }
34
+ options = {registers: {"template" => self, "message" => message}}
35
35
 
36
- message.to = render(:to, assigns, locale, options).split(',').reject(&:empty?).join(',')
37
- message.from = render(:from, assigns, locale, options).split(',').reject(&:empty?).join(',')
36
+ message.to = render(:to, assigns, locale, options).split(",").reject(&:empty?).join(",")
37
+ message.from = render(:from, assigns, locale, options).split(",").reject(&:empty?).join(",")
38
38
  message.subject = render(:subject, assigns, locale, options)
39
39
  message.html = render(:html, assigns, locale, options.merge(layout: layout&.data))
40
40
  message.text = render(:text, assigns, locale, options)
@@ -45,12 +45,12 @@ module Nuntius
45
45
 
46
46
  def translation_scope
47
47
  scope = %w[]
48
- scope << layout.name.underscore.tr(' ', '_') if layout
49
- scope << klass.underscore.tr('/', '_')
48
+ scope << layout.name.underscore.tr(" ", "_") if layout
49
+ scope << klass.underscore.tr("/", "_")
50
50
  scope << event
51
51
  scope << transport
52
- scope << description.underscore.gsub(/[^a-z]+/, '_') if description
53
- scope.join('.')
52
+ scope << description.underscore.gsub(/[^a-z]+/, "_") if description
53
+ scope.join(".")
54
54
  end
55
55
 
56
56
  # Trix correctly escapes the HTML, but for liquid this is not what we need.
@@ -64,7 +64,7 @@ module Nuntius
64
64
 
65
65
  def interval_duration
66
66
  unless interval.blank?
67
- number, type = interval.split(' ')
67
+ number, type = interval.split(" ")
68
68
  number = number.to_i
69
69
 
70
70
  return number.public_send(type) if number.respond_to?(type)
@@ -76,11 +76,11 @@ module Nuntius
76
76
  def interval_time_range
77
77
  return 0.seconds..0.seconds if interval.blank?
78
78
 
79
- start = if event.start_with?('before')
80
- interval_duration.after
81
- else
82
- interval_duration.ago
83
- end
79
+ start = if event.start_with?("before")
80
+ interval_duration.after
81
+ else
82
+ interval_duration.ago
83
+ end
84
84
 
85
85
  (start - 1.hour)..start
86
86
  end
@@ -90,11 +90,11 @@ module Nuntius
90
90
  def render(attr, assigns, locale, options = {})
91
91
  I18n.with_locale(locale) do
92
92
  if attr == :payload
93
- YAML.safe_load(::Liquidum.render(send(attr), { assigns: assigns.merge('template' => self), registers: { 'template' => self } }.merge(options)))
93
+ YAML.safe_load(::Liquidum.render(send(attr), {assigns: assigns.merge("template" => self), registers: {"template" => self}}.merge(options)))
94
94
  elsif attr == :html
95
- ::Liquidum.render(send(attr), { filter: 'markdown', assigns: assigns.merge('template' => self), registers: { 'template' => self } }.merge(options))
95
+ ::Liquidum.render(send(attr), {filter: "markdown", assigns: assigns.merge("template" => self), registers: {"template" => self}}.merge(options))
96
96
  else
97
- ::Liquidum.render(send(attr), { assigns: assigns.merge('template' => self), registers: { 'template' => self } }.merge(options))
97
+ ::Liquidum.render(send(attr), {assigns: assigns.merge("template" => self), registers: {"template" => self}}.merge(options))
98
98
  end
99
99
  end
100
100
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'delegate'
3
+ require "delegate"
4
4
 
5
5
  # http://nithinbekal.com/posts/rails-presenters/
6
6
  class ApplicationPresenter < SimpleDelegator
@@ -4,12 +4,12 @@ class TemplatePresenter < ApplicationPresenter
4
4
  def all_events
5
5
  events = []
6
6
  Nuntius.config.nuntiable_class_names.each do |class_name|
7
- next if class_name == 'Custom'
7
+ next if class_name == "Custom"
8
8
 
9
9
  messenger = Nuntius::BaseMessenger.messenger_for_class(class_name)
10
10
  messenger.instance_methods(false).each do |m|
11
- events << [m, m, { 'data-chain': class_name,
12
- 'data-timebased': messenger.timebased_scopes.include?(m) }]
11
+ events << [m, m, {"data-chain": class_name,
12
+ "data-timebased": messenger.timebased_scopes.include?(m)}]
13
13
  end
14
14
  end
15
15
  events.sort_by(&:first)
@@ -1,31 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'apnotic'
3
+ require "apnotic"
4
4
 
5
5
  module Nuntius
6
6
  class ApnoticPushProvider < BaseProvider
7
7
  transport :push
8
8
 
9
9
  setting_reader :certificate,
10
- required: true,
11
- description: 'The contents of a valid APNS push certificate in .pem format'
10
+ required: true,
11
+ description: "The contents of a valid APNS push certificate in .pem format"
12
12
  setting_reader :passphrase,
13
- required: false,
14
- description: 'If the APNS certificate is protected by a passphrase, ' \
15
- 'provide this variable to use when decrypting it.'
13
+ required: false,
14
+ description: "If the APNS certificate is protected by a passphrase, " \
15
+ "provide this variable to use when decrypting it."
16
16
  setting_reader :environment,
17
- required: false,
18
- default: :production,
19
- description: 'Development or production, defaults to production'
17
+ required: false,
18
+ default: :production,
19
+ description: "Development or production, defaults to production"
20
20
 
21
21
  def deliver
22
22
  return message if message.to.size != 64
23
23
 
24
24
  connection = if environment.to_sym == :development
25
- Apnotic::Connection.development(cert_path: StringIO.new(certificate), cert_pass: passphrase)
26
- else
27
- Apnotic::Connection.new(cert_path: StringIO.new(certificate), cert_pass: passphrase)
28
- end
25
+ Apnotic::Connection.development(cert_path: StringIO.new(certificate), cert_pass: passphrase)
26
+ else
27
+ Apnotic::Connection.new(cert_path: StringIO.new(certificate), cert_pass: passphrase)
28
+ end
29
29
 
30
30
  notification = Apnotic::Notification.new(message.to)
31
31
  notification.alert = message.text
@@ -34,10 +34,10 @@ module Nuntius
34
34
  response = connection.push(notification)
35
35
 
36
36
  message.status = if response.ok?
37
- 'sent'
38
- else
39
- 'undelivered'
40
- end
37
+ "sent"
38
+ else
39
+ "undelivered"
40
+ end
41
41
  message
42
42
  end
43
43
  end