Module inclusion in ruby eigen class - ruby

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"

Related

How to output the Ruby class name wrapped around Singleton method

My goal is:
class Bermuda
class << self
def grass
puts self.superclass.name
end
end
end
# my goal is that this expression
Bermuda.grass
# will output a string of the class name it resides in
=> "Bermuda"
I cannot display the name of the class that holds a singleton method. I have tried a number of different stabs and standard library searches but haven't found an answer.
This is partially pointless because you would not need a class method to display the information that you would need in the first place to call that method. I'm curious if it's possible or I'm completely missing the scope.
Just call name on self
class Bermuda
class << self
def grass
puts self.name
end
end
end
This sort of an implementation isn't recommended since all you have to do to get the class name is call Bermuda.name
Please see the answer given below by #MatthewCliatt for more info.
It's as simple as:
self.class.name
But, the catch is that this isn't for class methods, it's for instance methods.
That means you don't declare it with self. This was your code:
class Bermuda
class << self
def grass
puts self.superclass.name
end
end
end
And that will make the grass method a class method. You would have to call it like Bermuda.grass.
But, if you can call class methods like the one above, you could just as easily write: Bermuda.name.
I'm assuming you can't call class methods, probably because you're working with an instance. So you want to write this method as such:
class Bermuda
def grass
puts self.class.name
end
end
You say you're creating a singleton method, but I don't think your method is a singleton method in the usual sense (i.e. a method on an object that is an instance of a class, but not itself a class).
I believe the class << self notation you use merely results in a class method being defined, identical to:
def self.grass
puts name
end
In irb:
2.3.0 :003 > class Bermuda; def self.grass; puts name; end; end
=> :grass
2.3.0 :004 > Bermuda.grass
Bermuda

Call a method in a class in another class in Ruby

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.

Ruby: What is the difference between writing extend outside the class and inside the class

I have read this question at url In Ruby or Rails, why is "include" sometimes inside the class and sometimes outside the class?. As per the answer of this question i am expecting the method print2 of module Calculation should be accessible to the class Addition and ABc .but i am getting error as undefined methodprint2' for Addition:Class (NoMethodError)`
module Calculation
def print2
puts "print2"
end
end
require '/home/sanjay/Desktop/m'
extend Calculation;
class Addition
end
class Abc
end
Addition.print2
Abc.print2
Please explain why this error is comming??
Thanks
The receiver of extend should be each class, so you want Addition.extend Calculation and Abc.extend Calculation (after the class definitions). The semicolon is not required.
module D
def dog
puts "woof"
end
end
class A; end
A.extend D
A.dog #=> "woof"
class B; end
B.extend D
B.dog #=> "woof"
My hunch is that your extend (outside any class) extends the Eigenclass of main. Main being the instance of Object that is your current runtime environment. An Eigenclass is the class of an object whose only instance is the given object. Each object in Ruby has a Eigenclass. Nothing inherits from an Eigenclass. So your extend is not inherited to any other classes.

Why can't I call include from a class method in ruby?

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) }

Ruby metaprogramming question

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.

Resources