10

我想扩展 ActiveStorage::Attachment 类并添加一个枚举属性以显示附件。

我最初的方法是在 \app\models 目录中创建一个新文件 attachment.rb,如下所示。

class ActiveStorage::Attachment < ActiveRecord::Base
    enum visibility: [ :privately_visible, :publicly_visible]
end

这行不通。

欢迎任何建议。Rails 扩展类的方法是什么?

更新

我有一个现在可以部分工作的解决方案。为此,我创建了一个扩展 active_storage_attachment_extension.rb 并将其放在 \lib

module ActiveStorageAttachmentExtension

  extend ActiveSupport::Concern

  included do
    enum visibility: [ :privately_visible, :publicly_visible]

    def describe_me
      puts "I am part of the extension"
    end

  end
end

扩展在 extensions.rb 初始化期间加载

ActiveStorage::Attachment.send(:include, ::ActiveStorageAttachmentExtension)

不幸的是,它只是部分起作用:而枚举方法public_visible?和private_visible?在视图中可用,它们在控制器中不可用。当调用控制器中的任何方法时,枚举似乎已经消失了。我收到“NoMethodError - 未定义的方法”错误。令人惊讶的是,一旦枚举方法在控制器中被调用一次,它们在视图中也不再可用。我假设 ActiveStorage::Attachment 类会动态重新加载,并且扩展会丢失,因为它们仅在初始化期间添加。

有任何想法吗?

4

4 回答 4

6

我假设 ActiveStorage::Attachment 类会动态重新加载,并且扩展会丢失,因为它们仅在初始化期间添加。

你是对的。用于Rails.configuration.to_prepare在应用程序启动后和每次重新加载代码时混合您的模块:

Rails.configuration.to_prepare do
  ActiveStorage::Attachment.send :include, ::ActiveStorageAttachmentExtension
end
于 2018-06-10T20:21:08.213 回答
3

这在 Rails 6 中对我有用。

# frozen_string_literal: true

module ActiveStorageAttachmentExtension
  extend ActiveSupport::Concern

  included do
    has_many :virus_scan_results
  end
end

Rails.configuration.to_prepare do
  ActiveStorage::Attachment.include ActiveStorageAttachmentExtension
end
于 2020-01-08T14:09:36.500 回答
1

正如我在评论中提到的,它需要app/models/active_storage_attachment.rb具有以下内容的文件:

class ActiveStorageAttachment < ApplicationRecord
  enum visibility: [ :privately_visible, :publicly_visible]
end

然后您还需要将整数类型的列可见性添加到表中active_storage_attachments

class AddVisibilityToActiveStorageAttachments < ActiveRecord::Migration[5.2]
  def change
    add_column :active_storage_attachments, :visibility, :integer

  end
end

访问 ActiveStorageAttachment 的新列

我使用我的模型举了一个例子:我有一个用户 has_one_attached :avatar。

例如,我可以访问通过user.avatar.attachment.inspect它返回的 active_storage_attachments 表#<ActiveStorage::Attachment id: 1, name: "avatar", record_type: "User", record_id: 1, blob_id: 3, created_at: "2018-06-03 13:26:20", visibility: 0>

请注意,列的值visibility是纯整数,不是由visibility数组转换的(我仍然想知道为什么)。

avatar_attachment一种可能的解决方法是在 User 模型中定义一个方法,如下所示:

class User < ApplicationRecord
  has_one_attached :avatar

  def avatar_attachment
    ActiveStorageAttachment.find_by(name: 'avatar', record_type: 'User', record_id: self.id)
  end
end

现在user.avatar_attachment.inspect返回#<ActiveStorageAttachment id: 1, name: "avatar", record_type: "User", record_id: 1, blob_id: 3, created_at: "2018-06-03 13:26:20", visibility: "privately_visible">

现在所有与可见性数组相关的方法都可用了。记录更新也有效:

user.avatar_attachment.publicly_visible! # => true
于 2018-06-09T15:10:50.247 回答
-1

If someone else runs into this, I didn't like the solutions entered here so I came up with another way. Not 100% sure if it's as good as the Rails.configuration.to_prepare solution, but the thing I like about it is that it's just one file in the app/models directory, so no magic going on in configuration files somewhere else in your project.

I make a file named: app/models/active_storage/attachment.rb. Because it's in your project it takes loading precedence over the Gem version. Then inside we load the Gem version, and then monkeypatch it using class_eval:

active_storage_gem_path = Gem::Specification.find_by_name('activestorage').gem_dir
require "#{active_storage_gem_path}/app/models/active_storage/attachment"

ActiveStorage::Attachment.class_eval do
  acts_as_taggable on: :tags
end

The slightly nasty part is locating the original file, since we can't find it normally because our new file takes precedence. This is not necessary in production, so you could put a if Rails.env.production? around it if you like I think.

于 2019-01-11T15:44:22.780 回答