Module: ActivityNotification::Renderable

Included in:
ORM::ActiveRecord::Notification, ORM::Dynamoid::Notification, ORM::Mongoid::Notification
Defined in:
lib/activity_notification/renderable.rb

Overview

Provides logic for rendering notifications. Handles both i18n strings support and smart partials rendering (different templates per the notification key). This module deeply uses PublicActivity gem as reference.

Instance Method Summary collapse

Instance Method Details

#layout_path(path = nil, root = nil) ⇒ String

Returns layout path from options

Parameters:

  • path (String) (defaults to: nil)

    Layout template name

  • root (String) (defaults to: nil)

    Root path of layout template

Returns:

  • (String)

    Layout template path



196
197
198
199
200
# File 'lib/activity_notification/renderable.rb', line 196

def layout_path(path = nil, root = nil)
  path.nil? and return
  root ||= 'layouts'
  select_path(path, root)
end

#partial_path(path = nil, root = nil, target = nil) ⇒ String

Returns partial path from options

Parameters:

  • path (String) (defaults to: nil)

    Partial template name

  • root (String) (defaults to: nil)

    Root path of partial template

  • target (String, Symbol) (defaults to: nil)

    Target type name to find template

Returns:

  • (String)

    Partial template path



178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/activity_notification/renderable.rb', line 178

def partial_path(path = nil, root = nil, target = nil)
  controller = ActivityNotification.get_controller         if ActivityNotification.respond_to?(:get_controller)
  root ||= "activity_notification/notifications/#{target}" if target.present?
  root ||= controller.target_view_path                     if controller.present? && controller.respond_to?(:target_view_path)
  root ||= 'activity_notification/notifications/default'
  template_key = notifiable.respond_to?(:overriding_notification_template_key) &&
                 notifiable.overriding_notification_template_key(@target, key).present? ?
                   notifiable.overriding_notification_template_key(@target, key) :
                   key
  path ||= template_key.tr('.', '/')
  select_path(path, root)
end

#prepare_assigns(params) ⇒ Hash

Returns assigns parameter for view

Parameters:

  • params (Hash)

    Parameters to add parameters at assigns

Returns:

  • (Hash)

    assigns parameter



206
207
208
# File 'lib/activity_notification/renderable.rb', line 206

def prepare_assigns(params)
  params.delete(:assigns) || {}
end

#prepare_locals(params) ⇒ Hash

Returns locals parameter for view There are three variables to be add by method:

  • notification

  • controller

  • parameters [converted into a HashWithIndifferentAccess]

Parameters:

  • params (Hash)

    Parameters to add parameters at locals

Returns:

  • (Hash)

    locals parameter



218
219
220
221
222
223
224
225
# File 'lib/activity_notification/renderable.rb', line 218

def prepare_locals(params)
  locals = params.delete(:locals) || {}
  prepared_parameters = prepare_parameters(params)
  locals.merge\
    notification: self,
    controller:   ActivityNotification.get_controller,
    parameters:   prepared_parameters
end

#prepare_parameters(params) ⇒ Hash

Prepares parameters with @prepared_params. Converted into a HashWithIndifferentAccess.

Parameters:

  • params (Hash)

    Parameters to prepare

Returns:

  • (Hash)

    Prepared parameters



232
233
234
# File 'lib/activity_notification/renderable.rb', line 232

def prepare_parameters(params)
  @prepared_params ||= ActivityNotification.cast_to_indifferent_hash(parameters).merge(params)
end

#render(context, params = {}) ⇒ String

Renders notification from views.

The preferred way of rendering notifications is to provide a template specifying how the rendering should be happening. However, you can choose using i18n based approach when developing an application that supports plenty of languages.

If partial view exists that matches the “target” type and “key” attribute renders that partial with local variables set to contain both Notification and notification_parameters (hash with indifferent access).

If the partial view does not exist and you wish to fallback to rendering through the i18n translation, you can do so by passing in a :fallback parameter whose value equals :text.

If you do not want to define a partial view, and instead want to have all missing views fallback to a default, you can define the :fallback value equal to the partial you wish to use when the partial defined by the notification key does not exist.

Render a list of all notifications of @target from a view (erb):

<ul>
  <% @target.notifications.each do |notification|  %>
    <li><%= render_notification notification %></li>
  <% end %>
</ul>

Fallback to the i18n text translation if the view is missing:

<ul>
  <% @target.notifications.each do |notification|  %>
    <li><%= render_notification notification, fallback: :text %></li>
  <% end %>
</ul>

Fallback to a default view if the view for the current notification key is missing:

<ul>
  <% @target.notifications.each do |notification|  %>
    <li><%= render_notification notification, fallback: 'default' %></li>
  <% end %>
