Module: ActivityNotification::ActsAsNotifiable

Extended by:
ActiveSupport::Concern
Included in:
Models
Defined in:
lib/activity_notification/roles/acts_as_notifiable.rb

Overview

Manages to add all required configurations to notifiable models.

Class Method Summary collapse

Class Method Details

.acts_as_notifiable(target_type, options = {}) ⇒ Hash

Adds required configurations to notifiable models.

Parameters:

  • :targets

    • Targets to send notifications. It it set as ActiveRecord records or array of models. This is a only necessary option. If you do not specify this option, you have to override notification_targets or notification_[plural target type] (e.g. notification_users) method.

  • :group

    • Group unit of notifications. Notifications will be bundled by this group (and target, notifiable_type, key). This parameter is a optional.

  • :group_expiry_delay

    • Expiry period of a notification group. Notifications will be bundled within the group expiry period. This parameter is a optional.

  • :notifier

    • Notifier of the notification. This will be stored as notifier with notification record. This parameter is a optional.

  • :parameters

    • Additional parameters of the notifications. This will be stored as parameters with notification record. You can use these additional parameters in your notification view or i18n text. This parameter is a optional.

  • :email_allowed

    • Whether activity_notification sends notification email. Specified method or symbol is expected to return true (not nil) or false (nil). This parameter is a optional since default value is false. To use notification email, email_allowed option must return true (not nil) in both of notifiable and target model. This can be also configured default option in initializer.

  • :action_cable_allowed

    • Whether activity_notification publishes notifications to ActionCable channel. Specified method or symbol is expected to return true (not nil) or false (nil). This parameter is a optional since default value is false. To use ActionCable for notifications, action_cable_allowed option of target model must also return true (not nil). This can be also configured default option in initializer as action_cable_enabled.

  • :action_cable_api_allowed

    • Whether activity_notification publishes notifications to ActionCable API channel. Specified method or symbol is expected to return true (not nil) or false (nil). This parameter is a optional since default value is false. To use ActionCable for notifications, action_cable_allowed option of target model must also return true (not nil). This can be also configured default option in initializer as action_cable_api_enabled.

  • :notifiable_path

    • Path to redirect from open or move action of notification controller. You can also use this notifiable_path as notifiable link in notification view. This parameter is a optional since polymorphic_path is used as default value.

  • :tracked

    • Adds required callbacks to generate notifications for creation and update of the notifiable model. Tracked notifications are disabled as default. When you set true as this :tracked option, default callbacks will be enabled for [:create, :update]. You can use :only, :except and other notify options as hash for this option. Tracked notifications are generated synchronously as default configuration. You can use :notify_later option as notify options to make tracked notifications generated asynchronously.

  • :printable_name or :printable_notifiable_name

    • Printable notifiable name. This parameter is a optional since ‘ActivityNotification::Common.printable_name` is used as default value. :printable_name is the same option as :printable_notifiable_name

  • :dependent_notifications

    • Dependency for notifications to delete generated notifications with this notifiable. This option is used to configure generated_notifications_as_notifiable association. You can use :delete_all, :destroy, :restrict_with_error, :restrict_with_exception, :update_group_and_delete_all or :update_group_and_destroy for this option. When you use :update_group_and_delete_all or :update_group_and_destroy to this parameter, the oldest group member notification becomes a new group owner as ‘before_destroy` of this Notifiable. This parameter is effective for all target and is a optional since no dependent option is used as default.

  • :optional_targets

    • Optional targets to integrate external notification serveces like Amazon SNS or Slack. You can use hash of optional target implementation class as key and initializing parameters as value for this parameter. When the hash parameter is passed, acts_as_notifiable will create new instance of optional target class and call initialize_target method with initializing parameters, then configure them as optional_targets for this notifiable and target. You can also use symbol of method name or lambda function which returns array of initialized optional target intstances. All optional target class must extends ActivityNotification::OptionalTarget::Base. This parameter is completely optional.

Examples:

Notify to all users

class Comment < ActiveRecord::Base
  acts_as_notifiable :users, targets: User.all
end

Notify to author and users commented to the article, except comment owner self

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  belongs_to :user
  acts_as_notifiable :users,
    targets: ->(comment, key) {
      ([comment.article.user] + comment.article.commented_users.to_a - [comment.user]).uniq
    }
end

