You can call include to mixin a module with a class in ruby, but it must be done at the beginning of the class definition. Why can't it be done inside a class function? Is there an alternate syntax?
EX:
module UsefulThings
def a() puts "a" end
end
class IncludeTester
include UsefulThings
def initialize
end
end
n = IncludeTester.new
n.a()
^^ This works, but if I change IncludeTester to the following, I get the error "undefined method `include'"
class IncludeTester
def initialize
include UsefulThings
end
end
It can be done in a class method.
This works:
module UsefulThings
def a
puts "a"
end
end
class IncludeTester
def self.mix_in_useful_things
include UsefulThings
end
end
x = IncludeTester.new
IncludeTester.mix_in_useful_things
x.a # => a
But "initialize" is not a class method, it's an instance method.
"new" is a class method. You can think of new as allocating a new object and then calling initialize on it, passing initialize whatever arguments were passed to new.
You can't call include directly in initialize because include is a private method of Class (inherited from Module), not of the newly created IncludeTester instance.
If you want to include a module into a class from an instance method, you have to do something like this:
class IncludeTester
def initialize
self.class.send(:include, UsefulThings)
end
end
It's necessary to use "send" here because include is private method, which means it can only be directly invoked with an implicit receiver (of self).
When you call initialize normally in a class definition, you're actually calling it with an implicit receiver of "self", referring to the class being defined.
This is what is actually happening when you do this:
class IncludeTester
include UsefulThings
end
include is a method from Module, Module is the superclass of Class and so include is a method on Class and that makes it a class method in your IncludeTester. When you do this:
class IncludeTester
def initialize
include UsefulThings
end
end
you're trying to call a class method inside an instance method and Ruby says
`initialize': undefined method `include'
because there is no instance method called include. If you want to call a class method inside an instance method (such as initialize), you'd do this:
def initialize
self.class.include UsefulThings
end
But that won't work because include is a private method; you can get around that with class_eval though:
def initialize
self.class.class_eval {
include UsefulThings
}
end
You would be doing include UsefulThings every single time you instantiated an IncludeTester, aside from not making much sense, it could cause problems if UsefulThings had an included method.
It's actually fully possible to include a module from a class method, like so:
module Stuff
def say_hello
puts "hello"
end
end
class Foo
def self.i_am_a_class_method
include Stuff
end
def i_am_an_instance_method
end
end
You cannot however do that from an instance method, because the include method is only available as a private class method, and therefore not accessible from a Foo.new instance.
You want the extend method:
class IncludeTester
def initialize
extend UsefulThings
end
end
This need not be done within the a method either:
IncludeTester.new.tap { |newTester| newTester.extend(UsefulThings) }
Related
Can somebody explain the following behavior?
def a_method
"a_method() was called by #{self}"
end
class MyClass
puts a_method
def another_method
puts a_method
end
end
MyClass.new.another_method
It produces following output:
a_method() was called by MyClass
a_method() was called by #<MyClass:0x000000034962b0>
How does puts a_method within the MyClass body work? How could I access a_method while defining Myclass?
(Outermost) Ruby statements are called and executed in the order they are written.
Within MyClass, there is a method call puts a_method, where self is the class MyClass. This prints the first line of your output. Then, later, there is a method call another_method, which calls puts a_method, where self is an instance of MyClass. This prints the second line of your output.
How could I access a_method while defining Myclass?
This seems to imply that you consider class definition to be completed when the (first occurrence of a) class body is closed. That is however not the case. A class is created right after you open the class body for the first time. Hence, in the class body, you can call methods on the class. Defining methods within a class body is not part of the creation of the class; the class is already there, and it is modifying the class.
The first call is executed in Class scope. Therefore self is the class. The second is executed in instance scope of the class. You could add an class method to have the same effect like first method call.
def a_method
"a_method() was called by #{self}"
end
class MyClass
puts a_method # how this works?
def another_method
puts a_method
end
def self.a_class_method
puts a_method
end
end
MyClass.a_class_method
MyClass.new.another_method
# result
a_method() was called by MyClass
a_method() was called by MyClass
a_method() was called by #<MyClass:0x00000002dc49f0>
A class body executes when it initially loads(global variable, class variables and Constants are defined within it, and finally methods are defined).
So that a_method is called by MyClass when it is defined.
I was wondering how I could call a method in an instance of a class in another class.
This is what I came up with
class ClassA
def method
return "This is a method_from_class_A"
end
end
class ClassB
def initialize
#method_from_class_A=instance.method
end
def method_calls_method
#method_from_class_A
end
end
instance=ClassA.new
instance2=ClassB.new
puts instance2.method_calls_method
But I get this error:
Testing.rb:9:in initialize': undefined local variable or method
instance' for # (NameError) from
Testing.rb:19:in new' from Testing.rb:19:in'
How could I fix it?
Thank you for your response.
From your description this seems to be what you're going for:
class ClassB
def initialize
#instance_of_class_a = ClassA.new
end
def method_calls_method
#instance_of_class_a.method
end
end
Or to pass in the ClassA instance (this is called dependency injection):
class ClassB
def initialize(class_a_instance)
#instance_of_class_a = class_a_instance
end
def method_calls_method
#instance_of_class_a.method
end
end
instance_a = ClassA.new
instance_b = ClassB.new(instance_a)
puts instance_b.method_calls_method
Another Option would be to take a look at class methods: https://rubymonk.com/learning/books/4-ruby-primer-ascent/chapters/45-more-classes/lessons/113-class-variables
So in your code it would look similar to this:
class ClassA
def self.method
return "This is a method_from_class_A"
end
end
class ClassB
def method_calls_method
ClassA.method
end
end
instance=ClassB.new
puts instance.method_calls_method
*Notice the self. in ClassA to signify a class method. This is similar to a static method in other languages.
According to wikipedia: https://en.wikipedia.org/wiki/Method_(computer_programming)#Static_methods
Class(static) methods are meant to be relevant to all the instances of a class rather than to any specific instance.
You see class methods used a lot in the ruby Math class:
http://ruby-doc.org/core-2.2.2/Math.html
For example taking a square root of a number in is done by using the class method Math.sqrt. This is different from an instance method which would look like object.method instead Class.method. There are a lot of resources and tutorials out that explains this concept in more detail and probably clearer.
I am very confused about this. In Programming Ruby book, it says,
"receiver checks for the method definition in its own class"
So class object stores all instance methods. Then why can't I call
instance method from within a class?
For example
Class ExampleClass
def example_method
end
example_method
end
I cannot call example_method inside ExampleClass.
However if I define a method in top level like this:
class ExampleClass
def example_method
end
end
def example_method1
end
example_method1
Then I can call top level method example_method1.
Isn't top level also a class? How come it is different than
a calling instance method from within ExampleClass?
The biggest reason that you cannot call that function in the way that you have written it is that it is, as you say, an instance method.
Try defining it in this way:
class ExampleClass
def self.class_method
puts "I'm a class method"
end
class_method
end
I believe you will find that you have a different result. It's not that it's "Top Level", it's whether or not it's in scope for what you're dealing with. Since you're dealing with a class, a class method would be necessary. If you're dealing with an object (an instantiated class) it's a different "scope".
Those "global" methods are an exception. They are defined as private instance methods of Object. Everything inherits from Object, so these methods are "globally" visible.
p self.class # => Object
p self.private_methods.sort # => [:Array, :Complex, ... :using, :warn] # all (?) from Kernel module
def aaaa
end
p self.private_methods.sort # => [:aaaa, :Array, ... :using, :warn]
The receiver checks for the method definition in its own class. The receiver is ExampleClass. The class of ExampleClass is Class. There is no example_method method in the Class class, ergo, you get a NoMethodError.
I'll try to explain it as follows.
class MyClass
def self.my_method
puts "Me, I'm a class method. Note that self = #{self}"
end
def my_method
puts "Me, I'm an instance method. Note that self = #{self}"
end
# I'm about to invoke :my_method on self. Which one will it be?"
# "That depends on what self is now, of course.
puts "self = #{self}"
# OK. It's MyClass. But wait. I'm just defining the set now.
# Do the methods I defined above even exist yet?
# Does the class exist yet? Let's find out.
print "class methods: "
puts self.methods(false)
print "instance methods: "
puts self.instance_methods(false)
# Cool! Let's try invoking my_method
my_method
# It worked. It was the class method because self = MyClass
# Now let's see if we can create an instance of the class before
# we finish defining the class. Surely we can't.
my_instance = new
puts "my_instance = #{my_instance}"
# We can! Now that's very interesting. Can we invoke the
# instance method on that instance?
my_instance.my_method
# Yes!
end
The following is printed while the class is being defined:
self = MyClass
class methods: my_method
instance methods: my_method
Me, I'm a class method. Note that self = MyClass
my_instance = #<MyClass:0x007fd6119125a0>
Me, I'm an instance method. Note that self = #<MyClass:0x007fd6119125a0>
Now let's confirm the methods can be invoked from outside the class. There should be no surprises here:
MyClass.my_method
#-> Me, I'm a class method. Note that self = MyClass
my_instance = MyClass.new
my_instance.my_method
#-> Me, I'm an instance method. Note that self = #<MyClass:0x007fd61181d668>
In my point of view, For instance_eval, the class scope becomes the singleton class also know as eigenclass of the target object. So Instance methods created on the singleton class for an object become singleton methods for that object. I mean the following two code snippets produces expected results
class TestClass; end
Using eigen class
class TestClass
class << self
def class_method
"class_method of TestClass"
end
end
end
Using instance_eval
TestClass.instance_eval do
def class_method2
"class_method of TestClass"
end
end
So we can call TestClass.class_method and TestClass.class_method2 and get the corresponding results.
Now let's assume we have a module TestModule
module TestModule
def instance_method
" instance_method from TestModule"
end
end
Now if we include this module in the eigen class then we can access the instance_method as a class method of the TestClass
class TestClass
class << self
include TestModule
end
end
So TestClass.instance_method will works as expected.
But if we do the same thing using instance_eval it fails.Please check the following snippet
TestClass.instance_eval do
include TestModule
end
When I tried to call TestClass.instance_method then I am getting the following error.
ArgumentError: wrong number of arguments(0 for 1)
Would anybody explain what is the issue and what is the internal logic behind this. I really appreciate any help you can provide.
It's because if you use include, it doesn't matter what a current class is, but what does self point to (include is a method called on self if you don't specify explicit receiver), and in your example, self points to TestClass, so TestModule#instance_method becomes instance method of TestClass, like in following example:
class TestClass
end
module TestModule
def test_method
'test'
end
end
TestClass.instance_eval { include TestModule }
TestClass.new.test_method
# => "test"
When I call self.class.instance_variable_set("#var", ...) from inside a class method, where is that variable actually stored? Is it on the class itself? On the instance of that class? I can't seem to find it with any of the following:
e = Example.new
e.instance_variables
e.class.class_variables
I even tried using the (class << self; self; end) trick but I can't find anything (http://ruby-metaprogramming.rubylearning.com/html/seeingMetaclassesClearly.html).
Here is the code snippet (which works as I need) but I'm not sure why it works :)
module HandyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def use_template(template_name)
self.class.instance_variable_set("#_template", template_name)
end
def build(attributes = {})
template_name = self.class.instance_variable_get("#_template")
# do stuff with the template
end
end
end
class Example
include HandyModule
use_template :contact_form
end
Essentially I can include this handy module, and then I have a class method called use_template with which I can specify which template to be used by my build method.
When you call use_template inside the class definition, the self is the class Example. When you call self.class, it is Example.class, or Class. You define the instance variable to the class of the classes.
class Class
p #_template
end
# prints :contact_form
You probably should use just self instead of self.class.