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
