I'm attempting to create a class that uses the Singleton module:
class Foo
include Singleton
# def initialize
# puts "Initialized!"
# end
singleton_class.class_eval do
attr_accessor :bar
end
end
The problem here is that bar does not have a default/initial value. I've added a initialize method to my class in hopes that it would be invoked once but this doesn't seem to be the case. What is the proper way to ensure that bar has a value when this class is loaded?
As you implemented it the accessors for #bar are methods of the class Foo, thus you have define #bar as a class instance variable of Foo, not of the instance of Foo:
class Foo
include Singleton
#bar = 0
singleton_class.class_eval do
attr_accessor :bar
end
end
Foo.bar
# => 0
Related
Can somebody help me distinguish When we create methods inside class << self block and when we define normal methods.
I saw somewhere code like this, but I don't know concisely the use cases of them
class Foo
def initialize
end
def bar
end
class << self
def foobar
end
end
end
The methods defined right inside a class block are instance methods:
class Foo
def bar
end
end
Methods defined within class << self inside a class block are class methods:
class Foo
class << self
def baz
end
end
end
Instance methods become available to any instance of a given class:
foo = Foo.new
foo.bar
Whereas class methods can be called directly on the class:
Foo.baz
Attempting to call instance methods on the class or vice versa results in an error:
Foo.bar #=> NoMethodError: undefined method `bar' for Foo:Class
foo.baz #=> NoMethodError: undefined method `baz' for #<Foo:0x00007ffe20055a20>
Another way to define class methods is by prefixing the method name with self.:
class Foo
def self.baz
end
end
You could also define them outside the class block, although this is rarely seen:
def Foo.baz
end
Or likewise:
class << Foo
def baz
end
end
Note that defining methods this way is not limited to classes. You can add methods to arbitrary objects, e.g.:
o = Object.new
def o.hello
"hello from o"
end
o.hello
#=> "hello from o"
Or via:
class << o
def hello
"hello from o"
end
end
Internally, these methods are added to the object's singleton class. It's a special purpose class to hold methods for just that instance:
o.singleton_class.instance_methods(false)
#=> [:hello]
For the Foo class above:
Foo.instance_methods(false) #=> [:bar]
Foo.singleton_class.instance_methods(false) #=> [:baz]
So technically, a class method is just an instance method defined on the class' singleton class.
You may need to read up on Ruby's instance and class methods.
But personally, I'd do
class Foo
class << self
def foobar
end
end
end
instead of
class Foo
def self.foobar
end
end
whenever I want to add some class level attributes, or make a method private etc as
class Foo
private
def self.foobar
end
end
wouldn't work the same as
class Foo
class << self
private
def foobar
end
end
end
Why doesn't this work?
module Greeter
def self.greet
puts "anyang"
end
end
Greeter.greet # => anyang
class KoreanKid
include Greeter
greet
end
# !> NameError : undefined local variable or method `greet' for KoreanKid:Class
KoreanKid.greet
# !> NoMethodError : undefined method `greet' for KoreanKid:Class
When I call greet right inside the KoreanKid class, that's just simply calling a class method right? That's the same thing as KoreanKid.greet right? Why doesn't the above work?
In my module, I'll have a mix of class methods and instance methods... how do I get both to work cleanly?
Kernel#include adds the existing methods in the module as instance methods of the class. To add class methods, you have to use Kernel#extend:
module Foo
def bar
42
end
end
class Baz
extend Foo
end
Baz.bar # => 42
Note that the methods that we extended were instance methods in the original module.
A popular way to do both is to use the Module#included hook to also extend:
module Foo
def bar
:wut
end
def self.included(target)
target.extend ClassMethods
end
module ClassMethods
def baz
:indeed
end
end
end
class Test
include Foo
end
Test.new.bar # => :wut
Test.baz # => :indeed
I know it's possible to define instance methods using class_eval. Is it possible to define class methods within the context of class_eval?
Yes, it is possible:
class Foo
end
Foo.class_eval do
def self.bar
puts "I'm a class method defined using class_eval and self"
end
def baz
puts "I'm an instance method defined using class_eval without self"
end
end
Foo.bar # => "I'm a class method defined using class_eval and self"
foo = Foo.new
foo.baz # => "I'm an instance method defined using class_eval without self"
As far as I can tell, this is because within class_eval, self is the Foo class, and doing def Foo.bar would create a class method.
Foo.class_eval do
...
end
is identical to:
class Foo
...
end
We need Module#class_eval to operate on a variable that holds the name of a class. For example, if:
klass = Foo
you can write:
klass.class_eval do
...
end
whereas the keyword class demands a constant.
Here class_eval does two things:
it changes self to the value of klass (Foo); and then it
"opens" the value of klass (Foo) in the same way the keyword class does.
I am trying to call a proc on a class but access instance methods from it with an inherited class. I think some mock code will make more sense :)
class Bar
def self.foo &block
#foo ||= block
end
def foo; self.class.foo.call(); end
def bar; 'bar'; end
end
class Foo < Bar
foo do
bar
end
end
Foo.new.foo
# NameError: undefined local variable or method `bar' for Foo:Class
I want to be able to access the bar instance method on the Bar class. The reason for calling the foo class method with a block from the inherited class is part of the DSL requirement, but any suggestions for a better design would be appreciated.
Blocks are lexically scoped, including the value of self. At the point where the block is defined, self is Bar and Bar doesn't respond to bar. You need to evaluate the block in the context of the object (in this case an instance of Bar and not Bar itself) whose methods you want to call. That's what instance_eval does:
class Bar
def self.foo(&block) #foo ||= block end
def foo; instance_eval(&self.class.foo) end
def bar; 'bar' end
end
class Foo < Bar; foo do bar end end
Foo.new.foo
# => 'bar'
Note that all the usual disclaimers about instance_eval apply: since you change the value of self methods and instance variables that the block author may expect to be available won't be.
You can dynamically define a class method for a class like so:
class Foo
end
bar = %q{def bar() "bar!" end}
Foo.instance_eval(bar)
But how do you do the opposite: remove/undefine a class method? I suspect Module's remove_method and undef_method methods might be able to be used for this purpose, but all of the examples I've seen after Googling for hours have been for removing/undefining instance methods, not class methods. Or perhaps there's a syntax you can pass to instance_eval to do this as well.
Thanks in advance.
class Foo
def self.bar
puts "bar"
end
end
Foo.bar # => bar
class <<Foo
undef_method :bar
end
# or
class Foo
singleton_class.undef_method :bar
end
Foo.bar # => undefined method `bar' for Foo:Class (NoMethodError)
When you define a class method like Foo.bar, Ruby puts it Foo's singleton class. Ruby can't put it in Foo, because then it would be an instance method. Ruby creates Foo's singleton class, sets the superclass of the singleton class to Foo's superclass, and then sets Foo's superclass to the singleton class:
Foo -------------> Foo(singleton class) -------------> Object
super def bar super
There are a few ways to access the singleton class:
class <<Foo,
Foo.singleton_class,
class Foo; class << self which is commonly use to define class methods.
Note that we used undef_method, we could have used remove_method. The former prevents any call to the method, and the latter only removes the current method, having a fallback to the super method if existing. See Module#undef_method for more information.
This also works for me (not sure if there are differences between undef and remove_method):
class Foo
end
Foo.instance_eval do
def color
"green"
end
end
Foo.color # => "green"
Foo.instance_eval { undef :color }
Foo.color # => NoMethodError: undefined method `color' for Foo:Class
You can remove a method in two easy ways. The drastic
Module#undef_method( )
removes all methods, including the inherited ones. The kinder
Module#remove_method( )
removes the method from the receiver, but it
leaves inherited methods alone.
See below 2 simple example -
Example 1 using undef_method
class A
def x
puts "x from A class"
end
end
class B < A
def x
puts "x from B Class"
end
undef_method :x
end
obj = B.new
obj.x
result -
main.rb:15:in
': undefined methodx' for # (NoMethodError)
Example 2 using remove_method
class A
def x
puts "x from A class"
end
end
class B < A
def x
puts "x from B Class"
end
remove_method :x
end
obj = B.new
obj.x
Result -
$ruby main.rb
x from A class
I guess I can't comment on Adrian's answer because I don't have enough cred, but his answer helped me.
What I found: undef seems to completely remove the method from existence, while remove_method removes it from that class, but it will still be defined on superclasses or other modules that have been extened on this class, etc.
If you would like to remove method with name what calculate dinamically, you should use eigenclasses like:
class Foo
def self.bar
puts "bar"
end
end
name_of_method_to_remove = :bar
eigenclass = class << Foo; self; end
eigenclass.class_eval do
remove_method name_of_method_to_remove
end
this way is better than others answers, becouse here i used class_eval with block. As you now block see current namespace, so you could use your variables to remove methods dinamically
Object.send(:remove_const, :Foo)