ActiveRecord :: Base.send:define_method会影响所有模型实例

时间:2013-10-11 04:51:58

标签: ruby-on-rails ruby activerecord rails-activerecord

我确信这与我对Ruby内部的缺乏理解有关,但是这里有:

基本上我希望能够在我的资产文件夹中设置一个'附件目录',只需在模型顶部附近调用has_attachments_folder "videos"即可。不要担心方法的目的是什么,我有一部分工作。以下示例用法。

Video.rb

class Video < ActiveRecord::Base
    has_attachment_assets_folder "videos"
end

Email.rb

class Email < ActiveRecord::Base
    has_attachment_assets_folder "attachments/email"
end

activerecord_base.rb

def self.has_attachment_assets_folder(physical_path, arguments={})
    ActiveRecord::Base.send :define_method, :global_assets_path do |args={}|
        # Default to using the absolute system file path unless the argument
        # :asset_path => true is supplied
        p = "#{ Rails.root }/app/assets/#{ physical_path }"
        if args[:asset_path]
            p = "/assets/#{ physical_path.include?("/") ? physical_path.partition("/")[2..-1].join("/") : physical_path }"
        end
        p.squeeze!("/")
        logger.info "Defining global_assets_path...class name: #{ self.class.name }, physical_path: #{ physical_path }, p: #{ p }"
        p
    end
end

基本上,global_assets_path方法应该返回调用has_attachment_assets_folder指定的附件目录的完整或相对路径。

has_attachment_assets_folder "videos", :asset_path => true = "/assets/videos" has_attachment_assets_folder "attachments/email", :asset_path => true = "/assets/attachments/email"

问题是,如果我在Rails应用程序的常规上下文中使用它(一次访问一个模型),它可以正常工作。但是我正在运行一个主要的迁移,它要求我在迁移文件中一次使用多个模型。似乎每个模型都共享方法global_assets_path,因为会发生以下情况:

示例1

Email.all.each do |e|
    puts e.global_assets_path(:asset_path => true) # Correct output of "/assets/attachments/email"
end
Video.all.each do |v|
    puts v.global_assets_path(:asset_path => true) # Correct output of "/assets/video"
end

示例2

test = Video.pluck(:category)
Email.all.each do |e|
    puts e.global_assets_path(:asset_path => true) # Correct output of "/assets/attachments/email"
end
Video.all.each do |v|
    puts v.global_assets_path(:asset_path => true) # Incorrect output of "/assets/attachments/email" because the Video model was instantiated for the first time on the 1st line.
end

1 个答案:

答案 0 :(得分:0)

你在错误的事情上打电话给define_method。这样:

ActiveRecord::Base.send :define_method, :global_assets_path

正在向define_method发送ActiveRecord::Base,以便将global_assets_path方法添加到ActiveRecord::Base。当你这样说:

class Email < ActiveRecord::Base
    has_attachment_assets_folder "attachments/email"
end

您希望将global_assets_path方法添加到Email,而不是ActiveRecord::Base。在self电话中Emailhas_attachment_assets_folder,因此您想说:

def self.has_attachment_assets_folder(physical_path, arguments = {})
    define_method :global_assets_path do |args={}|
        ...
    end
end

global_assets_pathself子类)上定义ActiveRecord::Base并单独留下ActiveRecord::Base