#!/bin/ruby --verion => 2.0.0-p353
In Ruby, self is a special variable that always references the current object.
- Inside class or module definition, self refer to the Class or Module object.
- Inside instance method, self refer to future instance object.
- Inside class method, self refer to the class.i
- Inside singleton method, self refer to same instance object.
Objects
do not store methods, only classes can.
Self is each method's default receiver, if the method cannot be found, then Ruby check ancestors chain until Ruby find that method's definition. We can call super to inherit ancestor method.
- Each object in Ruby has its own anonymous(singleton) class, a class that can have methods, but is only attached to the object itself.
#1. Open singelton class
class Single
end
def Single.show
puts self
end
#2. attach to singleton class
class Single
def self.show
puts self
end
end
#3. open singleton class inside class definition
class Single
class << self
def show
puts self
end
end
end
#4. instance_eval add instance method for specific instance.
# instance_eval add class method for default receiver(self) is a class.
class Single
end
Single.instance_eval do
def who
puts self
end
end
#5. Assume Single class has defined
# "<<" means open singelton class for Single class.
class << Single
def who
"Geek"
end
end
#6.
class Single
end
Single::class_eval do
define_method :who
puts self
end
end
Single::module_eval do
define_method :who
puts self
end
end
- Default Module will only appear in Class object:
class P; end
P.new.singleton_class.ancestors
# => [#<Class:#<P:0x000001011a0908>>, P, Object, Kernel, BasicObject]
P.singleton_class.ancestors
# => [#<Class:P>, #<Class:Object>, #<Class:BasicObject>,
Class, Module, Object, Kernel, BasicObject]
-
A customed module is an instance of Module.
-
Class
inherits fromModule
module. AModule
module inherit fromObject
. AnObject
mixin withKernel
module.
Class.superclass # Module
Module.superclass # Object
-
When you define a class
G
which is create an instance of the ClassG
. -
A class object is NOT an instance of it's superclass.
some_class_object.class
denotes "instance of",some_class_ object.superclass
denotes "inherits from". -
Included modules are part of inheritance chain, but
#superclass
ignore modules as super class when called from its subclass.
class G; end
G.superclass # Object
G.class # Class
-
Objects do not store methods, only classes/meta class can.
-
A class isn't really an object. From Ruby's source code:
// https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L767
struct RObject {
struct RBasic basic;
struct st_table *iv_tbl;
};
struct RClass {
struct RBasic basic;
struct st_table *iv_tbl;
struct st_table *m_tbl;
VALUE super;
};
-
include
mixin module's instance methods to that class. Its module locates at before object after object's super class in inheritance chain. -
extend
mixin module's instance methods to singleton class of current object/class. Which become to singleton methods for that class/object. It means the methods can directly be called by that class/object. -
Module can only call
self.methods
. It is the same like Class instance B callB.singleton_method_name
.
class V; end
class Z; end
module F
def fg; end
end
v = V.new
v.extend F
v.singleton_methods
# => [:fg]
v.methods false
# => nil
Z.extend F
Z.singleton_methods
# => [:fg]
-
Both
include
andextend
only add instance methods to the target. Singleton methods in module can be called by lexical lookup. -
Lookup principle:
Go out then up.
Start at its singleton class first, then move up. BecauseBasicObject.singleton_class.superclass
isClass
, so we can then checkClass#instance_methods
.
class Z; end
class B < Z; end
module M; end
module V; end
B.include M # open B's class and add inst mths to it.
B.extend V # open B's singleton class and add inst mths to it.
Kernel is included in Object.
meta_class === singleton_class
Ancsetors will never print out singleton_methods and modules under it.
class << B
# instance method i for singelton class B
# will be convert ro class method i to class B
def i; end
# singleton method i for B.singleton_class
def self.i; end
end
# Instance methods in a singelton class B will be used as class methods
# in class B.
Case 1
├── class BasicObj
├── module Kernel
├── class Object
│ <Object's instance mth>
├── class Module
│ <Module's instance mth>
├── class Class
│ <Class's instance mth>
│
├── module M
│ <inject M, but add instance method to B,
│ B cannot call those instance mths.>
│
│ <ancestors print since here.>
├── class BasicObject OR meta_class BasicObj
│ <Singleton class BasicObj's instance methods>
│<class BasicObject's singleton methods>
├── class Object OR meta_class Object
│ <Singleton class Object's instance methods>
│<class Object's singleton methods>
├── class Z OR meta_class Z
│ <Singleton class Z's instance methods>
│<class Z's singleton methods>
├── module V
├── class B OR meta_class B
│ <Singleton class B's instance methods>
│<class B's singleton methods>
B.mths
B.instance_of? Object # false
B.instance_of? Class # true
B.superclass # Object
B.new.class.ancestors
# "class Object" is from Class. B.class == Object.class == Class
# Class's super class is Module.
# Append singleton_class.ancestors for traget to know the lookup chain.
# B.singleton_class.ancestors
# [#<Class:B>, V, #<Class:Z>, #<Class:Object>,
# #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
# Lookup Flow:
# Start at B's singleton class.
# Then look up instance methods in singelton class B,
# and look up singelton methods in class B.
# If method is not found, go up to singleton class B's super class.
# Until hit BasicObject, BasicObject.singleton_class.superclass is Class.
# BasicObject.singleton_class.instance_of? Class is True.
# Then up to instnace methods in :
# Class > Module > Object > Kernel > BasicObject.
# If method is still not found, return MethodError.
Case 2
├── class BasicObj
├── module Kernel
├── class Object
│ <Object's instance mth>
├── class Module
│ <Module's instance mth>
├── class Class
│ <Class's instance mth>
├── class BasicObj OR meta_class BasicObj
│ <Singleton class BasicObject's instance method>
│ <BasicObj's singleton mth>
├── module Kernel
│ <Kernel's instance mth>
├── class Object OR meta_class Object
│ <Singleton class Object's instance method>
│ <Obj's singleton mth>
│
├── class Module OR meta_class Module
│ <Singleton class Module's instance method>
│ <Mod's singleton mth>
│
├── class Class OR meta_class Class
│ <Singleton class Class's instance method>
│ <Class' singleton mth>
│ <print since here.>
B.class.mths
B.class.singleton_class.ancestors
# [#<Class:Class>, #<Class:Module>, #<Class:Object>,
# #<Class:BasicObject>, Class, Module, Object,
# Kernel, BasicObject]
B.class # Class
B.class.superclass # Module
B.class.ancestors
Class.ancestors
Case 3
<BasicObj's instance mths>
├── class BasicObj
| <class BasicObject's instance methods>
├── module Kernel
| <module Kernel's instance methods>
├── class Object
| <class Object's instance methods>
├── class Module
| <class Module's instance methods>
├── class Class
| <class Class's instance methods>
│ <BasicObject.singleton_class.superclass # => Class>
│ # so Case 3 go through class Class inheritance chain.
├── meta_class BasicObject
| <Singleton class BasicObjec's singleton methods>
├── meta_class Object
| <Singleton class Object's singleton methods>
├── meta_class Module
| <Singleton class Module's singleton methods>
├── meta_class Class
| <Singleton Class's singleton methods>
├── singelton_class Z
| <Singleton class Z's singleton methods>
├── module V
| <add V's instance methods to B's singleton class,
| but singleton class cannot call those instance methods directly.
| It needs initialization but singleton class cannot do so.>
├── singelton_class B
| <Singleton class B's singleton methods>
|
| <print since here.>
B.singleton_class.mths
Z.singleton_class.singleton_class.ancestors
# [#<Class:#<Class:Z>>, #<Class:#<Class:Object>>,
# #<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>,
# #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel,
# BasicObject]
# Start at B.singleton_class's singleton class.
# We found Ruby defautly add #<Class:Class>, #<Class:Module>
# in look up chain(ancestor).
B.singleton_class.includede_modules # [V, Kernel]
B.singleton_class.instance_methods # [V's instance methods, ...]
Case 4
├── class BasicObject => instance mths
├── module Kernel => instance mths
├── class Object => instance mths
├── class Module => instance mths
├── class Class => instance mths
│
├── class BasicObject OR singleton_class BasicObject
│ <Singleton class BasicObject's instance method>
│ <BasicObj's singleton mth>
├── class Object OR singleton_class Object
│ <Singleton class Object's instance method>
│ <Object's singleton mth>
├── class Z OR singleton_class Z
│ <Singleton class Z's instance method>
│ <Z's singleton mth>
├── module V
├── class B OR singelton_class B
│ <Singleton class B's instance method>
│ <B's singleton mth>
├── singleton_class of instance B => instance mths
B.new.singleton_class.mths
# check B.new singleton_class's singleton class ancestors(look up chain).
B.new.singleton_class.singleton_class.ancestors
#[#<Class:#<Class:#<B:0x007fb9e99030a8>>>, #<Class:B>,
# V, #<Class:Z>, #<Class:Object>, #<Class:BasicObject>,
# Class, Module, Object, Kernel, BasicObject]
Case 5
├── class BasicObject => instance mths
├── module Kernel => instance mths
│
├── class Object => instance mths
│
├── Super class Z => instance mths
│
├── module M => instance mths
│
├── class B => instance mths
├── singleton_class of instance B => instance mths
B.new.mths
B.new.singleton_class.ancestors
# [#<Class:#<B:0x007fc20d84cc28>>, B, M, Z, Object, Kernel, BasicObject]
Instance relation:
Class.singleton_class.instance_of? Class # => true
Module.singleton_class.instance_of? Class # => true
BasicObject.singleton_class.instance_of? Class # => true
Object.singleton_class.instance_of? Class # => true
Module.singleton_class.instance_of? Module # => false
- Method lookup: Check target's
singleton_class
, then up by its ancestors. - If the receiver is an instance, then the singleton class will not super
singleton Class
andsingelton Moudle
. If the receiver is an class object, then they will be inlcuded in lookup chain.
class A; end
class B < A; end
class C < B; end
A.superclass
#Object
B.superclass
# A
C.superclass
# B
# class C will first follow ancestor chain which is:
# C B A Object, Object inherits from BasicObject
# (class methods)
# If still cannot found method, then go C.class = Class, C is a instance of Class.
# lookup in Class ineritance chain, lookup all instance methods:
# Class Moudle Object Kernel BasicObject
# (all instance methods)
Look up Chain.
Method Lookup Flow.
-
module's class method can only be called by lexical lookup.
-
Define instance mehtod in
singleton_class of class A
will create singleton method for class A.
class G; end
class << G
def a; end
end
G.singleton_methods
# => [:a]
- Define singleton mehtod in
singleton_class of class A
will create singleton method forsingleton class of A
.
class G; end
class << G.singleton_class
def b; end
end
class << G
def self.v; end
end
# open G.singlton_class' singleton class.
G.singleton_class.singleton_methods
# => [:b, :v, :nesting, :constants]
-
Define instance mehtod in
singleton class of instance a
will create singleton method forinstance a
, instance method forsingleton class of instance a
. -
Define singleton mehtod in
instance a
will only be called by Lexical lookup:a::method_name
.
class V; end
v = V.new
def v.s; end
v.singleton_methods
# => [:s]
v.singleton_class.instance_methods
# => [:s]
# << means open some target's singleton class.
class << v.singleton_class
def g; end
def self.a; end
end
v.singleton_class.singleton_methods
# => [:g]
v.singleton_class.singleton_class.singleton_methods
# => [:a, :nesting, :constants]
class << v
def h; end
def self.e; end
end
v.singleton_methods
#=> [:s, :e]
v.singleton_class.singleton_methods
# => [:g, :h]
- Calling
super
looks for the next method in the method lookup chain.
Callin super from moudle's method.
Delegator
module Blah
def drive
super + 'fast'
end
end
class Vehicle
def drive
'driving'
end
end
class Car < Vehicle
prepend Blah
end
class Kar < Vehicle
include Blah
end
class Mar < Vehicle
extend Blah
end
Car.ancestors
# => [Blah, Car, Vehicle, Object, Kernel, BasicObject]
Kar.ancestors
# => [Kar, Blah, Vehicle, Object, Kernel, BasicObject]
Mar.ancestors
# [Mar, Vehicle, Object, Kernel, BasicObject]
Mar.singleton_class.ancestors
# <Class:Mar>, Blah, #<Class:Vehicle>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Car.new.drive
# drivingfast
Kar.new.drive
# drivingfast
class F
def drive
"Super E "
end
end
# Instance object can only extend module to get singleton methods.[for this object use only]
# class object can extend or include to get singleton and instance methods.
f = F.new.extend(Blah)
f.singelton_class.ancestors
# [#<Class:#<F:0x00000103002428>>, Blah, F, Object, Kernel, BasicObject]
# So when we call f.dirve => lookup chain will make Blah's drive call first.
# then super works to find F.
f.drive
# Super E fast
-
An instance object can only extend some module: which get to instance methods: reason:
extend
define method asdef self.method
instead ofdef method
. so you will only can useself.method
. -
Consider main object, self suppose should not have
:include
methods. but why it works? Becausemethod_missing
may directinclude
to its super class'smethod
. see code below.
self.respond_to? :include
self.respond_to? :extend
module Foo; def foo; end; end
module Zoo; def zoo; end; end
include Foo
# Open Object class and set instance method for it.
respond_to? :foo # true
extend Zoo
# Open main object and set self.zoo for it.
resond_to? :zoo # true
# So this means main object should have method_missing define as follows:
def method_missing mth, *args
self.send mth.to_sym, *args
end
# This let include can work for main object.
# include or extend always add a module after current class but before Super class.
# prepend can add before current object.
- Delegator:
class Delegatte
def initialize(obj)
@obj = obj
@obj.class.instance_methods(false).each do |m|
# For BowlerHatDecorator.new, receiver is BowlerHatDecorator.
# And self is BowlerHatDecorator.
# Though in method lookup chain will traverse othe class,
# self still set to BowlerHatDecorator.
self.class.superclass.send(:define_method, m) do |*args|
@obj.send(m, *args)
end
end
end
def describee
super
puts "I am describer"
end
end
class SimpleDelegattor < Delegatte
def initialize(obj)
# instance variable @obj will be created by super
super
end
end
class Character
def describe
puts "You are a dashing, rugged adventurer."
end
def describee
puts "You are a dashing, rugged adventurer."
end
end
class BowlerHatDecorator < SimpleDelegattor
def describe
super
puts "A jaunty bowler cap sits atop your head."
end
end
SimpleDelgator.instance_methods
# [:nil?, :===, :=~, :!~, :eql?, :hash, ...]
cohen = BowlerHatDecorator.new(Character.new)
# Now has define a describe method.
SimpleDelgator.instance_methods
# [:describe, :nil?, :===, :=~, :!~, :eql?, :hash, ...]
cohen.describe
# => You are a dashing, rugged adventurer.
# A jaunty bowler cap sits atop your head.
p cohen.singleton_class.ancestors
# [#<Class:#<BowlerHatDecorator:0x0000010211ee70>>,
# BowlerHatDecorator, SimpleDelegattor, Delegatte, Object,
# Kernel, BasicObject]
del = Delegatte.new(Character.new)
del.describee
# You are a dashing, rugged adventurer.
# I am describer
# example from:
# http://ruby-metaprogramming.rubylearning.com/html/ruby_metaprogramming_3.html
def who
person = "Matz"
yield("rocks")
end
person = "Matsumoto"
who do |y|
puts("#{person}, #{y} the world") # => Matsumoto, rocks the world
city = "Tokyo"
end
# puts city # => undefined local variable or method 'city' for main:Object (NameError)
def mem_result(klass, method)
mem = {}
Class.new(klass) do
define_method(method) do |*args|
if mem.has_key?(args)
mem[args]
else
mem[args] = super
end
end
end
end
-
When you define a block, it simply grabs the bindings that are there at that moment, then it carries those bindings along when you pass the block into a method.
-
Observe that the code in the block sees the person variable that was around when the block was defined, not the method's person variable. Hence a block captures the local bindings and carries them along with it. You can also define additional bindings inside the block, but they disappear after the block ends.
-
Passing
lambda
or->()
as block parameter will still be alambda
forreturn
. -
return
in lambda will treat as method and return back to current method scope.return
in block will return the result for current method. Usebreak
,continue
,next
insted. -
For procs created using
lambda
or->()
an error is generated if the wrong number of parameters are passed to a Proc with multiple parameters. Forprocs
created usingProc.new
orKernel.proc
, extra parameters are silently discarded.
Difference between proc, lambda, and block.
class G
def lambda_literal
[1,2,3,4].each(&->(x) { return false})
true
end
def lambda_test
[1,2,3,4].each(&lambda { |c| return false})
true
end
def block_test
[1,2,3,4].each do |i|
return false
end
true
end
def proc_test
[1,2,3,4].each(&Proc.new do
return false
end)
true
end
def check &block
p block
end
end
G.new.lambda_test
# true
G.new.lambda_literal
# true
G.new.block_test
# false
G.new.proc_test
# false
G.new.check &->() {}
# <Proc:0x0000010309b100@(irb):31 (lambda)>
# return => #<Proc:0x0000010309b100@(irb):31 (lambda)> Still a lambda
G.new.check &lambda {}
# <Proc:0x00000103092730@(irb):32 (lambda)>
# return => #<Proc:0x00000103092730@(irb):32 (lambda)> Still a lambda
G.new.check do; end
# <Proc:0x00000103089fe0@(irb):33> is a Proc
# return => #<Proc:0x00000103089fe0@(irb):33>
G.new.check &(Proc.new do; end)
# <Proc:0x0000010306b7e8@(irb):35>
# return => #<Proc:0x0000010306b7e8@(irb):35>
class B
def self.b &block
block.call
end
end
B.b &->{ p "hi"}
# "hi"
-
When Ruby does a method look-up and can't find a particular method, it calls a method named
method_missing()
on the original receiver. TheBasicObject#method_missing()
responds by raising a NoMethodError. -
The methods class_variable_get (this takes a symbol argument representing the variable name and it returns the variable’s value) and class_variable_set (this takes a symbol argument representing a variable name and a second argument which is the value to be assigned to the variable) can be used.
-
Use the
class_variables
method to obtain a list of class variables. -
Use the
instance_variable_get
andinstance_variable_set
to obtain list of instance variables.const_get
with symbols andconst_set
to get constants -
Entities like local variables, instance variables, self. . . are basically names bound to objects. We call them bindings.
-
eval
,module_eval
, andclass_eval
are operate on Class or Module rather than instance. Instance which is a specific object as well can callinstance_eval
to operate executoins and call instance variables. -
instance_eval
can also be used to add class methods in class object.class_eval
able to define an instance/singleton method in class or module,instance_eval
create instance or class methods depeonds on its receiver is either a instance or class. -
The
module_eval
andclass_eval
methods can be used to add and retrieve the values of class variables from outside a class. -
The
Module#define_method()
is a private instance method of the class Module. Thedefine_method
is only defined on classes and modules. The defined method then can be called by the instance. -
Leveraging by
respond_to?()
,class()
,instance_methods()
,instance_variables()
, we can check object information at run-time. -
You can call any methods with
send()
, including private methods. Usepublic_send()
to call oublic methods only. -
The
Module#define_method()
is a private instance method of the class Module. Thedefine_method
is only defined on classes and modules. You can dynamically define an instance method in the receiver with define_method( ). You just need to provide a method name and a block, which becomes the method body -
To remove existing methods, use the
remove_method
within the scope of a given class. If a method with the same name is defined for an ancestor of that class, the ancestor class method is not removed. Alternatively,undef_method
prevent specific class call that method even though the ancestor has the same method.
# 1.
# First open the class H.
# Thorugh << to set self as singleton class.
# Becasue it is in singleton class scope so it creates new scope.
# << will set self to singleton class, and open it.
x = 5
class H
# open H's singleton class
class << self
p x
p self.name
end
end
# directly open singleton class, set self to singleton class;
# create new scope because its in singleton class scope.
class << H
p x
p self.name # <Class:H> which is singleton class.
# define instance methods, for class H, which is H's singleton methods.
def h
p self # H.h => because reciver self is set to H, so will return H,
# rather than <Class:H>.
end
# define instance method in H.singleton_class' singleton class.
# which is H.singleton_class' singleton method
def self.g # H.singleton_class.g
p self # return <Class:H>.
end
end
# 2.
# This open class H, not in singleton class, so not create new scope.
# It is able to use local variable x.
# defines method in class H, but not in singleton class.
H.class_eval { p x; p self.name }
# instance_eval breaks apart the self into two parts.
# - the self that is used to execute methods
# - the self that is used when new methods are defined.
# When instance_eval is used:
# - new methods are defined on the singleton class.
# - but the self is always set the object itself. [Either class or instance]
# -> No matter inside or outside of new methods block.
# while method defined in singleton class,
# -> Ruby let it able to access outside local variable x
# -> Because it sets self as receiver's class.
Person.instance_eval do
def species
"Homo Sapien"
end
self.name #=> "Person"
end
# 3.
# This open singleton class for H class, will create new scope.
# The self will still assoicate to 'H' because for method s, H is the recevier.
# And because << is not used in here which will set self to singleton class
# So it is not able to call local variable x in a singleton method.
def H.s
p x
p self.name
end
# same as 3, here we still set self as H, then define s method in H's singleton class.
# so not able to use local variable outside.
class H
def self.s
p x
p self.name
end
end
mechanism | method resolution | method definition | new scope? |
---|---|---|---|
class Person | Person | Person | yes |
class << Person | Person’s metaclass | Person’s metaclass | yes |
Person.class_eval | Person | Person | no |
Person.instance_eval | Person | Person’s metaclass | no |
define new scope are not able to access outside local variable
-
In ruby 1.8.7 case 1 cannot call class_variable_get to get class variables.
-
In ruby 2.0.0 case 1 can call class_variable_get to get class variables though it is in the new scope.
-
<< not only open snigleton class, also set current self to self's singleton class.
-
Ruby is used to denote a singleton class: #<Class:#String:...>. ex: #Class:H
-
Each instance object have its own singleton class.
-
class_eval
open object class, and deirectly add method definition for it, so we can adddef self.method
for add class mehthods. -
instance_eval
will set self to receiver, and evaluate all methods in the block as singleton methods. If receiver is a class, then it define new singleton method in singleton class. If receiver is an instance, then it deine new instance method in instance's singleton class. -
Singleton class holds instance methods, but when it attached to an object which either class or instance, those instance methods convert to singleton methods for that class or instance.
-
You can’t create a new instance of a singleton class.
-
methods only defined in class or singleton classs, not instance object.
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self
module One
CONST = "Defined in One"
def self.eval_block(&block)
instance_eval(&block)
end
end
module Two
CONST = "Defined in Two"
def self.call_eval_block
One.eval_block do
CONST
end
end
end
Two.call_eval_block # => "Defined in Two" in 1.9.2 and 1.8.7 and later
Two.call_eval_block # => "Defined in One" in 1.9.0
-
In Ruby 1.8.7 and 1.9.2, calling constant in
instance_eval
will get the constant in the lexical scope which refered to. -
In Ruby 1.9.0 calling contant in
instance_eval
will get the constant in the receiver's class. -
instance_eval
open receiver's singleton class and bind each method's receiver if it is not specified(instance method, or so-called UnboundMethod). So methods all are binded. -
class_eval
open class and define it as usual. Methods without explicitly receiver areUnboundMethod
.
class T; end
T.class_eval do
def um; end
end
x = T.instance_method :um
# <UnboundMethod: T#um>
# To bind unbound method :um
t = T.new
x.bind t
# <Method: T#um>
# instance_eval automatically bind methods to its receiver.
T.instance_eval do
def m; end
end
s = T.method :m
# <Method: T.m>
-
In block of
instance_eval
, self be set and hijack to current receiver. Need caution for DSL. -
class
definition changes the default definee but method definition does not. -
When you give a receiver to a method definition, the method will be adde into the eigenclass of the receiver.
-
if you define a method with the normal method definition syntax, the default definee will have the method as an instance method.
-
class_eval
andinstance_eval
:self default definee class_eval the receiver the receiver instance_eval the receiver eigenclass of the receiver Must read: Three implicit contexts in Ruby.
DSL instance eval.
instance_eval change self. -
class_eval
andinstance_eval
can accept string as code, both setsself
to receiver during evaluation. We can useHere
doc or string to make codes rather than use block. When we callclass_eval
andinstance_eval
with block,self
will be set to receiver inside the block. And forinstnace_eval
the method definee will bind to receiver fromunbound method
tobound method
.
module Migration
class Base
def self.create_table name, &block
t = Table.new(name)
t.evaluate &block
t.create
end
end
end
class Table
attr_reader :name, :columns
def initialize(name)
@name = name.to_s
@columns = []
end
def evaluate &block
instance_eval &block
end
def string(*columns)
@columns += columns
end
def create
puts "creating the #{@name}, with columns #{columns.inspect}"
end
end
class Mardel < Migration::Base
def self.change
create_table :table do
string name("c1"), name("c2")
end
end
def self.set_name(name)
"#{name}_column"
end
end
Mardel.change
# ArgumentError: wrong number of arguments (1 for 0)
# This is cause error because, self has set to Table due to instance_eval.
# So `name("c1")` will get error because attr_reader :name not accept any argument.
# We make changes as below:
class Mardel < Migration::Base
def self.change
create_table :table do
string set_name("c1"), set_name("c2")
end
end
def self.set_name(name)
"#{name}_column"
end
end
Mardel.change
# NoMethodError: undefined method `set_name' for #<Table:0x0000010184c928>
# In here, we need passing block's binding, so can call Mardel.set_name method.
# Fix as below:
class Table
def method_missing(method, *args, &block)
@self_in_block.send(method, *args, &block)
end
def evaluate &block
@self_in_block = eval "self", block.binding
instance_eval &block
end
end
Mardel.change
# creating the table, with columns ["c1_column", "c2_column"]
# now works.
class_eval
only works on class object, which open that class and evaluate it. Not prepend receiver for it.eval
evalute string as expression. ex:eval "'p' + 'pp'"
- Inside the block of
clas_eval
orinstance_eval
, don't directly make method definition, instead, usedefine_method
to let closure works, which will enable to use variables outside of block. This is becausedefine_method
is not a keyword, so method definition will be resolve at runtime. If we usedef
, method definition will be directly scanned in lexical parsing. The same idea asalias
v.s.alias_method
.
class A; end
class B
def b
x = 10
A.class_eval do
# x can be used inside block due to closure feature.
p x
# def is a keyword, the method definition will be parsed in lexical parsing
# so x is actually some local var.
def g
x
end
end
# define_method not a key word.
# so variable x will be parse until run time.
A.class_eval do
define_method :z do
p self
x
end
end
end
end
A.new.z
# => 10
A.new.g
# NameError: undefined local variable or method `x' for #<A:0x0000010126d408>
- Methods in Ruby are not objects, but
Method
class can represent the given method. Methods are bind with symbol, so we can provide a symbol tosend
which calls the method bind with its name.
# First call to eval, the context is binding to the main object
# -> and call local variable str.
# Second call, the context moves inside the getBinding method.
# And the local value of str is now that of the
# -> str argument or variable within that method.
class MyClass
@@x = " x"
def initialize(s)
@mystr = s
end
def getBinding
return binding()
end
end
class MyOtherClass
@@x = " y"
def initialize(s)
@mystr = s
end
def getBinding
return binding()
end
end
@mystr = self.inspect
@@x = " some other value"
ob1 = MyClass.new("ob1 string")
ob2 = MyClass.new("ob2 string")
ob3 = MyOtherClass.new("ob3 string")
puts(eval("@mystr << @@x", ob1.getBinding))
puts(eval("@mystr << @@x", ob2.getBinding))
puts(eval("@mystr << @@x", ob3.getBinding))
puts(eval("@mystr << @@x", binding))
# In 1.9 output:
# ob1 string some other value
# ob2 string some other value
# ob3 string some other value
# main some other value
# In Ruby 1.9 does evaluate class variables within a binding.
# However, it gives preference to class variables.
# if they exist, in the current binding(main object).
# This is differ than 1.8, which is always binding context to receiver's context.
-
use
Kernel::eval
to evalute string expression with binding in main scope. -
Local and instance variables can captured by calling
binding
. You can access any local and instance variables by passing a reference of a binding context and callingeval
on it.
@x = 40
class B
def initialize
@x = 20
end
def get_binding
binding
end
end
class A
attr_accessor :x
def initialize
@x = 99
end
def a b
s = eval "@x", b
puts "A's @x: #{@x}"
puts "Binding's @x: #{s}"
@x = s
puts @x
@x
end
def ev b, binding
eval "a #{b}", binding
end
end
obj = A.new
b = B.new
# use Kernel::eval, which is in main scope
# send context, variables to obj
# obj.ev "binding", binding
# NoMethodError: undefined method `a' for main:Object.
# It is because obj.ev binding to main scope, but main scope dont have method a.
def a b
puts 'in Main scope'
end
obj.ev "binding", binding
# in Main scope
obj.a binding
# A's @x: 99
# Binding's @x: 40
# 40
# => 40
obj.a b.get_binding
# A's @x: 40
# Binding's @x: 20
# 20
# => 20
http://www.hokstad.com/ruby-object-model
# An block cannot exist alone, it needs a method to be attached with it.
# We can convert a block to Proc object.
# a = Proc.new{|x| x = x*10; puts(x) }
# b = lambda{|x| x = x*10; puts(x) }
# c = proc{|x| x.capitalize! }
# send does not take block as params.
def method_missing( methodname, *args )
self.class.send( :define_method, methodname,
# => A Proc object pass into send *args array
lambda{ |*args| puts( args.inspect) }
)
end
-
The extend method will mix a module’s instance methods at the class level. The instance method defined in the Math module can be used as a class/static method.
-
Module's class methods can only be call by constant lookup implicitly or explicitly. Some object include a module can change object to module's namespace, then can call the class method through the constant lookup by
object.module_eval
.
module A
module B
def b; end
def self.bb; end
end
end
class G
include A
end
G::B
# => A::B, Object G change to namespace A and do constant Lookup.
class V
extend A
end
V.singleton_class::B
# => A::B
-
The include method will mix a module’s methods at the instance level, meaning that the methods will become instance methods.
-
The
Module.append_features
on each parameter forextend
orinclude
in reverse order. -
If we include multiple module in one line, it will look up by its declaring order. If we include or extend in multiple times, then it will look up from the reverse declaring order.
module A
def say; puts "In A"; end
end
module B
def say; puts "In B"; end
end
class Parent
def say; puts "In Parent"; end
end
class Child
include A, B
end
p Child.ancestors
# [Child, A, B, Object, Kernel, BasicObject]
Child.new.say
# In A
class ChildToo
include A
include B
end
p ChildToo.ancestors
# [ChildToo, B, A, Object, Kernel, BasicObject]
ChildToo.new.say
# In B
-
The difference is that
include
will add the included class to the ancestors of the including class, whereasextend
will add the extended class to the ancestors of the extending classes' singleton class. -
Recall that for each instance inherits from
Object
,Object
provides aextend
method which add instance method from the module. It is different as declareextend
in class definition, which add methods to object's singleton class.http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/
-
extend a module A in another module B will add A to singleton class of B, include a module A in module B will add A to B's class.
-
class method
defined in module is prepare for receiver's singleton class to use,instance method
is prepare for receiver's instance. -
Class.singleton_class.ancestors
to track inheritance chain from singleton class. -
Module cannot be instantiate with
new
for itself.
# Namespace will transfer to module's namespace.
module A
module B
module C
def a(val = nil)
!val ? super val : puts "IN C"
end
end
def a(val = nil)
puts "IN B"
end
end
module D
def self.d
puts "IN D"
end
end
end
module A
include B
extend D
# D will not be found in ancestors output, but in singleton class' chain.
end
class G
include A
end
class T
extend A
end
T.singleton_class.ancestors
# [#<Class:T>, A, A::B, #<Class:Object>,
# #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
T.singleton_class.included_modules
# [A, A::B, Kernel]
T.singleton_class::D
# A::D
# Because ruby cannot find namespace in T class, so go module A to find namespace D.
T.singleton_class::D.d
# IN D
# now call singleton method d in moudle D.
# We cannot call instance method here, because receiver is a class.
G.ancestors
# [G, A, A::B, Object, Kernel, BasicObject]
# Include or extend will not transfer namespace to for host module.
# For class or singleton_class, it will check include or extended module namespace for finding methods.
G::B
# A::B
# Because cannot find namespace in G, so go to namespace A then go module B and fount it.
G::C
# A::B::C, ruby go deeper to find namespace C under module B.
G::D.d
# In D
G::B.a
# NoMethodError
G.new.a("str")
# In B
# because we not include C in A, so it will only call B::a
module A
include C
end
class G
include A
end
G.include A
# Now we add a new include in module A.
# But class G will load previous module A's included modules.
# So we need to reopen the class G and include module A for reloading again.
# Or just one line G.include A for same thing above.
G.ancestors
# [G, A, A::B::C, A::B, Object, Kernel, BasicObject]
G.new.a("str")
# In C
G.new.a
# In B
# finally check moudle A's namspaces.
A.included_modules
# [A::B::C, A::B]
A.singleton_class.included_modules
# [A::D, Kernel]
module W
class O
def o
puts "OO"
end
def self.o
puts "Singleton OO"
end
end
end
# class dont have to include in module. Just namespace for it.
class L
include W
end
L::O
# W::O
L::O.new.o
# "OO"
L::O.o
# "Singleton OO"
-
In module, define class method such
self.method
will be in module level methods. Which only be access in that namespace explicitly or implicitly. -
Include module make instance method in module become instance method in that included class.
-
Extend module make instance method in module become class method in that extended class.
-
Class method in module is only call by the way of constant lookup.
-
So in module A extend B, use extend to get instance methods from B to A's class methods, which are the same as
self.instance_methods_of_b
, B's class methods still need constant lookup to call.
module G
def g; end
def self.gg; end
end
class A
extend G
end
A.singleton_methods
# have :g
A.include G
A.instance_methods
# have :g
module H
extend G
end
# because extend send instance method of G to H's singleton class.
H.singleton_methods.include? :g
# true
# rails c
module A
module S
extend ActiveSupport::Concern
included do
def self.n
@@n
end
def self.n= (str)
@@n = str
end
end
end
module D
def d
puts "IN D"
puts "name: #{n}"
end
end
module Z
def pi; end
# This made :ty in A::Z namespace only.
def self.ty; end
end
module ZZ
def pizz; end
# This made :ty in A::ZZ namespace only.
def self.tyzz; end
end
end
module A
extend D
include S
include Z
extend ZZ
end
A.ancestors
# => [A, A::Z, A::S]
A.singleton_class.ancestors
# => [#<Class:A>, A::ZZ, A::D, Module,
ActiveSupport::Dependencies::ModuleConstMissing,
Object, PP::ObjectMixin, ActiveSupport::Dependencies::Loadable,
JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]
A::Z.ty
# => nil
A::ZZ.tyzz
# => nil
# self.method in module will make that method only in that namespace scope.
# Instace method will only be used in instance objec only.
# Include instance method will be usable. Because it include in that instance class.
# Extend instance method have no meaning in that instance.
# Because instance method is in singleton_class but cannot be called.
A::ZZ.instance_methods
# => [:pizz]
class T
include A
end
T.new.singleton_class::Z
# => A::Z
T.new.singleton_class::Z.instance_methods
# => [:pi]
T.new.singleton_class::ZZ.instance_methods
# => [:pizz]
T.module_eval do
A::ZZ.tyzz
end
# tyzz existed for module level namespace A::ZZ
T.module_eval do
A::Z.ty
end
# ty existed for module level namespace A::Z
T.module_eval do
# A::Z.pi
# no-method error
end
A.n = "str"
A.d
# IN D
# name: str
A.respond_to? :pizz
# true
# Conclude this:
# module A extend module B
# B's instance method will be treat as A's method
# B's class method will be treat as A::B's method
#
# module A include module B
# B's instance method will be in class T which include or extend A's method
# if class T include A => B's instance method in T.new.methods
# if class T extend A => B's instance method in T.new.singleton_class.methods
# B's class method will be treat as A::B's method
# if class T include A => B's class method in T.new.singleton_class::B
# if class T extend A => B's class method cannot be called directly.
# (danger way: T.singleton_class::A::B) or use module_eval
T.module_eval do
A::ZZ.tyzz
end
# class MaskedString < String
# def tr_vowel
# tr 'aeiou', '*'
# end
# def self.tr_vowel str
# str.tr 'aeiou', '*'
# end
# end
# Define the following class without class MaskedString,
# -> def tr_vowel and def self.tr_vowel.
# Hint:
# instance_eval, class_eval, define_method, module,
# -> extend, extended, include, included.
# Naming as constant, so the anonymous class: Class.new will be given a name.
ClassObject = Class.new(String)
module AddClassMethods
self.instance_eval do
define_method :tr_vowel do |str|
str.tr 'aeiou', '*'
end
end
end
ClassObject.class_eval do
define_method :tr_vowel do
tr 'aeiou', '*'
end
# instance method
def lets
puts 'lets'
end
# singleton method
def self.lets
puts 'self.lets'
end
end
ClassObject.instance_eval do
extend AddClassMethods
define_singleton_method :as do
puts "as"
end
# singleton method
# def less === def self.less
def less
puts 'less'
end
end
puts ClassObject.tr_vowel("America")
puts ClassObject.new("ByMyWill").tr_vowel
-
yield
expect method carry a block and transfer evaluation from method to that block. By default method's argument list don't have to list block argument, but provide it explicitly with&
in argument list will convert that block to Proc object. It is more readable and avoid block chain pass arguments which may affect performance. -
Although the method did not explicitly ask for the block in its arguments list, the
yield
can call the block. This can be implemented in a more explicit way using a Proc argument. -
Whenever a block is appended to a method call, Ruby automatically implicitly converts it to a Proc object but one without an explicit name. The method, however, has a way to access this Proc, by means of the
yield
statement. -
If
&
provides in a argument, the block attached to this method is explicilty converted to a Proc object and gets assigned to that last argument by its argument name.
# bad
def with_tmp_dir
Dir.mktmpdir do |tmp_dir|
Dir.chdir(tmp_dir) { |dir| yield dir } # this block just passes arguments to yield
end
end
# good
def with_tmp_dir(&block)
Dir.mktmpdir do |tmp_dir|
Dir.chdir(tmp_dir, &block)
end
end
with_tmp_dir do |dir|
puts "dir is accessible as parameter and pwd is set: #{dir}"
end
Consider using explicit block argument to avoid writing block literal that just passes its arguments to another block. Beware of the performance impact, though, as the block gets converted to a Proc.
https://github.com/bbatsov/ruby-style-guide
http://rubylearning.com/blog/2010/11/30/how-do-i-build-dsls-with-yield-and-instance_eval/
http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Method_Calls#Blocks
Method missing.
Ruby’s define_method, method_missing, and instance_eval.
class A
def method_missing(name, *args, &block)
if name ~= /^ss$/
puts "Name: #{name}"
puts "Args: #{args}"
puts "Block: #{block}" if block_given?
else
super
end
end
end
A.new.ss(1, 2, 10) { |x| puts x }
# Name: ss
# Args: [1, 2, 10]
# Block: <#<Proc:0x0000010215ed68@(irb)>
x = A.new
class << x
define_mehthod("mth") { |args = nil| args ? puts "#{args}" : puts "No args input." }
define_mehthod(:sym_mth) { |*args| puts "Args: #{args}" }
end
x.mth("args in here")
# args in here
x.mth
# No args input
x.sym_mth(1, 2, 3)
# Args: [1, 2, 3]
x.sym_mth
# Args:
-
Fiber requires a block. Inside block, put
Fiber
class object to use its class method. -
Arguments passed to
resume
will be the value of theFiber.yield
expression or will be passed as block parameters to the fiber’s block if this is the first resume. -
Alternatively, when resume is called it evaluates to the arguments passed to the next Fiber.yield statement inside the fiber’s block, or to the block value if it runs to completion without hit any further Fiber.yield.
-
Due to above statment, fiber last return result is the same as argument value passed to
resume
. -
Fiber can store context, block switch to new context every time.
class Enum
def initialize
@yielder = Fiber.new do
yield Fiber
end
end
def next
@yielder.resume
end
def mock_yield
next
end
end
e = Enum.new do |yielder|
num = 1
loop do
# Fiber.yield
yielder.yield num
num += 1
end
end
p e.mock_yield # 1
p e.mock_yield # 2
p e.mock_yield # 3
f = Fiber.new do |arg|
Fiber.yield arg + 5, arg + 6
end
f.resume 5
# [10, 11]
f.resume 5
# 5
f.resume 5
# fiber dead
require 'fiber'
f.alive?
# false
f = Fiber.new do |arg|
p 1
Fiber.yield arg + 5
p 2
Fiber.yield
p 3
end
# first resume run until hit Fiber.yield or block end.
# if hit block yield, return its control to f's scope.
f.resume 4
# 1
# => 9
f.resume 4
# 2
# => nil
# f.resume transfer control to Fiber block, finish print 3, hit the block end
f.resume 4
# 3
# => 3
z = Fiber.new do |arg|
Fiber.yield arg + 5
end
z.reusme 4
# => 9
z.resume 4
# => 4
# Because resume turns control back to Fiber block, but does not hit any Fiber.yield in next expressions,
# It returns reusme's argument value as block's return value.
- Ruby allow letters, underscore, and numbers not in first letter, end with
=
,?
,!
for naming conventions. - It also support a syntax sugar for
[]
and[]=
which is[](id)
,[](key, value)
for hash or array liked method. - And many operators can redeine in your class, but you would not able to process original operators if it support from superclass.
class MockHash
def initialize(input)
@hash = Hash(input)
end
def [](id)
@hash[id]
end
def []=(id, value)
@hash[id] = value
end
end
class MockHashII
def initialize(input)
@hash = Hash(input)
end
def to_h
@hash
end
def method_missing(mth, *args)
to_h.public_send(mth, *args)
end
end
class MockArray
def initialize(input)
@ary = Array(input)
end
def [](id)
@ary[id]
end
def []=(id, value)
@ary[id] = value
end
def to_a
puts "Loosely conversion for explicitly cal."
@ary
end
def to_ary
puts "Strct conversion for implicitly call."
@ary
end
def <<(values)
@ary << values
end
end
ary = MockArray.new [1,2,3,4,5,6]
hash = MockHash.new a: 1, b: 2, c: 3
hash2 = MockHashII.new z: 1, b: 2, c: 3
hash[:a]
# => 1
hash[:z] = 8
# => 8
ary[0]
# => 1
ary[6] = 9
#=> 9
hash2[:v] = 8
# => 8
[] + ary
# Strct conversion for implicitly call.
# => [1, 2, 3, 4, 5, 6, 9]
a = *ary
# Loosely conversion for explicitly cal.
# => [1, 2, 3, 4, 5, 6, 9]
- Ruby core class never use explicit conversions if not explicit conversion calls. But it have counter example. Check Confident Ruby book page 58 for more info.
Counter example :
- When we use string interpolation, Ruby implicitly uses #to_s to convert arbitrary objects to strings.
"Time class use explicit conversion: #{Time.now}"
# string interploation conver Time object by Time#to_s method
class B
def to_s
"B"
end
end
"" + B.new
# TypeError: no implicit conversion of B into String
String B.new
# "B"
# call `to_s` instead
Explict or implicit conversion methods
Method naming
Ruby conversion protocols.
- Use implicit conversion if you want to guard the input
type
such asnil
value. If you dont care about the edge case of input, just want to run the logic, use explicitly conversion instead.
nil.to_s
# convert the result "" and kepp logic running
nil.to_str
# NoMethodError: undefined method `to_str' for nil:NilClass
class A
def initialize(app = nil)
@app = app
end
def call(env)
p env
p "A"
end
end
class B
def initialize(app)
@app = app
end
def call(env)
p env
p "B"
@app.call(env)
end
end
class C
def initialize(app)
@app = app
end
def call(env)
p env
p "C"
@app.call(env)
end
end
# simulate Rack::Builder call chain
app_a = A.new
stack = [Proc.new{ |app| B.new app}, Proc.new{ |app| C.new app}]
stack = stack.reverse.inject(app_a) { |e, a| e[a] }
# (Proc.new{ |app| B.new app }.call(Proc.new{ |app| C.new app}))
# #<B:0x000001030bff00 @app=#<C:0x000001030bff28 @app=#<A:0x000001030bff50 @app=nil>>>
# return B object
stack.call 5
# will go B.call() method, if it calls @app.call, then will pass to C.call and so on.
# In here B.call is object method, not Proc's call().
- If we override
#eql?
method then must override#hash?
in class for Hash key comparison. Otherwise ruby will back to its default implementation for#hash
in Object class. - Each hash insertion generate hash values first, then compare whether two keys is duplicate(
#eql?
return true) repeatedly.eql?
andhash
are used when insertion to some hash-based data strucutre(hash, set). Set/Hash #include?
method will get hash values first, check whether itsobecjt_id
and hash value exist in the set/hash. It shortcircuit to return true if it can find the entry, o.w. calleql?
method to determine. Check theEQUAL(table,x,y)
function in source code to prove this.
// http://rxr.whitequark.org/mri/source/st.c#396
static inline st_index_t
find_packed_index(st_table *table, st_index_t hash_val, st_data_t key)
{
st_index_t i = 0;
while (i < table->real_entries &&
// if hash_val exists and key is exist in table(use ==), break loop.
(PHASH(table, i) != hash_val || !EQUAL(table, key, PKEY(table, i)))) {
i++;
}
return i;
}
#define collision_check 0
int
st_lookup(st_table *table, register st_data_t key, st_data_t *value)
{
st_index_t hash_val;
register st_table_entry *ptr;
hash_val = do_hash(key, table);
if (table->entries_packed) {
st_index_t i = find_packed_index(table, hash_val, key);
if (i < table->real_entries) {
if (value != 0) *value = PVAL(table, i);
return 1;
}
return 0;
}
ptr = find_entry(table, key, hash_val, hash_val % table->num_bins);
if (ptr == 0) {
return 0;
}
else {
if (value != 0) *value = ptr->record;
return 1;
}
}
// EQUAL(table,x,y) function
// http://rxr.whitequark.org/mri/source/st.c#085
#define EQUAL(table,x,y) ((x)==(y) || (*(table)->type->compare)((x),(y)) == 0)
# Only override #eql?, Hash insertion will call Object#hash for key's hash value.
class B
def eql?(c)
true
end
end
{B.new => 5, B.new => 7}
# Two entries
# {#<B:0x007f8d1c807680>=>5, #<B:0x007f8d1c807658>=>7}
# Only override #hash
# Hash insertion will call Object#eql? for key's hash value comparison.
# But Object#eql? will also compare object_id by default implementation.
class T
def hash
p "T's hash"
0
end
end
{T.new => 5, T.new => 7}
# "T's hash"
# "T's hash"
# => {#<T:0x007f8d1d86f068>=>5, #<T:0x007f8d1d86f040>=>7}
class V
def hash
p "V's hash #{self.object_id}"
0
end
def eql?(c)
p "V's eql?"
self.hash == c.hash
end
end
k = V.new
# k.object_id 70122022664000
b = V.new
# b.object_id 70122022646420
# each hash insertion generate hash values first,
# then compare whether two key is duplicate.
{ k => 5}
# "V's hash 70122022664000"
# => {#<V:0x007f8d1c80ee80>=>5}
{ k => 5, b => 7}
# "V's hash 70122022664000"
# "V's hash 70122022646420"
# "V's eql?"
# "V's hash 70122022646420"
# "V's hash 70122022664000"
=> {#<V:0x007f8d1c80ee80>=>6}
# {#<V:0x007f8d1c8bb0e0>=>7}
{ k => 5, v => 7, V.new => 8}
# "V's hash 70122022664000"
# "V's hash 70122022646420"
# "V's eql?"
# "V's hash 70122022646420"
# "V's hash 70122022664000"
# "V's hash 70122014579860"
# "V's eql?"
# "V's hash 70122014579860"
# "V's hash 70122022664000"
# => {#<V:0x007f8d1c80ee80>=>8}
- If you redefine hash method with different hash value in later(duck-typing), then the hash value of entries in set will not change, which result inconsistent hash values.
- Caveat: Always make hash method returns consistent values in all time(don't do duck typing on
#hash
method), or you have to update the hash values for each entries.
class Point
def eql?(b)
p "eql? #{b}"
#super
end
def hash
p "hash #{self}"
0
end
end
v = Point.new
p = Point.new
require 'set'
s = Set.new
s << v
s.include? p
# "hash #<Point:0x007fd21a868c58>"
# "eql? #<Point:0x007fd21a879300>"
class Point
def hash
p "hash changed #{self}"
21345
end
end
# Now the hash value for v has changed.
# But in set s, its entry still keep previous hash value for instance v.
s.include? v
# "hash changed #<Point:0x007fd21a879300>"
# false
- http://ruby-doc.org/core-2.2.0/Object.html#method-i-hash
- http://openhome.cc/Gossip/Ruby/Equality.html (Mandarin)
- http://rxr.whitequark.org/mri/source/st.c#409 st_lookup source code
- If we create an instance through the
Class
initialization, then it is instance's class will beClass
rather than its implemented class name, this implemented class will be super class of thatClass
. So the strcitly class name comparison may not work, e.g.p2.class == p1.class
.
class Point
def initialize(x, y)
@x, @y = x, y
end
end
p = Class.new(Point) do
def to_s
"#{@x}, #{@y}"
end
end
p1 = Point.new(2,1)
p.new(2,1)
# <#<Class:0x007f85ab8f7c70>:0x007f85aa07f2d8 @x=2, @y=1>
p2.class
# Class
# p2 cannot equal to p1, though implementation are the same.
p2.class == p1.class
# false
p2.superclass
# Point
p2.is_a? p1.class
# false, p2 is not an instance of Point.
block === proc
are almost the same. Both accept var args,lambda
restrict args count must match as its declaration.lambda
is like a method definition,proc
are more like a code snippet reside in some other method definition or code block.proc
act as similar as code snippet inside method scope. So when you callproc
in that scope andproc
hasreturn
keyword, then it will return from that outers cope and stop rest of code in that outer scope.lambda
just act as normal method, return from itslambda
scope and continue execution for outer scope.
http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/
http://ixti.net/development/ruby/2011/09/03/understanding-rack-builder.html
http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/
http://ruby-metaprogramming.rubylearning.com/html/ruby_metaprogramming_3.html
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self
http://www.infoq.com/presentations/metaprogramming-ruby
http://www.hokstad.com/ruby-object-model
http://ruby-metaprogramming.rubylearning.com/html/seeingMetaclassesClearly.html
Book of Ruby
http://pragprog.com/book/ruby/programming-ruby
http://ruby-metaprogramming.rubylearning.com/
http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Method_Calls#Blocks
http://rubylearning.com/blog/2010/11/30/how-do-i-build-dsls-with-yield-and-instance_eval/