Skip to content

Notification

MariusDoe edited this page Dec 9, 2022 · 5 revisions

The notifications system consists of the Notification base class and specific classes for each type of notification, such as the LendRequestNotification.

Possible implementations of inheritance

To accomplish a kind of inheritance, rails has built-in support for Single Table Inheritance (STI): All data of subclasses is stored inside a single large table. All columns that aren't needed for one specific instance are simply filled with NULL.

Instead of STI, Multiple Table Inheritance (MTI) can be used: Each subclass has its own table for storing custom data. There is one additional table for the base class, which contains information necessary to link to the table of the concrete subclass.

Current implementation

To reduce column bloat, the current design uses MTI over STI. As rails doesn't have built-in support for MTI, an external gem has to be used. It adds the concept of one model acting as another model.

The base model, in our case Notification, is labeled as being actable. Conceptual submodels, e. g. LendRequestNotification, are labeled as acting_as the base model. However, they are not actual subclasses of the base model, but simple models inheriting from ApplicationModel directly.

Although there is no subclass relationship between the base model and any of the specific models, one can still treat an instance of a specific model as an instance of the base model and vice versa. I. e. one can call all methods defined on Notification on an instance of LendRequestNotification and one can call all methods defined on LendRequstNotification on an instance of Notification, provided the instance really corresponds to an instance of the specific class.

If needed, instances of the base class and the specific classes can be converted into one another. The method specific converts instances of the base model into instances of the specific model and the method acting_as does the opposite.

Pushing a notification

To push a notification to a user, an instance of the specific model has to be created and saved to the database. To create a notification of type XYZNotification, which we'll assume has some data named abc, and push it to a user john, do the following:

xyz_notification = XYZNotification.new(user: john, date: Time.now, abc: data)
xyz_notification.save

Creating a new type of notification

You can use this template:

class XYZNotification < ApplicationRecord
  acts_as :notification

  def title
    I18n.t "views.notifications.xyz.title"
  end

  def description
    I18n.t "views.notifications.xyz.description"
  end
end

Replace the three occurences of xyz (class name, title and description) with the name of your notification and don't forget to add the translations. You can of course have translations depend on the custom data.

Displaying custom markup

Each notification type can display custom markup below the description, like buttons, with the help of a partial named similarly to the class: While rendering an instance of SomeInterestingNotification, the partial app/views/notifications/_some_interesting_notification.html.erb will be rendered. If the partial doesn't exist, no error will be thrown. The partial will be given a notification parameter containing the instance of the specific class (SomeInterestingNotification).