Adding method to class in Ruby
-
15-04-2021 - |
문제
I am trying to add a method to Observable, so that for a class that includes it, it can call the method observe_attribute :attribute
that would generate a method attribute=
with the logic to check to see if the value has changed:
module Observable
def observe_attribute(attribute)
raise NameError, "#{self.class} does not contain #{attribute}" unless instance_variables.include? "@#{attribute}"
eval %" def #{attribute}=(new_value)
unless @#{attribute} == new_value
changed
notify_observers
puts 'ok'
end
end
";
end
end
However, for the following class, the last call to observe_attribute :att
results in a NoMethodError, while the commented one does not:
class Test
include Observable
def initialize
@att = 3
#observe_attribute :att
end
observe_attribute :att
end
What would I need to do in order for the last call to observe_attribute
to work correctly?
해결책
When you include a module, the methods contained in it are loaded into the "instance" level of methods.
If you want to include methods from a module into the Class scope, you have to extend the module.
Or if you need both, but you want to package it as a single "include", you can extend the "included" method of the module you're extending and do a bit of magic with it by extending and including 2 modules from there.
module Observable
def self.included(other)
other.send(:include, InstanceMethods)
other.send(:extend, ClassMethods)
end
module InstanceMethods
def some_helper
puts "called some_helper"
end
def other_helper
puts "called other_helper"
end
end
module ClassMethods
def observe_attribute(name)
# do something useful, maybe in your case define the method
define_method("#{name}=") do |val|
puts "called #{name}= #{val}"
# bla bla
some_helper
other_helper
# end
puts "done"
end
end
end
end
class Foo
include Observable
observe_attribute :foo
end
Now you can call ...
o = Foo.new
o.foo = :bar
And then the result would be ...
called foo= bar
called some_helper
called other_helper
done