</ul>

Layouts

You can supply a layout that will be used for notification partials with :layout param. Keep in mind that layouts for partials are also partials.

Supply a layout:

# in views:
# All examples look for a layout in app/views/layouts/_notification.erb
render_notification @notification, layout: "notification"
render_notification @notification, layout: "layouts/notification"
render_notification @notification, layout: :notification

# app/views/layouts/_notification.erb
<p><%= notification.created_at %></p>
<%= yield %>

Custom Layout Location

You can customize the layout directory by supplying :layout_root or by using an absolute path.

Declare custom layout location:

# Both examples look for a layout in "app/views/custom/_layout.erb"
render_notification @notification, layout_root: "custom"
render_notification @notification, layout: "/custom/layout"

Creating a template

To use templates for formatting how the notification should render, create a template based on target type and notification key, for example:

Given a target type users and key notification.article.create, create directory tree app/views/activity_notification/notifications/users/article/ and create the create partial there

Note that if a key consists of more than three parts splitted by commas, your directory structure will have to be deeper, for example:

notification.article.comment.reply => app/views/activity_notification/notifications/users/article/comment/_reply.html.erb

Custom Directory

You can override the default ‘activity_notification/notifications/#target` template root with the :partial_root parameter.

Custom template root:

# look for templates inside of /app/views/custom instead of /app/views/public_directory/activity_notification/notifications/#{target}
render_notification @notification, partial_root: "custom"

Variables in templates

From within a template there are three variables at your disposal:

  • notification

  • controller

  • parameters [converted into a HashWithIndifferentAccess]

Template for key: notification.article.create (erb):

<p>
  Article <strong><%= parameters[:title] %></strong>
  was posted by <em><%= parameters["author"] %></em>
  <%= distance_of_time_in_words_to_now(notification.created_at) %>
</p>

Parameters:

  • context (ActionView::Base)
  • params (Hash) (defaults to: {})

    Parameters for rendering notifications

Options Hash (params):

  • :target (String, Symbol) — default: nil

    Target type name to find template or i18n text

  • :partial_root (String) — default: "activity_notification/notifications/#{target}", controller.target_view_path, 'activity_notification/notifications/default'

    Partial template name

  • :partial (String) — default: self.key.tr('.', '/')

    Root path of partial template

  • :layout (String) — default: nil

    Layout template name

  • :layout_root (String) — default: 'layouts'

    Root path of layout template

  • :fallback (String, Symbol) — default: nil

    Fallback template to use when MissingTemplate is raised. Set :text to use i18n text as fallback.

  • :assigns (Hash)

    Parameters to be set as assigns

  • :locals (Hash)

    Parameters to be set as locals

  • others (Hash)

    Parameters to be set as locals

Returns:

  • (String)

    Rendered view or text as string



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/activity_notification/renderable.rb', line 150

def render(context, params = {})
  params[:i18n] and return context.render plain: self.text(params)

  partial = partial_path(*params.values_at(:partial, :partial_root, :target))
  layout  = layout_path(*params.values_at(:layout, :layout_root))
  assigns = prepare_assigns(params)
  locals  = prepare_locals(params)

  begin
    context.render params.merge(partial: partial, layout: layout, assigns: assigns, locals: locals)
  rescue ActionView::MissingTemplate => e
    if params[:fallback] == :text
      context.render plain: self.text(params)
    elsif params[:fallback].present?
      partial = partial_path(*params.values_at(:fallback, :partial_root, :target))
      context.render params.merge(partial: partial, layout: layout, assigns: assigns, locals: locals)
    else
      raise e
    end
  end
end

#text(params = {}) ⇒ String

Virtual attribute returning text description of the notification using the notification’s key to translate using i18n.

Parameters:

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

    Parameters for rendering notification text

Options Hash (params):

  • :target (String)

    Target type name to use as i18n text key

  • others (Hash)

    Parameters to be referred in i18n text

Returns:

  • (String)

    Rendered text



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/activity_notification/renderable.rb', line 13

def text(params = {})
  k = key.split('.')
  k.unshift('notification') if k.first != 'notification'
  if params.has_key?(:target)
    k.insert(1, params[:target])
  else
    k.insert(1, target.to_resource_name)
  end
  k.push('text')
  k = k.join('.')

  attrs = (parameters.symbolize_keys.merge(params) || {}).merge(
    group_member_count:          group_member_count,
    group_notification_count:    group_notification_count,
    group_member_notifier_count: group_member_notifier_count,
    group_notifier_count:        group_notifier_count
  )

  # Generate the :default fallback key without using pluralization key :count
  default = I18n.t(k, **attrs)
  I18n.t(k, **attrs.merge(count: group_notification_count, default: default))
end