Open sedubois opened 4 years ago
OK, so there are several things here.
Some of what you list is "generic" ActiveStorage stuff as mentioned at https://github.com/Dreamersoul/administrate-field-active_storage/issues/30#issuecomment-592530628. I have done very little work with ActiveStorage (and none at all with Cloudinary) but it does sound like Administrate shouldn't be concerned with them.
Then there's generating the dashboards. The dashboard generator currently fails at namespaced models, and this is a known bug. Your approach of moving things around manually looks correct to me as a workaround.
The issue with admin_blob_path
looks to me like another namespacing issue. Possibly it should be admin_active_storage_blob_path
, but the namespace got lost along the way. Would you be able to find where this is happening?
The issue with admin_blob_path looks to me like another namespacing issue. Possibly it should be admin_active_storage_blob_path, but the namespace got lost along the way. Would you be able to find where this is happening?
I don't have time right now to dig more but there seems to be something specific going on with ActiveStorage. I use Globalize and PaperTrail for which I did manage to generate everything correctly (see here). It seems that it is only in the case of ActiveStorage that the generated path look this way.
OK so the main issue was that ActiveStorage's routing file contains this:
resolve("ActiveStorage::Blob") { |blob, options| route_for(:rails_blob, blob, options) }
resolve("ActiveStorage::Attachment") { |attachment, options| route_for(:rails_blob, attachment.blob, options) }
This allows to resolve polymorphic_path(blob)
as blob_path
, but polymorphic_path([:admin, blob])
then results in admin_blob_path
which doesn't exist and we can't fix it by making another resolve
clause for the admin route (see resolve documentation):
NOTE: This custom behavior only applies to simple polymorphic URLs where a single model instance is passed and not more complicated forms
NOTE: The +resolve+ method can't be used inside of a scope block such as +namespace+ or +scope+ and will raise an error if it detects that it is.
Instead this seems to work (can the repetitions admin_blob
/admin_blobs
/edit_admin_blob
be somehow simplified?):
Rails.application.routes.draw do
...
namespace :admin do
...
namespace :active_storage do
resources :blobs
resources :attachments
end
end
direct(:admin_blob) { |blob, options| route_for(:admin_active_storage_blob, blob, options) }
direct(:admin_blobs) { |opts| route_for(:admin_active_storage_blobs, opts) }
direct(:edit_admin_blob) { |blob, opts| route_for(:edit_admin_active_storage_blob, blob, opts) }
direct(:admin_attachment) { |attachment, options| route_for(:admin_active_storage_attachment, attachment, options) }
direct(:admin_attachments) { |opts| route_for(:admin_active_storage_attachments, opts) }
direct(:edit_admin_attachment) { |attachment, opts| route_for(:edit_admin_active_storage_attachment, attachment, opts) }
end
As a reminder the controllers and dashboards can be generated as follows:
rails g administrate:dashboard ActiveStorage::Blob
rails g administrate:dashboard ActiveStorage::Attachment
mkdir app/controllers/admin/active_storage/ app/dashboards/active_storage/
mv app/controllers/admin/blobs_controller.rb app/controllers/admin/active_storage/
mv app/controllers/admin/attachments_controller.rb app/controllers/admin/active_storage/
mv app/dashboards/blob_dashboard.rb app/dashboards/active_storage/
mv app/dashboards/attachment_dashboard.rb app/dashboards/active_storage/
Then the dashboard configs and the controllers need to be tweaked:
# app/dashboards/active_storage/blob_dashboard.rb
ATTRIBUTE_TYPES = {
id: Field::Number,
key: Field::String,
# TODO figure out how to support the actual ActiveStorage::Filename type
# (this throws an error when creating new record)
filename: Field::String,
content_type: Field::String,
metadata: Field::Text,
byte_size: Field::Number,
checksum: Field::String,
created_at: Field::DateTime,
attachments: Field::HasMany.with_options(class_name: "ActiveStorage::Attachment"),
preview_image_attachment: Field::HasOne.with_options(class_name: "ActiveStorage::Attachment"),
preview_image_blob: Field::HasOne.with_options(class_name: "ActiveStorage::Blob"),
}.freeze
# app/dashboards/active_storage/attachment_dashboard.rb
ATTRIBUTE_TYPES = {
id: Field::Number,
name: Field::String,
record: Field::Polymorphic.with_options(classes: [Post]),
blob: Field::BelongsTo.with_options(class_name: "ActiveStorage::Blob"),
blob_id: Field::Number,
created_at: Field::DateTime,
}.freeze
At this point we have a way to browse and edit the blob and attachment tables:
The next step would be to be able to actually preview the blobs and download them.
Thank you for the research @sedubois! It looks to me that the solution here is to document this. Possibly on the Wiki. I'll tag it for documentation.
Rails 6.1 is adding a new ActiveStorage::VariantRecord
model with accompanying *variant_record*
routes, so they also need to be added. Here is a more general method (instead of 9 separate direct
lines) to do so:
namespace :admin do
namespace :active_storage do
resources :blobs
resources :attachments
resources :variant_records
end
end
[:blob, :attachment, :variant_record].each do |model|
%W[admin_#{model} admin_#{model}s edit_admin_#{model}].each do |route_name|
direct(route_name.to_sym) do |blob, options|
route_for(route_name.sub("admin", "admin_active_storage").to_sym, blob, options)
end
end
end
The open-source Spina CMS has a "media picker" feature built on top of the Trix editor and Active Storage, so it looks feasible:
module Spina
class Image < ApplicationRecord
# ...
has_one_attached :file
FYI, we managed to implement a basic "media gallery" in our app admin dashboard (on top of administrate) to re-attach Active Storage blobs instead of needing to re-upload files every time. We use Stimulus to listen to the Trix editor events and deliver the view through ajax using Turbo Frames.
What were you trying to do?
I tried to generate dashboards to visualize/manage our ActiveStorage blobs and attachments, hopefully in order to see which kind of blobs we have in the DB, which records are pointing to them, what size they have, which ones are orphan, etc.
Or is there some "official" way to get such a "media library/manager" in Rails/ActiveStorage? How do other Rails projects answer this need? Wordpress has a "Media library" showing thumbnails of all available media and it would be great to get a basic equivalent of this. Under the hood as a storage service I am currently trialling cloudinary, which does act as such a media library where all assets can be browsed, but the correspondence between the assets visible there and the contents of the DB is not obvious. For instance, how to find where a specific image found in Cloudinary is used in our app? How to identify orphan files? Sometimes I've been seeing inexplicable
ActiveStorage::IntegrityError
which led me to feel like more transparency into what's happening would be useful. EDIT: I was using Cloudinary as storage service and the IntegrityError seemed to occur when I moved the images into sub-folders in the Cloudinary dashboard (to organize the assets a bit better). I didn't realize this would affect the access from ActiveStorage. Based on this comment it looks like I might have been able to re-establish the link by updating the blob'skey
.Anyway, adding
active_storage_blobs
andactive_storage_attachments
to Administrate looks like a useful solution to better visualize the ActiveStorage assets, which led me to write this issue.NB: this question concerns managing all ActiveStorage assets, not how to display/edit an indiviual resource's ActiveStorage attachment. I already managed to do that using
administrate-field-active_storage
.What did you end up with (logs, or, even better, example apps are great!)?
To start, I tried to make a dashbord for ActiveStorage::Blob. As the model is namespaced, first it is necessary to fix the location of the generated controller and dashboard (see https://github.com/thoughtbot/administrate/issues/1291#issuecomment-545054789):
I also add the route:
OK, now I can view
/admin
, with "Active Storage Blobs" visible in the left menu:However if I click it, which goes
/admin/active_storage/blobs
, I getundefined method 'admin_blob_path'
thrown bypolymorphic_path([namespace, resource])
and variouslink_to
in_collection.html.erb
. I can workaround all these issues with this kind of hack:What versions are you running?