Module methods in Object Ruby - ruby

I have troubles with understanding the global area of visibility in Ruby, so, I know that you cant use Module methods in your own class for example:
module Mod
def self.meth
“module method”
end
end
class Klass
include Mod
end
p Klass.meth
# Error
but when i knew that you can do such thing:
include Math
p sin 2
#0.909....
I was confused, because i thought you cant use module methods in any class without calling the method name. Also i had a supposition, that module Math has instance methods, like Kernel, but, unfortunately, no. Now i am doubting, that I understood such methods like extend and include correctly, so, could you please explain to me this thing and what will happen if we will change include to extend

You have encounter a weirdness of module_function: https://apidock.com/ruby/Module/module_function/
module Foo
def foo # this is (irb) 2
end
end
Foo.singleton_methods #=> []
Foo.instance_methods #=> [:foo]
Foo.instance_method(:foo).source_location #=> ["(irb)", 2]
module Foo
module_function :foo # this is (irb) 9
end
Foo.singleton_methods #=> [:foo]
Foo.singleton_method(:foo).source_location #=> ["(irb)", 2]
Foo.instance_methods #=> []
Foo.private_instance_methods #=> [:foo]
Foo.instance_method(:foo).source_location #=> ["(irb)", 2]
So, module_function takes an instance method of the module, makes it private and copies it onto a singleton class.
module_function can also be used without the method name, which works a bit like private method, modifying all the future methods added to this module.
Math is a full module_function module, meaning that the methods are defined both as singletons and private instance methods, which is why you can use it both ways.

Related

Customizing a Ruby Struct with pre-defined definitions and a custom block

