Я надеюсь, что мой подход не заставляет людей кровоточить глаза. Возможно, это не лучший код для копирования (рад за подсказки), но я надеюсь, что идея будет полезной (и она действительно работает) :-p
У меня есть большая сеть элементов модели, которые можно использовать для создания «предварительного плана» чего угодно, от одного здания до целого университетского городка, состоящего из десятков строений, сотен пожарных гидрантов... И в строении могут быть все виды вещи (кровля, входы (двери), опасные материалы, системы обнаружения пожара, системы защиты, спринклеры и т. д.).
К любому элементу может быть прикреплена/отредактирована/удалена одна или несколько фотографий.
Полиморфный владелец: мне нравится украшать отслеживание активности, чтобы показать, кто добавил/редактировал/удалил фото и для какого элемента владеет, независимо от класса.
Таким образом, повторно используемый частичный элемент используется для создания красивой целевой зоны перетаскивания «Добавить фотографии» для любого элемента. Партиал передается локальному элементу, чтобы сообщить, что является элементом владения (структурой, крышей или системой сигнализации):
= render partial: 'photos/photos', locals: {owner: item}
И внутри этого партиала элемент-владелец анализируется немного дальше... (вероятно, ненужный, можно было бы просто передать владельца и проанализировать его в контроллере, теперь, когда я смотрю на это!)
= link_to new_photo_path(:owner_id => owner, :owner_class => owner.class, :tag => owner.class::TAG),
Затем у контроллера есть некоторые действия для работы с «владеющим» объектом...
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
И контроллер вручную добавляет действия под нужными действиями CRUD с помощью этого высокополиморфного класса «owner» (который включает модуль HasActivities, включающий гем public_activity):
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
Вышеупомянутый модуль HasActivities:
module HasActivities
extend ActiveSupport::Concern
included do
include PublicActivity::Common
end
end
![Пример полиморфных действий](https://i.stack.imgur.com/28yht.png)
person
Jon Kern
schedule
31.08.2017