All unopened notifications to the same target will be grouped by ‘article`

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  acts_as_notifiable :users, targets: User.all, group: :article
end

All unopened notifications to the same target within 1 day will be grouped by ‘article`

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  acts_as_notifiable :users, targets: User.all, group: :article, :group_expiry_delay: 1.day
end

Set comment owner self as notifier

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  belongs_to :user
  acts_as_notifiable :users, targets: User.all, notifier: :user
end

Set constant values as additional parameter

# app/models/comment.rb
class Comment < ActiveRecord::Base
  acts_as_notifiable :users, targets: User.all, parameters: { default_param: '1' }
end

Set comment body as additional parameter

# app/models/comment.rb
class Comment < ActiveRecord::Base
  acts_as_notifiable :users, targets: User.all, parameters: ->(comment, key) { body: comment.body }
end

Enable email notification for this notifiable model

# app/models/comment.rb
class Comment < ActiveRecord::Base
  acts_as_notifiable :users, targets: User.all, email_allowed: true
end

Enable notification ActionCable for this notifiable model

# app/models/comment.rb
class Comment < ActiveRecord::Base
  acts_as_notifiable :users, targets: User.all, action_cable_allowed: true
end

Enable notification ActionCable for this notifiable model

# app/models/comment.rb
class Comment < ActiveRecord::Base
  acts_as_notifiable :users, targets: User.all, action_cable_api_allowed: true
end

Redirect to parent article page from comment notifications

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  acts_as_notifiable :users, targets: User.all, notifiable_path: :article_notifiable_path

  def article_notifiable_path
    article_path(article)
  end
end

Add all callbacks to generate notifications for creation and update

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  acts_as_notifiable :users, targets: User.all, tracked: true
end

Add callbacks to generate notifications for creation only

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  acts_as_notifiable :users, targets: User.all, tracked: { only: [:create] }
end

Add callbacks to generate notifications for creation (except update) only

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  acts_as_notifiable :users, targets: User.all, tracked: { except: [:update], key: "comment.edited", send_later: false }
end

Add callbacks to generate notifications asynchronously for creation only

# app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article
  acts_as_notifiable :users, targets: User.all, tracked: { only: [:create], notify_later: true }
end

Define printable name with comment body

# app/models/comment.rb
class Comment < ActiveRecord::Base
  acts_as_notifiable :users, targets: User.all, printable_name: ->(comment) { "comment \"#{comment.body}\"" }
end

Define :delete_all dependency to generated notifications

# app/models/comment.rb
class Comment < ActiveRecord::Base
  acts_as_notifiable :users, targets: User.all, dependent_notifications: :delete_all
end

Define to integrate with Amazon SNS, Slack and your custom ConsoleOutput targets

# app/models/comment.rb
class Comment < ActiveRecord::Base
  require 'activity_notification/optional_targets/amazon_sns'
  require 'activity_notification/optional_targets/slack'
  require 'custom_optional_targets/console_output'
  acts_as_notifiable :admins, targets: Admin.all,
    optional_targets: {
      ActivityNotification::OptionalTarget::AmazonSNS => { topic_arn: '<Topin ARN of yours>' },
      ActivityNotification::OptionalTarget::Slack  => {
        webhook_url: '<Slack Webhook URL>',
        slack_name: :slack_name, channel: 'activity_notification', username: 'ActivityNotification', icon_emoji: ":ghost:"
      },
      CustomOptionalTarget::ConsoleOutput => {}
    }
end

Parameters:

  • target_type (Symbol)

    Type of notification target as symbol

  • options (Hash) (defaults to: {})

    Options for notifiable model configuration