Given the following program, in which I want to:
Create a Struct with some keys
Provide some default customization
Allow a block to be passed for further customization
module Magic
def self.MagicStruct(keys_array, &block)
Struct.new(*keys_array) do
#keys = keys_array
def self.magic_class_method
puts "constructed with #{#keys}"
end
def magic_instance_method
puts "instance method"
end
# --- DOESN'T WORK, CONTEXT IS OUTSIDE OF MODULE --- #
# yield if block_given?
instance_eval(&block) if block_given?
end
end
end
Foo = Magic.MagicStruct([:a, :b, :c]) do
puts "class customizations executing..."
def self.custom_class_method
puts "custom class method"
end
def custom_instance_method
puts "custom instance method"
end
end
Foo.magic_class_method # works
Foo.custom_class_method # works
x = Foo.new({a: 10, b: 20, c: 30})
x.magic_instance_method # works
x.custom_instance_method # fails
Output:
class customizations executing...
constructed with [:a, :b, :c]
custom class method
instance method
Traceback (most recent call last):
`<main>': undefined method `custom_instance_method' for #<struct Foo a={:a=>10, :b=>20, :c=>30}, b=nil, c=nil> (NoMethodError)
Why is the self.custom_class_method correctly added to the Foo class, but the custom_instance_method is not? This usage is clearly stated in the Struct documentation, so I'm afraid there's some kind of scoping or context issue I'm missing here.
I would prefer to keep the nice def method() ... end syntax rather than resorting to having a strict requirement to use define_method("method") in the customization block, which does happen to work.
In Ruby there is the notion of a current class which is the target of keywords such as def.
When you use instance_eval, the current class is set to self.singleton_class. In other words, def x and def self.x are equivalent. In your code, custom_instance_method is defined on the singleton class of the newly created Struct, making it a class method.
When you use class_eval, the current class is set to self. Since this method is only available on classes, it will set the current class to the one you called the method on. In other words, def x will define a method that's available to all objects of that class. This is what you wanted.
For more details, see my other answer.
This is a gem I wrote that I believe does most, if not all that you want (not so sure about adding class methods though, I'd have to play with that). While the Struct created is immutable, you can have a look at the code and alter it to meet your needs; it's pretty simple. What you'd be interested in, would be here, which basically amounts to what you see below. The extension of the modules uses instance_eval in a way that I believe is what you want as well:
# https://github.com/gangelo/immutable_struct_ex/blob/main/lib/immutable_struct_ex.rb
# Defines the methods used to create/manage the ImmutableStructEx struct.
module ImmutableStructEx
class << self
# Most likely changing this method name to #create in subsequent version, but alas...
def new(**hash, &block)
Struct.new(*hash.keys, keyword_init: true, &block).tap do |struct|
return struct.new(**hash).tap do |struct_object|
struct_object.extend Comparable
struct_object.extend Immutable
end
end
end
end
end
Usage details can be found in the README.md in the github repository.
I hope this help, at least in part.

Is the functionality extend different from doing tap and then include

If I initialize an object like this:
a = A.new
a.extend(B)
Is that different from doing the following?
a = A.new.tap do |a|
include B
end
No, both are not equivalent.
a.extend(B) adds the methods of module B only to instance a, whereas
a = A.new.tap do |a|
include B
end
is equivalent to
include B
Inside the block of tap, the current self is main and whenever you include a module in main, the methods from included module becomes instance methods of Object class, as shown below:
class A
def class_meth
"class meth"
end
end
module B
def module_meth
"module meth"
end
end
p Object.instance_methods
#=> [:nil?, :===, :=~, :!~,...
a = A.new.tap do |a|
include B
end
p Object.instance_methods
#=> [:module_meth, :nil?, :===, :=~,...
Hence, you feel like the effect is similar to a.extend(B), but it is much more. Any new objects of any class will have methods of module B in them via the Object class.
class C
end
p C.instance_methods
#=> [:module_meth, :nil?, :===, :=~, :!~,...
p Hash.instance_methods.grep(/module_meth/)
#=> [:module_meth]
p [].module_meth
#=> "module meth"
include and extend are not the same thing. include can only add instance level methods, while extends "includes" far more:
But there are occasions when you would want to include some methods from a module as class-level methods in a class. This is where the method extend becomes useful.
This is just a high level difference, there's much more differences such as include being limited in the scenarios where it can work - you can read more here.
they are not same
extend is method if Object class
include is method if Module class
so, if you will do
object.extend(Module)
you will add methods from Module to object, but
SomeClass.include(Module)
will add methods from Module to instance of SomeClass

Using extend self in module

Before voting for closing due to question duplication I want to say that my question is really simple one (not asked in above mentioned questions).
There are two modules, one defines module method using extend self, another defines mixin method.
module A
extend self
def module_a_meth
"Called module_a_meth"
end
end
module B
def module_b_meth
"Called module_b_meth"
end
end
There is a class, where I both include and extend these modules:
class Test
include A
extend A
include B
extend B
end
When we includeing module, its methods become class' instance methods, when extending - class methods.
Question:
it doesn't matter for class, if methods in module defined as module methods or mixin methods, right? I mean, when included - EVERY method (either module methods or mixin methods) become instance methods, and when extended - either become class methods.
If I'm wrong - where is the difference?
obj = Test.new
puts obj.module_a_meth
puts obj.module_b_meth
puts Test.module_a_meth
puts Test.module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth
EDIT
Please start your answer with Yes or No, since my question implies this type of answer :).
Regardless of whether you are using extend or include you are always copying over instance methods. The difference is where those instance methods live.
When you call Class#include you are "copying" all of the instance methods in the module to be instance methods in the class. It's similar to how inheritance work, and if you call Class#ancestors you'll see the module there.
When you call Object#extend you are copying all of the instance methods of the module to the object's singleton class. This is a class reserved just for this object instance that is created at runtime. This is how you get "class methods" (e.g. MyClass.hello_world); by adding them to the class's singleton. You can also do things like extend a particular object instance (e.g. s = String.new; s.extend(SomeModule); s.hello_world)
There are some other differences too. The context binding is different depending on whether you use extend or include. extend doesn't cause the module to show up in the ancestor chain while include does.
When trying to add both "class" and instance methods, one common pattern you'll see is doing things like this which uses the included callback to extend the base class with a ClassMethods module:
module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def hello_world
end
end
end
ActiveSupport::Concerns also abstracts this pattern allowing you to add both instance and "class" methods in one call.
I personally prefer having modules only work with instance methods and using singleton methods (e.g. def self.my_method) to have scoped methods (sort of like how you would use private methods). This allows consumers to use either extend or include however they want and have it work as expected.
I'm not sure if that answers your question or not, but there's some info for you
Let's look at this in steps.
module A
puts "self = #{self}"
extend self
def module_a_meth
"Called module_a_meth"
end
end
class Test
end
Test.include A
#-> self = Test
Test.instance_methods.include?(:module_a_meth)
#=> true
Test.methods.include?(:module_a_meth)
#=> false - no class method
So include includes :module_a_meth as an instance method. As self is Test, the line:
extend self
is equivalent to:
extend Test
which of course makes no reference to the module. Now we extend and obtain the expected result:
Test.extend A
#=> true
Test.methods.include?(:module_a_meth)
#=> true
including and extending B is normal:
module B
def module_b_meth
"Called module_b_meth"
end
end
Test.include B
Test.instance_methods.include?(:module_b_meth)
#=> true
Test.extend B
Test.methods.include?(:module_b_meth)
#=> true
First of all, regarding the actual question: No :).
Class (or any other object) cares how methods are defined in a module you're including. Basically, method's in a module you've described are defined as mixin methods. extend self doesn't redefine methods to be a module methods, but, basically, duplicates them to both contexts.
It's pretty much a question about how does extend work, it's just a tricky case.
First of all, think of extend as an include in object's singleton class context. Those two definitions are equal:
module SomeModule
def hi
'hi'
end
end
class SomeClass
extend SomeModule
end
class SomeClass
class << self
include SomeModule
end
end
Given that, by using extend self in a module you're saying: Take all of the mixin methods I've defined and extend module's singleton class with them. This magic is a result of ruby's nature: an ability to re-open any definition. Here's how a verbose version of extend self would look like:
module Module1
def hi
'hi'
end
end
module Module1
extend Module1 # which is self
#### now "hi" is both here:
# def hi; end
#### and here:
# class << self; def hi; end
end
Module1.hi # => 'hi'
class SomeClass; include Module1; end;
SomeClass.new.hi # => 'hi'
__ EDIT __
Just a quick proof that object cares about how methods in a module are defined:
module SomeModule
def self.hi
'hi'
end
end
object = 'some string'
class << object
include SomeModule
end
object.hi # => NoMethodError: undefined method

