开发者

Calling super on a method defined by define_method

开发者 https://www.devze.com 2022-12-15 03:19 出处:网络
I have created a Model class where I define methods based on a method (attribute) called in User (which inherits from Model). The problem is that I cannot override the 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). The problem is that I cannot override the method defined by define_method, and call super to pass to the defined method. 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 (i.e. Model).

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:

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. 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.

It is well described in this article https://thepugautomatic.com/2013/07/dsom/

So following this article you could define your attribute method like this

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
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号