Options Hash (options):

  • :targets (Symbol, Proc, Array) — default: nil

    Targets to send notifications

  • :group (Symbol, Proc, Object) — default: nil

    Group unit of the notifications

  • :group_expiry_delay (Symbol, Proc, Object) — default: nil

    Expiry period of a notification group

  • :notifier (Symbol, Proc, Object) — default: nil

    Notifier of the notifications

  • :parameters (Symbol, Proc, Hash) — default: {}

    Additional parameters of the notifications

  • :email_allowed (Symbol, Proc, Boolean) — default: ActivityNotification.config.email_enabled

    Whether activity_notification sends notification email

  • :action_cable_allowed (Symbol, Proc, Boolean) — default: ActivityNotification.config.action_cable_enabled

    Whether activity_notification publishes WebSocket using ActionCable

  • :notifiable_path (Symbol, Proc, String) — default: polymorphic_path(self)

    Path to redirect from open or move action of notification controller

  • :tracked (Boolean, Hash) — default: nil

    Flag or parameters for automatic tracked notifications

  • :printable_name (Symbol, Proc, String) — default: ActivityNotification::Common.printable_name

    Printable notifiable name

  • :dependent_notifications (Symbol, Proc) — default: nil

    Dependency for notifications to delete generated notifications with this notifiable, [:delete_all, :destroy, :restrict_with_error, :restrict_with_exception, :update_group_and_delete_all, :update_group_and_destroy] are available

  • :optional_targets (Hash<Class, Hash>) — default: nil

    Optional target configurations with hash of ‘OptionalTarget` implementation class as key and initializing option parameter as value

Returns:

  • (Hash)

    Configured parameters as notifiable model



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/activity_notification/roles/acts_as_notifiable.rb', line 230

def acts_as_notifiable(target_type, options = {})
  include Notifiable
  configured_params = {}

  if options[:tracked].present?
    configured_params.update(add_tracked_callbacks(target_type, options[:tracked].is_a?(Hash) ? options[:tracked] : {}))
  end

  if available_dependent_notifications_options.include? options[:dependent_notifications]
    configured_params.update(add_destroy_dependency(target_type, options[:dependent_notifications]))
  end

  if options[:action_cable_allowed] || (ActivityNotification.config.action_cable_enabled && options[:action_cable_allowed] != false)
    options[:optional_targets] ||= {}
    require 'activity_notification/optional_targets/action_cable_channel'
    unless options[:optional_targets].has_key?(ActivityNotification::OptionalTarget::ActionCableChannel)
      options[:optional_targets][ActivityNotification::OptionalTarget::ActionCableChannel] = {}
    end
  end

  if options[:action_cable_api_allowed] || (ActivityNotification.config.action_cable_api_enabled && options[:action_cable_api_allowed] != false)
    options[:optional_targets] ||= {}
    require 'activity_notification/optional_targets/action_cable_api_channel'
    unless options[:optional_targets].has_key?(ActivityNotification::OptionalTarget::ActionCableApiChannel)
      options[:optional_targets][ActivityNotification::OptionalTarget::ActionCableApiChannel] = {}
    end
  end

  if options[:optional_targets].is_a?(Hash)
    options[:optional_targets] = arrange_optional_targets_option(options[:optional_targets])
  end

  options[:printable_notifiable_name] ||= options.delete(:printable_name)
  configured_params
    .merge set_acts_as_parameters_for_target(target_type, [:targets, :group, :group_expiry_delay, :parameters, :email_allowed], options, "notification_")
    .merge set_acts_as_parameters_for_target(target_type, [:action_cable_allowed, :action_cable_api_allowed], options, "notifiable_")
    .merge set_acts_as_parameters_for_target(target_type, [:notifier, :notifiable_path, :printable_notifiable_name, :optional_targets], options)
end

.add_destroy_dependency(target_type, dependent_notifications_option) ⇒ Hash<Symbol, Symbol>

Adds destroy dependency.

Parameters:

  • target_type (Symbol)

    Type of notification target as symbol

  • dependent_notifications_option (Symbol)

    Specified :dependent_notifications option

Returns:

  • (Hash<Symbol, Symbol>)

    Configured dependency options



350
351
352
353
354
355
356
357
358
359
360
# File 'lib/activity_notification/roles/acts_as_notifiable.rb', line 350

def add_destroy_dependency(target_type, dependent_notifications_option)
  case dependent_notifications_option
  when :delete_all, :destroy, :restrict_with_error, :restrict_with_exception
    before_destroy -> { destroy_generated_notifications_with_dependency(dependent_notifications_option, target_type) }
  when :update_group_and_delete_all
    before_destroy -> { destroy_generated_notifications_with_dependency(:delete_all, target_type, true) }
  when :update_group_and_destroy
    before_destroy -> { destroy_generated_notifications_with_dependency(:destroy, target_type, true) }
  end
  { dependent_notifications: dependent_notifications_option }
end

.add_tracked_callback(tracked_callbacks, tracked_action, tracked_proc) ⇒ Object

Adds tracked callback.

Parameters:

  • tracked_callbacks (Array<Symbol>)

    Array of tracked callbacks (Array of [:create or :update])

  • tracked_action (Symbol)

    Tracked action (:create or :update)

  • tracked_proc (Proc)

    Proc or lambda function to execute



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/activity_notification/roles/acts_as_notifiable.rb', line 324

def add_tracked_callback(tracked_callbacks, tracked_action, tracked_proc)
  return unless tracked_callbacks.include? tracked_action

  # FIXME: Avoid Rails issue that after commit callbacks on update does not triggered when optimistic locking is enabled
  # See the followings:
  #   https://github.com/rails/rails/issues/30779
  #   https://github.com/rails/rails/pull/32167

  # :nocov:
  if !(Gem::Version.new("5.1.6") <= Rails.gem_version && Rails.gem_version < Gem::Version.new("5.2.2")) && respond_to?(:after_commit)
    after_commit tracked_proc, on: tracked_action
  else
    case tracked_action
    when :create
      after_create tracked_proc
    when :update
      after_update tracked_proc
    end
  end
  # :nocov:
end

.add_tracked_callbacks(target_type, tracked_option = {}) ⇒ Hash<Symbol, Symbol>

Adds tracked callbacks.

Parameters:

  • target_type (Symbol)

    Type of notification target as symbol

  • tracked_option (Hash) (defaults to: {})

    Specified :tracked option

Returns:

  • (Hash<Symbol, Symbol>)

    Configured tracked callbacks options



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/activity_notification/roles/acts_as_notifiable.rb', line 303

def add_tracked_callbacks(target_type, tracked_option = {})
  tracked_callbacks = [:create, :update]
  if tracked_option[:except]
    tracked_callbacks -= tracked_option.delete(:except)
  elsif tracked_option[:only]
    tracked_callbacks &= tracked_option.delete(:only)
  end
  if tracked_option.has_key?(:key)
    add_tracked_callback(tracked_callbacks, :create, ->{ notify target_type, tracked_option })
    add_tracked_callback(tracked_callbacks, :update, ->{ notify target_type, tracked_option })
  else
    add_tracked_callback(tracked_callbacks, :create, ->{ notify target_type, tracked_option.merge(key: notification_key_for_tracked_creation) })
    add_tracked_callback(tracked_callbacks, :update, ->{ notify target_type, tracked_option.merge(key: notification_key_for_tracked_update) })
  end
  { tracked: tracked_callbacks }
end

.arrange_optional_targets_option(optional_targets_option) ⇒ Hash<ActivityNotification::OptionalTarget::Base, Hash>

Arrange optional targets option.

Parameters:

  • optional_targets_option (Symbol)

    Specified :optional_targets option

Returns:



365
366
367
368
369
370
371
372
373
# File 'lib/activity_notification/roles/acts_as_notifiable.rb', line 365

def arrange_optional_targets_option(optional_targets_option)
  optional_targets_option.map { |target_class, target_options|
    optional_target = target_class.new(target_options)
    unless optional_target.kind_of?(ActivityNotification::OptionalTarget::Base)
      raise TypeError, "#{optional_target.class.name} for an optional target is not a kind of ActivityNotification::OptionalTarget::Base"
    end
    optional_target
  }
end

.available_dependent_notifications_optionsArray<Symbol>

Returns array of available notifiable options in acts_as_notifiable.

Returns:

  • (Array<Symbol>)

    Array of available notifiable options



289
290
291
292
293
294
295
296
297
# File 'lib/activity_notification/roles/acts_as_notifiable.rb', line 289

def available_dependent_notifications_options
  [ :delete_all,
    :destroy,
    :restrict_with_error,
    :restrict_with_exception,
    :update_group_and_delete_all,
    :update_group_and_destroy
  ].freeze
end

.available_notifiable_optionsArray<Symbol>

Returns array of available notifiable options in acts_as_notifiable.

Returns:

  • (Array<Symbol>)

    Array of available notifiable options



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/activity_notification/roles/acts_as_notifiable.rb', line 271

def available_notifiable_options
  [ :targets,
    :group,
    :group_expiry_delay,
    :notifier,
    :parameters,
    :email_allowed,
    :action_cable_allowed,
    :action_cable_api_allowed,
    :notifiable_path,
    :printable_notifiable_name, :printable_name,
    :dependent_notifications,
    :optional_targets
  ].freeze
end