(Ruby,Rails) Context of SELF in modules and libraries...? - ruby-on-rails

Quick question regarding the use of "SELF" inside a module or library. Basically what is the scope/context of "SELF" as it pertains to a module or library and how is it to be properly used? For an example of what I'm talking about, check out the "AuthenticatedSystem" module installed with "restful_authentication".
NOTE: I'm aware that 'self' equates to 'this' in other languages and how 'self' operates on a class/object, however in the context of a module/library there is nothing to 'self'. So then what is the context of self inside something like a module where there is no class?

In a module:
When you see self in an instance method, it refers to the instance of the class in which the module is included.
When you see self outside of an instance method, it refers to the module.
module Foo
def a
puts "a: I am a #{self.class.name}"
end
def Foo.b
puts "b: I am a #{self.class.name}"
end
def self.c
puts "c: I am a #{self.class.name}"
end
end
class Bar
include Foo
def try_it
a
Foo.b # Bar.b undefined
Foo.c # Bar.c undefined
end
end
Bar.new.try_it
#>> a: I am a Bar
#>> b: I am a Module
#>> c: I am a Module

For a short summary...
http://paulbarry.com/articles/2008/04/17/the-rules-of-ruby-self
self is also used to add class methods (or static methods for C#/Java people). The following snippet is adding a method called do_something to the current class object (static)...
class MyClass
def self.do_something # class method
# something
end
def do_something_else # instance method
end
end

Related

ruby monkey patching on the fly

Is there a way to implement monkey patching while an object is being instantiated?
When I call:
a = Foo.new
Prior to the instance being instantiated, I would like to extend the Foo class based on information which I will read from a data store. As such, each time I call Foo.new, the extension(s) that will be added to that instance of the class would change dynamically.
tl;dr: Adding methods to an instance is possible.
Answer: Adding methods to an instance is not possible. Instances in Ruby don't have methods. But each instance can have a singleton class, where one can add methods, which will then be only available on the single instance that this singleton class is made for.
class Foo
end
foo = Foo.new
def foo.bark
puts "Woof"
end
foo.bark
class << foo
def chew
puts "Crunch"
end
end
foo.chew
foo.define_singleton_method(:mark) do
puts "Widdle"
end
foo.mark
are just some of the ways to define a singleton method for an object.
module Happy
def cheer
puts "Wag"
end
end
foo.extend(Happy)
foo.cheer
This takes another approach, it will insert the module between the singleton class and the real class in the inheritance chain. This way, too, the module is available to the instance, but not on the whole class.
Sure you can!
method_name_only_known_at_runtime = 'hello'
string_only_known_at_runtime = 'Hello World!'
test = Object.new
test.define_singleton_method(method_name_only_known_at_runtime) do
puts(string_only_known_at_runtime)
end
test.hello
#> Hello World!
Prior to the instance being instantiated, I would like to extend
Given a class Foo which does something within its initialize method:
class Foo
attr_accessor :name
def initialize(name)
self.name = name
end
end
And a module FooExtension which wants to alter that behavior:
module FooExtension
def name=(value)
#name = value.reverse.upcase
end
end
You could patch it via prepend:
module FooPatcher
def initialize(*)
extend(FooExtension) if $do_extend # replace with actual logic
super
end
end
Foo.prepend(FooPatcher)
Or you could extend even before calling initialize by providing your own new class method:
class Foo
def self.new(*args)
obj = allocate
obj.extend(FooExtension) if $do_extend # replace with actual logic
obj.send(:initialize, *args)
obj
end
end
Both variants produce the same result:
$do_extend = false
Foo.new('hello')
#=> #<Foo:0x00007ff66582b640 #name="hello">
$do_extend = true
Foo.new('hello')
#=> #<Foo:0x00007ff66582b280 #name="OLLEH">

Ruby include/extend Module: a class method - Beginner

I've been reading this article on the difference between include & extend in ruby.
If I have this module, I understand how the first and second methods of the module will be used in the class. What I don't understand is how the class << self will be used by include or extend.
module Direction
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
class << self
def stop
puts "stopping!"
end
end
end
# This will work because `include` brings them in as instance methods
class Car
include Direction
end
Car.new.straight
Car.new.turn
# ---------------------
# Now this will also work because `extend` brings them in as class methods
class Car
extend Direction
end
Car.straight
Car.turn
# ---------------------
Now, the issue is, doing Car.stop or Car.new.stop will always result in an error:
/Users/<name>/Projects/ruby-testing/main.rb:34:in `<main>': undefined method `stop' for Car:Class (NoMethodError)
Why are class methods not carried over via include and extend?
I started thinking about this because of my research into the [forwardable source code at line 119].(https://github.com/ruby/ruby/blob/master/lib/forwardable.rb#L119)
Thank you for any help you may have!
Update from Answer Below
The following was an example given:
module Direction
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
end
class Car
include Direction
end
This I understand now, and I understand how I can implement class methods from a module into a class using def self.included(base). My question is, if we used extend inside of Car instead of include, would we still be able to get at those class methods using def self.included(base)?
When you define a method with class << self you are defining a class method. It's the same as defining the methed like this:
class Foo
def self.foo
puts 'foo'
end
# the above definition is the same as doing:
class << self
def foo
puts 'foo'
end
end
end
The above shows 2 ways of defining class methods which are called directly on the class and not on instances of the class. You might use the 2nd syntax if you want to define only class methods or several of them inside of the class << self block. But either style has the same result.
Since you've defined a class method on the Direction module, include or extend will not inherit the class method of that module. This is the expected behavior.
If you want to use inheritance with class methods from a module, you should do it like this which is explained further down in the article you've linked
module Direction
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
end
class Car
include Direction
end
Now calling class methods on Car will inherit as defined in the Direction class.
Car.stop
stopping!
=>nil # calling a method will return nil unless the method returns a value.
However always be careful using inheritance of any kind as Ruby is a dynamic language. So if you do the above code and then later redefine this method:
module Direction
module ClassMethods
def stop
puts 'go!'
end
end
end
Guess what will happen if you do this:
Car.stop
Since the method was defined inside Direction module, when the method gets called on Car it will be calling the method from the Direction module.
Car.stop
go!
=>nil
Updated based on comments:
If you prefer to use extend vs include you would need to do this instead:
module Direction
def self.extended(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
end
class Car
extend Direction
end
In this example, all the methods which were inherited from the module are "copied" to the class extending them. This avoids the problem of possible result of redefining the module method which I warned about when using include previously in my answer.
But you may want to look at answers to this question for ideas about when and why to use either case.

Rails concern method override another concern method doesn't work like normal modules

Let's say I have the following structure in ruby (no rails)
module Parent
def f
puts "in parent"
end
end
module Child
def f
super
puts "in child"
end
end
class A
include Parent
include Child
end
A.new.f # prints =>
#in parent
#in child
Now when using rails concerns
module Parent
extend ActiveSupport::Concern
included do
def f
puts "In Parent"
end
end
end
module Child
extend ActiveSupport::Concern
included do
def f
super
puts "In Child"
end
end
end
class A < ActiveRecord::Base
include Parent
include Child
end
A.new.f #exception
NoMethodError: super: no superclass method `f' for #<A:0x00000002244490>
So what am I missing here? I need to use super in concerns like in normal modules. I searched but I could not find help on this topic
The reason for this is that included method block is actually evaluated in the context of the class. That mean, that method defined in it is defined on a class when module is included, and as such takes precedence over included modules.
module Child1
extend ActiveSupport::Concern
included do
def foo
end
end
end
module Child2
def bar
end
end
class A
include Child1
include Child2
end
A.new.method(:foo).owner #=> A
A.new.method(:bar).owner #=> Child2
Method lookup
In ruby, every time you want to call a method, ruby has to find it first (not knowing whether it is method or a variable). It is done with so called method lookup. When no receiver is specified (pure call like puts) it firstly searches the current scope for any variables. When not found it searches for that method on current self. When receiver is specified (foo.bar) it naturally search for the method on given receiver.
Now the lookup - in ruby all the methods always belongs to some module/class. The first in the order is receiver's eigenclass, if it exists. If not, regular receiver's class is first.
If the method is not found on the class, it then searches all the included modules in given class in the reversed order. If nothing is found there, superclass of given class is next. The whole process goes recursively until something is found. When lookup reaches BasicObject and fails to find the method it quit and triggers search for method_missing, with default implementation defined on BasicObject.
Important thing to notice is that methods which belongs to the class always take precedence over module methods:
module M
def foo
:m_foo
end
end
class MyClass
def foo
:class_foo
end
include M
end
MyClass.new.foo #=> :class_foo
About super
Search for a super method is very similar - it is simply trying to find a method with the same name which is further in the method lookup:
module M1
def foo
"M1-" + super
end
end
module M2
def foo
'M2-' + super
end
end
module M3
def foo
'M3-' + super
end
end
class Object
def foo
'Object'
end
end
class A
include M2
include M3
end
class B < A
def foo
'B-' + super
end
include M1
end
B.new.foo #=> 'B-M1-M3-M2-Object'
ActiveSupport::Concern#included
included is a very simple method that takes a block and creates a self.included method on the current module. The block is executed using instance_eval, which means that any code in there is actually executed in the context of the class given module is being included in. Hence, when you define a method in it, this method will be owned by the class including the module, not by the module itself.
Every module can hold only one method with given name, once you tries to define second one with the same name, the previous definition is completely erased and there is no way it can be find using ruby method lookup. Since in your example you included two modules with same method definition in included block, the second definition completely overrides the first one and there is no other definition higher in method lookup, so super is bound to fail.

Call module subclass without specify the name

I would like to access my subclass using only the name of my module.
module MyModule
class UselessName
include OtherModel
# only self method
def self.x
end
end
# No other class
end
And I would like to write MyModule.x and not MyModule::UselessName.x
I could transform my module in class, but I use RoR Helpers, and I would prefer that MyModule remains a module and not a class.
Is there a way to do this ?
Thanks ;)
OK, let's split problem into two - getting list of such methods and making proxies in the module.
Getting list might be a little tricky:
MyModule::UselessName.public_methods(false) - MyModule::UselessName.superclass.public_methods(false)
Here we start with list of all public class methods and subtract list of all superclass's public class methods from it.
Now, assuming we know method's name, we need to make proxy method.
metaclass = class << MyModule; self; end
metaclass.send(:define_method, :x) do |*args, &block|
MyModule::UselessName.send(:x, *args, &block)
end
This code will just make equivalent of following definition at runtime.
module MyModule
def x(*args, &block)
MyModule::UselessName.send(:x, *args, &block)
end
end
So let's put it together in simple function.
def make_proxies(mod, cls)
methods = cls.public_methods(false) - cls.superclass.public_methods(false)
metaclass = class << mod; self; end
methods.each do |method|
metaclass.send(:define_method, method) do |*args, &block|
cls.send(method, *args, &block)
end
end
end
So now you'll just need to call it for needed modules and classes. Note that "destination" module can be different from "source" module owning the class, so you can slurp all methods (given they have different names or you'll prefix them using class name) to one module. E.g. for your case just make following call.
make_proxies(MyModule, MyModule::UselessName)
Ok, I've found a VERY DIRTY way to accomplish what I THINK you mean:
module MyModule
class UselessName
include OtherModule
# have whatever you want here
end
def self.x
# whatever
end
end
So somewhere in your code you can do, and I repeat THIS IS VERY, VERY DIRTY!
MyModule.methods(false).each do |m|
# m = method
# now you can redefine it in your class
# as an instance method. Not sure if this
# is what you want though
MyModule::UselessName.send(:define_method, :m) do
# in this NEW (it's not the same) method you can
# call the method from the module to get the same
# behaviour
MyModule.send(m)
end
end
I don't know if this overwrites an instance method with the same name if it's in the class before or if it throws an exception, you have to try that.
In my opinion you should overthink your app design, because this is not the way it should be, but here you go...

Modules in rails lib directory

module MyModule
def self.my_method
#instance_variable ||= "some string"
end
end
Consider another module OtherModule that includes MyModule. When code in OtherModule calls my_method multiple times will #instance_variable be instantiated multiple times? Or just once?
updated answer:
if the delegate module call the method, you will get an error.
if the original module call the method, the class instance variable only instanced 1 time.
if you want to define "class methods" in an module, you can't define self.xx then simply extend/include it. you should either "extend" it, or "include it to the eigen class", e.g.
module SomeModule
def self.aim_to_be_class_method ; end;
def aim_to_be_instance_method ; end;
end
# class methods: []
# instance methods: ["aim_to_be_instance_method"]
class SomeClassIncludingTheModule
include SomeModule
end
# class methods: ["aim_to_be_instance_method"]
# instance methods: []
class SomeClassExtendingTheModule
extend SomeModule
end
# class methods: ["aim_to_be_instance_method"]
# instance methods: []
class SomeClassMadeEigenClassIncludingModule
class << self
include SomeModule
end
end
your code is an example of "class instance variables". according to the book << metaprogramming ruby>>, page 127, you can consider "class instance variables" as Java's static fields. so, I think most of the case, it should run once.
for more details, please write an unit test and see what happens? I will update my answer after a short while with my own unit test code written.
UPDATED: my unit test and result:
# all of the code is placed in a file: class_instance_variable_test.rb
# define a class so that we see its initialize process
class Apple
def initialize
puts "Apple initialized~"
end
end
# define an original_module that calls Apple.new
module OriginalModule
def self.original_method
#var ||= Apple.new
end
end
# define another module to call the original_module
module DelegateModule
include OriginalModule
end
# now the test begins
require 'test/unit'
class ClassInstanceTest < Test::Unit::TestCase
# output of this test case:
# NoMethodError: undefined method `original_method' for DelegateModule:Module
def test_if_delegate_module_could_call_original_method_by_including_original_module
DelegateModule.original_method
end
# output of this test case:
# ----- calling from original_method, 3 times called, how many times initialized?
# Apple initialized~
# ------ ends
def test_if_we_could_call_original_method_from_original_module
puts "----- calling from original_method, 3 times called, how many times initialized? "
OriginalModule.original_method
OriginalModule.original_method
OriginalModule.original_method
puts "------ ends "
end
end

Resources