扩展ActiveStorage :: Attachment - 添加自定义字段

时间:2018-06-09 03:31:03

标签: ruby-on-rails attachment

我想扩展ActiveStorage :: Attachment类,并为附件的可见性添加一个enum属性。

我最初的方法是在\ 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)

不幸的是,它只是部分工作: 虽然enum方法公开可见?和privately_visible?在视图中可用,它们在控制器中不可用。当调用控制器中的任何方法时,枚举似乎已经消失。我得到一个&#34; NoMethodError - 未定义的方法&#34;错误。 令人惊讶的是,一旦enum方法在控制器中被调用一次,它们在视图中也不再可用。 我假设ActiveStorage :: Attachment类被动态重新加载,并且扩展会丢失,因为它们仅在初始化期间添加。

有什么想法吗?

4 个答案:

答案 0 :(得分:3)

  

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

你是对的。在应用程序启动后以及每次重新加载代码时,使用PostfixSQLConfig混合模块:

Rails.configuration.to_prepare do
  ActiveStorage::Attachment.send :include, ::ActiveStorageAttachmentExtension
end

答案 1 :(得分: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的方法,如下所示:

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

答案 2 :(得分:0)

如果有人遇到这个问题,我不喜欢这里输入的解决方案,所以我想出了另一种方法。不能100%地确定它是否与Rails.configuration.to_prepare解决方案一样好,但是我喜欢的是它只是app/models目录中的一个文件,因此配置文件中的其他地方没有什么神奇之处您的项目。

我制作了一个名为app/models/active_storage/attachment.rb的文件。因为它在您的项目中,所以它的加载优先于Gem版本。然后在内部加载Gem版本,然后使用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

有点讨厌的部分是查找原始文件,因为我们的文件优先,因此我们通常无法找到它。在生产中这不是必需的,因此如果您喜欢我的话,可以在其周围加上if Rails.env.production?

答案 3 :(得分:0)

这在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