ruby-on-railsrubyactiverecordassociationspublic-activity

Using One-to-Many Associations for the Public Activity Rails gem


I just started using the PublicActivity gem and wanted to display the activities of authors throughout the site. In the app an author has many books. When an author releases a new book I'd like there to be a notification to appear in the feed's of users. Trying my best to translate what's shown in the code example here's what I've come up with so far:

Models

class Reader < ActiveRecord::Base
end

class Author < ActiveRecord::Base
    has_many :books
end

class Book < ActiveRecord::Base
  include PublicActivity::Model
  tracked owner: :author, recipient: :reader

  belongs_to :author, :class_name => "Author"
  belongs_to :reader, :class_name => "User"
end

Activities Controller

class ActivitiesController < ApplicationController
  def index
    @activities = PublicActivity::Activity.all
  end
end

Activities Index View

<% @activities.each do |activity| %>
    <%= activity.inspect %> # Using inspect for now to debug
<% end %>

For now in the console I'm creating and appending books to an author (author being the instance variable) as so:

author.books << Book.create(name: "Jaws")

Activities are being recorded but the owner_id and recipient_id are nil when in reality the owner should be the author and the recipient should be the user.

#<PublicActivity::Activity id: 1, trackable_id: 1, trackable_type: "Book", owner_id: nil, owner_type: nil, key: "book.create", parameters: {}, recipient_id: nil, recipient_type: nil, created_at: "2015-04-01 17:36:18", updated_at: "2015-04-01 17:36:18">

Solution

  • To save the author you better use the relation while creating the new book

    author.books.create(name: "Jaws")
    

    Then if you want to save the reader, you'll need to add it in the args hash

    author.books.create(name: "Jaws", reader: some_user)
    

    Note:
    The reason why this prefills the object is that when you call new on an active record relation object, all the conditions inside the condition are used to create the new object, for example

    Book.where(author: some_author).new
    

    This will generate an instance of book and the author_id will be the id of the some_author from the where query.

    So when we did an author.books this created a query

    Book.where(author_id: author.id)
    

    And by calling new, the new book will have the id of the author.

    PS: This also works on multiple args in the where

    Model.where(key1: value1, key2: value2, key3: value3).new
    

    will create a new instance where the attributes key1, key2, key3 are already filled.