user4965201
user4965201

Reputation: 973

Rails 4 public_activity with two types of owner

I am using public_activity gem in my app and to track the owner who created the record to track the event model, I am using devise and I have two types of users, individual and company , my code is this to track the owner

class Event < ActiveRecord::Base
  include PublicActivity::Model
  tracked owner: ->(controller, model) { controller && controller.current_individual }

end

Here the problem is I want to track the owner as current_company if the company is signed in , but public_activity gem doesnt provide multiple owner types

I want to track that if the current_individual is signed in then the owner type and id would be of individual and if company is signed in then according to that.

what i am doing is

class Event < ActiveRecord::Base
  include PublicActivity::Model
  tracked owner: ->(controller, model) { controller &&  controller.individual_signed_in? ? controller.current_individual : controller.current_company  }
end

but i am getting owner id and type when individual is logged in but not getting owner type and id when company is logged in

Upvotes: 1

Views: 1055

Answers (2)

Jon Kern
Jon Kern

Reputation: 3235

I hope my approach does not make people's eyes bleed. It might not be the best code to copy (happy for tips) but I hope the idea is helpful (and it does work) :-p

I have a large network of model elements that can be used to create a "preplan" for anything from a single building to an entire university campus full of dozens of structures, hundreds of fire hydrants... And a structure can have all kinds of things (roof, accesses (doors), hazardous materials, fire detection systems, protection systems, sprinklers, and so on).

Any element can have one or more photos attached/edited/deleted.

Polymorphic owner: I like to adorn the activity tracking to show who added/edited/deleted a photo and for what "owning" element, regardless of the class.

So a reusable partial is used to put up a nice "Add Photos" drag-n-dop landing zone for any element. The partial is passed a local to tell what the "owning" element is (the structure, or a roof, or an alarm system):

= render partial: 'photos/photos', locals: {owner: item}

And inside that partial, the owning element is parsed a bit further... (probably unnecessary, could just pass the owner along and parse it out in the controller, now that I look at it!)

= link_to new_photo_path(:owner_id => owner, :owner_class => owner.class, :tag => owner.class::TAG),

Then the controller has some actions to deal with the "owning" object...

class PhotosController < ApplicationController

  before_action :set_owning_object, only: [:new, :index]
  before_action :get_owning_object, only: [:create, :update]
  ...      
  def set_owning_object
    @owner_class = params["owner_class"]
    @owner_id = params["owner_id"]
    @photo_tag = params["tag"]
    session[:owner_class] = @owner_class if @owner_class
    session[:owner_id] = @owner_id if @owner_id
    session[:photo_tag] = @photo_tag if @photo_tag
    # Unsafe reflection method constantize called with parameter value
    # Let's whitelist the allowable classes that can have Photos
    if @owner_class
      if (%w(Preplan Structure Organization PreplanLayout StagingArea) + Structure::Default_Elements).include? @owner_class
        @owning_object = (@owner_class.constantize).find(@owner_id)
      else
        Rails.logger.warn("OWNING CLASS NEEDS TO BE ADDED: #{@owner_class} was not cleared for use in #{__method__}.")
      end
    else
      @owning_object = nil
    end

  end

And the controller manually adds activities under the desired CRUD actions with this highly polymorphic "owner" class (which includes HasActivities module that includes the public_activity gem):

  def create
    authorize Photo
    @photo = Photo.new(photo_params)
    @photo.tags << @photo_tag
    add_photo(@owning_object, @photo)

    respond_to do |format|
      if @photo.save
        if @owning_object.respond_to? :create_activity
          @owning_object.create_activity(:add_photo,
                                         owner: view_context.current_user,
                                         organization: view_context.current_org,
                                         recipient: @owning_object,
                                         parameters: { name: @photo.caption, file: @photo.photo_name, owner: @owning_object.name })
        end
        pseudo_url = owning_object_url(@owning_object)
        format.html {
          redirect_to pseudo_url, notice: "Photo was successfully added."
        }
        format.json {render json: { photo: render_to_string(partial: 'photos/photo', layout: false, :formats => [:html], locals: { photo: @photo, photo_counter: @photo.id }) }}
      else
        format.html {render action: 'new'}
        format.json {render json: { error: @photo.errors.full_messages.join(',') }, :status => 406}
      end
    end
  end

  def update
    authorize @photo
    if @photo.update_attributes(photo_params)
      @photo.create_activity(:update,
                             owner: view_context.current_user,
                             organization: view_context.current_org,
                             recipient: @owning_object,
                             parameters: { name: @photo.caption, file: @photo.photo_name })
      respond_to do |format|
        format.js
      end
    end
  end

  def destroy
    authorize @photo
    @photo.create_activity(key: 'photo.destroy',
                           owner: current_user,
                           organization: view_context.current_org,
                           parameters: { name: @photo.caption, file: @photo.photo_name })
    @photo.destroy

    respond_to do |format|
      format.html {redirect_to :back}
      format.json {head :no_content}
    end
  end

The aforementioned HasActivities module:

module HasActivities
  extend ActiveSupport::Concern

  included do
    include PublicActivity::Common
  end
end

Sample Polymorphic Activities

Upvotes: 1

dimakura
dimakura

Reputation: 7655

Actually owner is polymorphic as can be seen here:

create_table :activities do |t|
  t.belongs_to :trackable, :polymorphic => true
  t.belongs_to :owner, :polymorphic => true
  t.string  :key
  t.text    :parameters
  t.belongs_to :recipient, :polymorphic => true

  t.timestamps
end

So it's not a problem to assign owner of different type.

If you want to assign company as owner when company's user is signed-in and a person, when personal account is signed in, you should just make appropriate changes in your code:

tracked owner: ->(controller, model) { controller.current_user.organization? ? controller.current_user.organization : controller.current_user.person }

Upvotes: 1

Related Questions