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
精彩评论