添加直接在模型上调用的方法

add methods directly called on model

我想实现我自己的在 class 加载时调用的方法。

我的第一个例子来自 PaperClip,其中行 has_attached_file 直接写在 class 上,指的是周围某处的另一个植入 (lib/Paperclip)。

我想实现一个方法,以便在 class 上调用其他方法。我希望它看起来 link 和 before_filter 但在模特身上。

这个想法是不必在您想要的每个方法上手动调用特定方法。模型开头的一行就足够了。

我尝试使用 Concern 但据我所知,我只能定义 class/instance 方法,包括一些依赖项等等。

我想为 2 个方法 go_for_itdo_it_again 调用方法 watch_here。我不想这样称呼它:

def go_for_it
  watch_here :go_for_it
  puts "Do it!"
end

def do_it_again
  watch_here :do_it_again
  puts "Do it again!"
end

private
def watch_here(name)
  puts "I'm watching on #{name}"
end

我宁愿有这样的东西:

watch_here :go_for_it, :do_it_again

谢谢你的帮助!

啊,元编程的乐趣!要向 class 动态添加方法或行为,您可以使用 instance_eval and class_eval.

请注意,这是一个非常高级的主题,您应该首先对 classes、模块和消息传递 Ruby 的工作原理有一个清晰的了解!


module Magic
  def self.included(base)
    base.extend ClassMethods
  end
  module ClassMethods
    def make_awesome
      # self here is the class - so we are adding class methods
      self.instance_eval do
        def magic_class_method
          puts "I'm a metamagical class method, deal with it."
        end
        # you could call another class method such as
        # before_save :magic_instance_method
      end
      self.class_eval do
        def magic_instance_method
          puts "I'm metamagical instance method, deal with it."
        end
      end
    end
  end
end

class Test
  include Magic
  make_awesome
end

Test.magic_class_method 
# => "I'm a metamagical class method, deal with it."
Test.magic_instance_method 
# => "I'm metamagical instance method, deal with"

这有点违反直觉,但疯狂背后是有原因的:

instance_eval 中,接收者是测试 class 本身。请记住,class 名称只是一个指向 class Class 实例的常量。

class_eval是模块class的一个方法,意味着接收者将是一个模块或class。您传递给 class_eval 的块在 class 的上下文中进行评估,就像您使用 class 关键字声明 class 时一样。

补充阅读:

所以我自己测试了这个 && 进一步 @max 的回答(无论如何我有点想,但他肯定正确地指出了它)...


您可以使用许多资源:

经过一些简短的研究,我发现了这个问题: Ruby on Rails - passing a returned value of a method to has_attached_file. Do I get Ruby syntax wrong?

The problem is that you're trying to use an instance method picture_sizes_as_strings in a declaration (has_attached_image)

您要查找的是与声明 class 有关的内容。我在这方面的经验和你一样多,所以我写这篇文章是为了我自己的利益:

For those coming from static object oriented languages, such as C++ and Java, the concept of open classes is quite foreign. What does it mean that Ruby has open classes? It means that at run time the definition of a class can be changed. All classes in Ruby are open to be changed by the user at all times.

class Talker
  def self.say(*args)
    puts "Inside self.say"
    puts "self = #{self}"
    args.each do |arg|
      method_name = ("say_" + arg.to_s).to_sym
      send :define_method, method_name do
        puts arg
      end
    end
  end
end

class MyTalker < Talker
  say :hello
end

m = MyTalker.new

m.say_hello

似乎如果您 delcare class,它将 运行 初始化的声明性 (?) 方法。这些方法可用于填充对象的其他部分...在 has_many :associations 的情况下,它将创建 @parent.associations.

的实例方法

因为 ActiveRecord::Concerns are modules, you need to treat them as such (according to the epic tutorial 我发现了):

#app/models/concerns.rb
require 'active_support/concern'

module Helper
  extend ActiveSupport::Concern

  module ClassMethods
    def help(*args) #-> each argument represents a method
      args.each do |arg|
        method_name = ("say_" + arg.to_s).to_sym
        send :define_method, method_name do
          puts arg
        end
      end
    end
  end

end

#app/models/x.rb
class X < ActiveRecord::Base
  include Helper
  help :help
end

@x = X.new
@x.say_help #-> puts "help"

[[仍在研究其余部分]] -- 如何添加到实例方法;似乎 super 不太好用