简体   繁体   English

在define_method定义的方法上调用super

[英]Calling super on a method defined by define_method

I have created a Model class where I define methods based on a method (attribute) called in User (which inherits from Model). 我创建了一个Model类,我根据在User中调用的方法(属性)定义方法(继承自Model)。 The problem is that I cannot override the method defined by define_method, and call super to pass to the defined method. 问题是我无法覆盖define_method定义的方法,并调用super来传递给定义的方法。 I guess this is because the defined method is added to User itself, and not to the Model, so it actually has no method in the superclass (ie Model). 我想这是因为定义的方法被添加到User本身,而不是模型,所以它实际上没有超类中的方法(即模型)。

The reason I want to do this is because most attributes should be saved to the database directly, while some attributes, like password, needs some additional processing. 我想这样做的原因是因为大多数属性应该直接保存到数据库中,而某些属性(如密码)需要一些额外的处理。

class Model
  def self.attribute(name)
    define_method(name) do
      self
    end
  end  
end

class User < Model
  attribute :password
end

class User2 < Model
  attribute :password

  def password
    super
  end
end

@user = User.new
puts @user.password # => <User:0x00000100845540>

@user2 = User2.new
puts @user2.password
# define_super.rb:17:in `password': super: no superclass method 
# `password' for #<User2:0x00000100845578> (NoMethodError)
# from define_super.rb:25:in `<main>'

Is there any way I could change the code to get this working? 有什么办法可以改变代码来实现这个目的吗? I need a way to override dynamically created methods. 我需要一种方法来覆盖动态创建的方法。

Define the method on the superclass : superclass上定义方法:

class Model
  def self.attribute(name)
    superclass.send :define_method, name do
      self
    end
  end  
end

The way Rails deals with this is that there are multiple ways to get attributes. Rails处理这个问题的方法是有多种获取属性的方法。 One of these is (by convention) never overridden so it can be used in your defined methods: 其中一个(按惯例)从未被覆盖,因此可以在您定义的方法中使用它:

# This method is never overridden, but also rarely used as a public method
def[](key)
  # Returns value of `key` attribute
end

# This is the effective default implementation of an attribute
def att1
  self[:att1]
end

# This shows how you can have custom logic but still access the underlying value
def att2
  self[:att2] unless self[:att2].empty?
end

Using superclass.send :define_method might lead to some unexpected behaviour, because that method would be available to every Model subclasses. 使用superclass.send :define_method可能会导致一些意外行为,因为该方法可用于每个Model子类。

It is well described in this article https://thepugautomatic.com/2013/07/dsom/ 本文详细介绍了https://thepugautomatic.com/2013/07/dsom/

So following this article you could define your attribute method like this 因此,按照本文,您可以像这样定义attribute方法

class Model
  MODULE_NAME = :DynamicAttributes

  def self.attribute(name)
    if const_defined?(MODULE_NAME, _search_ancestors = false)
      mod = const_get(MODULE_NAME)
    else
      mod = const_set(MODULE_NAME, Module.new)
      include mod
    end

    mod.module_eval do
      define_method(name) do
        self
      end
    end
  end
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM