Burak Özmen
Burak Özmen

Reputation: 873

Implementing notifications models in Rails way

I am implementing a notification system for my application. I have many type of notifications, each of them has different number of references to other users. For example, Progress Updated notification has no references to any other user, while Agency Added Patient notification has 2 references, 1 to an admin and other one to a patient.

I've implemented all these types as a different ActiveRecord object, and created a model for each one. Each of them acts_as a Notification, where Notification is actable (see active_record-acts_as gem. Each different type of notification has its own table, and its own very specific columns. Since they are about different events, they have their own texts.

Initially, I hard coded this texts into the model methods, like this:

class Notification < ActiveRecord::Base
    actable
end

class NotificationTypeA < ActiveRecord::Base
    acts_as :notification

    def notify
        "This is notification A."
    end
end

class NotificationTypeB < ActiveRecord::Base
    acts_as :notification

    def notify
        user = User.find(self.notification_user_id)
        "This is notification B about the user #{user.name}"
    end
end

And render the notifications like this:

// _header.html.erb
<% current_user.notifications.where(:is_read => false).order('created_at DESC').each do |n| %>
     <li style="padding-top: 15px; padding-right: 6px;">
         <%= render :partial => "notifications/notification", :locals => {:n => n} %>
     </li>
     <% end %>

// notifications/_notification.html.erb
<%= n.specific.notify %>

However, I know it is not a good practice to incorporate view elements to models. Additionally, I can not use Rails Internalization (I18n) methods in models, so I can not translate them. If I go the other way, I can create a new view for each notification type, which they will only differ in their notification texts.

Which one is a better practice for a notification system? Or do you have any suggestions that make this better? I thought this one is a pretty good until I noticed that it is a bad practice and I am unable to use internalization methods in my solution.

Upvotes: 1

Views: 634

Answers (1)

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230521

First, it's not "internalization", it's "internationalization". Quite a mouthful, that's why it's always called "I18n".

Second, there's an easy solution: return i18n keys (along with dynamic data) from notification objects

def notify
  [
    'notifications.type_b.text', # or whatever
    { username: User.find(self.notification_user_id).name },
  ] 
end

Then in locale files

notifications:
  type_b:
    text: "This is notification B about the user %{username}"

Then in the view

// notifications/_notification.html.erb
<%= t(*n.specific.notify) %>

t(*n.specific.notify) is more or less like this:

notif_data = n.specific.notify

key = notif_data.first # 'notifications.type_b.text'
data = notif_data.last # { username: 'John' }

I18n.t(key, data)

Oh and by the way

You can use I18n in models

def notify
  I18n.t('notifications.type_b.text', username: 'John')
end

Upvotes: 3

Related Questions