Ruby: Adding things to initialize method through modules

In Ruby I want to have a class include a series of modules and have these individual modules execute a block or method (or just find some way to edit an instance variable) when initializing that class.
I know I can do this by creating a method in the module and then calling it in the class' initialize method, but I want some way to do this by simply including the module and calling one method to execute any code the modules add to initialize, that way I can have a large amount of things included in a class without worrying about adding a line of code in the initialize method for every single module included.
I've checked out aliasing, super, and related things but haven't gotten anything...
If it helps to understand what I'm hoping to accomplish here's some pseudocode:
module Mod1
call_this_block_on_initialize { #a.push 4 }
end
module Mod2
call_this_block_on_initialize { #a.push 5 }
end
class Test
attr_accessor :a
include Mod1
include Mod2
def initialize
#a = [1, 2, 3]
call_those_blocks_set_by_mods
end
end
t = Test.new
t.a # returns [1, 2, 3, 4, 5]
This may be a bit wordy but I think the title sums up what I'm trying to do. Thanks for any help!
There are a few ways you can do this. This example will redefine the initialize method and add whatever extra code you want:
module MyModule
def self.included(base) # built-in Ruby hook for modules
base.class_eval do
original_method = instance_method(:initialize)
define_method(:initialize) do |*args, &block|
original_method.bind(self).call(*args, &block)
#a.push 4 # (your module code here)
end
end
end
end
class Test
attr_accessor :a
def initialize
#a = [1, 2, 3]
end
# It must be included after the `initialize`
# definition or the module won't find the method:
include MyModule
end
However:
I think what you really want is subclassing. If you have a lot of classes with similar behavior, as it seems you do, ask yourself if there is a natural abstract parent class. Can you explain what you did with super and why it didn't work?

What is the difference between class_eval, class_exec, module_eval and module_exec?

I am reading the Module documentation but can't seem to understand their differences and which should be used where.
How is the eval different than exec?
I'm going to answer a bit more than your question by including instance_{eval|exec} in your question.
All variations of {instance|module|class}_{eval|exec} change the current context, i.e. the value for self:
class Array
p self # prints "Array"
43.instance_eval{ p self } # prints "43"
end
Now for the differences. The eval versions accepts a string or a block, while the exec versions only accept a block but allow you to pass parameters to it:
def example(&block)
42.instance_exec("Hello", &block)
end
example{|mess| p mess, self } # Prints "Hello" then "42"
The eval version does not allow to pass parameters. It provides self as the first parameter, although I can't think of a use for this.
Finally, module_{eval|exec} is the same as the corresponding class_{eval|exec}, but they are slightly different from instance_{eval|exec} as they change what is the current opened class (i.e. what will be affected by def) in different ways:
String.instance_eval{ def foo; end }
Integer.class_eval { def bar; end }
String.method_defined?(:foo) # => false
String.singleton_methods.include?(:foo) # => true
Integer.method_defined?(:bar) # => true
So obj.instance_{eval|exec} opens the singleton class of obj, while mod.{class|module}_{eval|exec} opens mod itself.
Of course, instance_{eval|exec} are available on any Ruby object (including modules), while {class|module}_* are only available on Module (and thus Classes)
To answer your last question first, eval (in all its variations) is completely different from exec. exec $command will start a new process to run the command you specify and then exit when that finishes.
class_eval and module_eval have the power to redefine classes and modules -- even those that you yourself did not write. For example, you might use class eval to add a new method that did not exist.
Fixnum.class_eval { def number; self; end }
7.number # returns '7'
class_eval can be used to add instance methods, and instance_eval can be used to add class methods (yes, that part is very confusing). A class method would be something like Thing.foo -- you're literally calling the foo method on the Thing class. An instance method is like the example above, using class_eval I've added a number method to every instance of Fixnum.
Okay, so that's the *_eval class of methods. The exec methods are similar, but they allow you to look inside a class and execute a block of code as though it was defined as a method on that class. Perhaps you have a class that looks like this:
class Foo
##secret = 'secret key'
##protected = 'some secret value'
def protected(key)
if key == ##secret
return ##protected
end
end
end
The class Foo is just a wrapper around some secret value, if you know the correct key. However, you could trick the class into giving you its secrets by executing a block inside the context of the class like so:
Foo.class_exec { ##secret = 'i'm a hacker' }
Foo.protected('i'm a hacker') #returns the value of ##protected because we overwrote ##secret
In general, with a lot of the tools in ruby, you could use any of these to solve a lot of problems. A lot of the time you probably won't even need to unless you want to monkey patch a class some library you use has defined (although that opens up a whole can of worms). Try playing around with them in irb and see which you find easier. I personally don't use the *_exec methods as much as the *_eval methods, but that's a personal preference of mine.
To avoid ambiguity I'm going to call a method that belongs to (owned by) a singleton class a singleton method. The rest are instance methods. Although one might say that a singleton method of an object is an instance method of its singleton class.
tl;dr Use class_eval/module_eval on a class/module to define instance methods, and instance_eval on a class/module to define class methods (or to be more precise, use instance_eval to define singleton methods). Additionally you can use instance_eval to access instance variables.
A terminology is a bit lacking in this case. ruby maintains a stack of class references (cref for short). When you open/reopen a class, the corresponding class reference is pushed to the stack. And the current class refernece affects where def defines methods (to which class/module they're added).
Now, class_eval/module_eval and class_exec/module_exec are aliases.
The *_exec() variants don't accept strings, and allow to pass arguments to the block. Since the *_eval() variants are mainly used I'll focus on them.
class_eval/module_eval changes cref and self to the receiver (Thing in Thing.module_eval(...)):
rb_mod_module_eval() -> specific_eval()
yield_under() (for blocks)
vm_cref_push()
eval_under() (for strings)
vm_cref_push()
instance_eval changes cref to the singleton class of the receiver, and self to the receiver.
Let's see them in action:
class A
p self #=> A
#a = 1
def initialize
#b = 2
end
end
p A.instance_variables #=> [:#a]
p A.new.instance_variables #=> [:#b]
#a on a class level adds an instance variable to the class A as an object. I'm adding it here for completeness. But that's not how you add a class variable.
A.instance_eval do
p self #=> A
p #a #=> 1
def m() puts 'm' end
end
sclass = A.singleton_class
p sclass.instance_methods(false).include? :m #=> true
A.m #=> m
a = A.new
a.instance_eval do
p self #=> #<A:0x00007fc497661be8 #b=2>
p #b #=> 2
def m2() puts 'm2' end
end
sclass = a.singleton_class
p sclass.instance_methods(false).include? :m2 #=> true
a.m2 #=> m2
So, inside instance_eval def adds a singleton method to the receiver (an instance method to the singleton class of the receiver). For a class/module that means a class/module method. For other objects, a method that is available for that particular object.
A.class_eval do
p self #=> A
p #a #=> 1
def m() puts 'm' end
end
p A.instance_methods(false).include? :m #=> true
A.new.m #=> m
And, inside class_eval def adds an instance method to the receiver itself (the class/module). class_eval is only available for classes/modules.
Also, when class_eval is passed a block, constant/class variable lookup is not affected:
module A
C = 1
##c = 1
class B
C = 2
##c = 2
end
A::B.class_eval { p [C, ##c] } #=> [1, 1]
A::B.class_eval 'p [C, ##c]' #=> [2, 2]
end
The naming is confusing. I might guess that instance in instance_eval suggests that receiver is treated as an instance (allows to change things for a particular instance), and class in class_eval as a class (allows to change things for a class of objects).

Resources