
No words needed. Charlie explains it perfectly
Ruby general notes
Hashes syntax
- There is two syntaxes to write hashes with symbols:
test = { :name => "sergio" } is the same than
test = { name: "sergio" }
- When a hash is the last argument in a method or function call, the curly braces are optional, so this two are equivalent:
stylesheet_link_tag 'application', { media: 'all', 'data-turbolinks-track': 'reload' }
# is the same as this:
stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
- When a hash is created passing an argument:
h = Hash.new('whatever') that will be the default value for non existing keys
h[:foo] # will return "whatever"
begin end rescue ensure
Begin end creates a scope. Whatever code we put inside will be ran. Interesting fact: Methods in Ruby are no more that begin end blocks.
To run a begin end block ONLY if the instance variable doesn't exist:
def my_method
@whatever ||= begin
#your code here
end
end
To explain "rescue" and "ensure" we are going to use an example:
def create_or_update_batch
@batch ||= begin
BookBatch.create(book_batch.batch_attrs)
end
@batch.update
rescue
raise_book_batch_error
ensure
clean_up_orphaned_book_batches
end
"rescue" will only run if an exception is thrown (like "catch" in javascript)
"ensure" will always run, whether there is an exception or not. (like "always" in javascript)
If we don't provide the type of error to "rescue", it will use "StandardError"
rescue ActiveRecord::AttributeAssignmentError
Ruby metaprogramming I
caveats
- If we create two classes with the same name, we end up with one class with all the methods.
- Classes are open classes, what means that we can always add -or rewrite- methods
instance variables vs class variables
- Instance variables do not exist until they are assigned. @v1 won't exists until the method is called. That's why several instances of the same class can have different instance variables and with different values. Can only be used in instance methods.
class MyClass
def my_method
@v = 1
end
end
- Class variables must be initialized before they are used. Then they can be modified in the methods if we want to. Can be used in class_methods and instance methods:
class Customer
@@no_of_customers = 0
def initialize(id, name, addr)
@cust_id = id
@cust_name = name
@cust_addr = addr
end
def display_details()
puts "Customer id #@cust_id"
puts "Customer name #@cust_name"
puts "Customer address #@cust_addr"
end
def total_no_of_customers()
@@no_of_customers += 1
puts "Total number of customers: #@@no_of_customers"
end
end
# Create Objects
cust1 = Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2 = Customer.new("2", "Poul", "New Empire road, Khandala")
# Call Methods
cust1.total_no_of_customers()
cust2.total_no_of_customers()
#This is the result
Total number of customers: 1
Total number of customers: 2
Get instance and class methods and variables
** Filter the search
.grep(/whatever/)
- All instance variables of a class A:
A.instance_variables
- All instance variables of an instance of A:
a = A.new
a.instance_variables
- All class variables of a class A:
A.class_variables
- Instance methods of class A:
A.instance_methods
- Instance methods of instance of class A:
a = A.new
a.methods
- You have as well the methods "private_methods" (for class private methods) and "private_instance_methods" (for private instance methods of the class)
classes and modules
Classes are modules that you can instantiate. Normally you use modules when you want to include them somewhere -and add methods- and we use classes when you want to instantiate it and/or do inheritance.
namespaces
To avoid collisions with the name of classes. Normally are set putting classes within modules
Include module
class MyClass
include MyModule
end
Will include all the methods of the module in the instance of the class.
- It's methods will be included in the methods that inherit from MyClass
- Modules are included as well when calling the method "ancestors"
- There is a similar method to "include" which is "prepend". It's just the same but instead of putting the method above in the herarchy, it puts it below.
extend vs include a module
- "include" add the methods to the class
- "extend" add the methods just to that instance:
my_instance.extend(MyModule)
Object system schema
class is the class that instantiate the object
superclass is the parent class of the class, so it's characteristics are inherited
in this way you can see that the classes are objects as well, meaning instances of another class
And more complete:
Constants
Everything that begins with an uppercase letter, including names of classes and modules, is a constant. It is pretty much like a variable, with a HUGE difference. They have scope!!!
Notice how the constants are not the same (event though the reference to the same)
About the path of the classes and all that crap
This example makes it pretty clear:
module M
class C
X = 'a constant'
end C::X # => "a constant"
end
M::C::X # => "a constant"
modules and ancestor chain
When you "include" a module in a class -or even in another module-, Ruby inserts the module in the ancestors chain, right above the including class itself.
When you do the same but with "prepend" it happens exactly the same, but the module get inserted inmediatelly after the class in the ancestor chain.
You can check the ancestors chain of a class -not an instance- using the method "ancestors":
MyClass.ancestors
If you want to do it of an instance, obviously you can use myclass_instance.class.ancestors
Kernel
Kernel is a module "included" in Object. This means that it's instance methods (and private instance methods) can be call in every class as "program methods". For example "print". This is the way Ruby manages to have programs methods. You could call them with self.print but as you can ignore the "self" they look like program methods: eg: print "tu puta madre"
self
references the "current" object
instance and class public and private methods
Instance method => can only be called in an instance
Class method => can only be called in a class (pretty much like "static" in javascript)
examples:
my_instance.my_instance_method
MyClass.my_class_method
class MyClass
def my_instance_method
end
def self.my_class_method
end
end
Public method => can be called is the instance or the class directly -depending of if it's a instance of class method-
Private method => have to be called from within another method of the instance or the class -depending of if it's an instance or a class method-
examples:
class MyClass
def my_instance_method
end
def self.my_class_method
end
private
def my_private_instance_method
end
class << self
private
def my_private_class_method
[:bark, :roll_over, :fetch]
end
end
end
note how this WON'T work when creating a private class method:
private
def self.my_private_class_method
end
other way to define private class method is using "private_class_method like" this:
class Dog
def self.tricks
[:bark, :roll_over, :fetch]
end
private_class_method :tricks
end
or like this -very similar-
class Dog
private_class_method def self.tricks
[:bark, :roll_over, :fetch]
end
end
or using modules like this:
class Dog
module ClassMethods
private
def tricks
[:bark, :roll_over, :fetch]
end
end
extend ClassMethods
end
Dynamic dispatch
With the "send" method you can trigger any method any Class, Module or instance.
MyClass.send(:my_class_method, arg1, arg2, arg3)
This has the advantage that you can wait until the very last minute to decide the method that you want to call and you can decide it dynamically.
** "send" has the capacity to call public and private methods. The "public_send" method version of the method only can call public methods
Define methods dynamically
You can define a method dynamically with "define_method".
class Developer
["frontend", "backend"].each do |method|
define_method "coding_#{method}" do
p "writing " + method.to_s
end
end
end
developer = Developer.new
developer.coding_frontend
# "writing frontend"
developer.coding_backend
# "writing backend"
method_missing
"method_missing" is a private instance method of BasicObject that every object inherits.
All this methods does is triggering a "NoMethod" error whenever we call a method that doesn't exist.
Each message landing on "method_missing" includes the name of the method that. was calles, plus any arguments and blocks associated with the call.
**Overriding "method_missing" allows you to call methods that don't really exist.
So you can do something like this:
class MyClass
def missing_method(name, *args)
'the method: #{name} is not found, lets do something with it'
end
end
One of the problems that you might have with this is that you have a real "method_missing" error with this class, it will swallow it and will be very difficult to track.
Blank slates
It might happen to you that you run in a problem like this:
class MyClass
def my_method
'whatever'
end
end
i = MyClass.new
i.display //nil
Instead of "NoMethodError" we are getting "nil". Why? It is because "display" is a method of object, which obviously is in the ancestors chain so the method is found, even though we wouldn't expect that to happen.
If you don't specify a superclass, your classes inherit by default from "Object" which superclass is "BasicObject".
You can have a blank slate class inheriting from BasicObject: class MyClass < BasicObject or you can remove the methods that you want from the class:
- undef_method whatever_method: will remove the method from the class and inherited ones
- remove_methos whatever_method: will only remove methods from the class, not inherited, which probably makes it useless for this case but is good that you know about this one
access of variables from an outer scope
Unlike javascript, in Ruby you can't access variables from an outer scope -unless they are global variables ($) class variables (@@) or instance variables (@)-
class Pepe
name = 'Pepe'
def say_name
puts name
end
end
p = Pepe.new
p.say_name #Error
Scope gates and flattening the scope
Places where a program leaves the old scope behind and opens a new one:
- Class definitions -keyword "class"-
- Modules definitions -keyword "module"-
- Methods -keyword "def"-
v1 = 1
class MyClass #scope gate entering class
v2 = 2
local_variables
def my_method #scope gate entering method
v3 = 3
local_variables
end #scope gate leaving method
local_variables
end #scope gate leaving class
But we can write exactly this code eliminating all the scope gates:
v1 = 1
MyClass = Class.new do
v2 = v2
define_method :my_method do
v3 = 3
end
end
voila! all variables are accessible from inner scopes now
Closure
A closure is a first-class function with an environment. The environment is a mapping to the variables that existed when the closure was created. The closure will retain its access to these variables, even if they’re defined in another scope.
Ruby doesn’t have first-class functions, but it does have closures in the form of blocks, procs and lambdas. Blocks are used for passing blocks of code to methods, and procs and lambda’s allow storing blocks of code in variables.
Example of closure using a block:
def my_method
x = "Goodbye" yield("cruel")
end
x = "Hello"
my_method {|y| "#{x}, #{y} world" } # => "Hello, cruel world"
As you can see, the block takes the x variable from the scope where it is defined and carries it with it to a different scope, ignoring the "x" value in the scope where is called.
instance_eval() and instance_exec()
- instance_eval evaluates a block in the context of an existing instance.
class MyClass def initialize
@v = 1
end end
obj = MyClass.new
obj.instance_eval do
self # => #<MyClass:0x3340dc @v=1> @v # => 1
end
- instance_exec is the same but let's you pass params to that env:
class C
def initialize
@x = 1
end
end
class D
def twisted_method
@y = 2
C.new.instance_exec(@y) {|y| "@x: #{@x}, @y: #{y}" } end
end
D.new.twisted_method # => "@x: 1, @y: 2"
Callable objects -you can call them using "call"-
- Blocks
- Procs
- Lambdas (Lambdas are Procs created with a lambda. So are basically Procs, but with some small difference)
- Methods
Procs (this includes lambdas)
In Ruby most of stuff are objects. Well, blocks are NOT. Proc is a block that has been turned into an object.
In this way the block becomes "first class citizen" and you can defer the call to whenever you want to.
Ways to create a Proc (or which is the same, to convert a block to a Proc):
inc = Proc.new {|x| x + 1}
dec = lambda {|x| x + 1} // exactly the same as the next line
dec = ->(x) {|x| x + 1}
There is another way to convert a block to a Proc, the "&" operator. It is used when executing the block with "yield" is not enough, and this can happen in two cases:
- You want to pass the block to another method (or even another block)
- You want to convert the block to a Proc
The way to do this is to pass a special argument to the method. This must be that last argument and prefixed with "&":
def math(a, b)
yield(a, b)
end
def do_math(a, b, &operation)
math(a, b, &operation)
end
do_math(2,3){|x,y|x*y} #=>6
What it actually does is convert a block to a Proc and vice-versa. So what you have in operation is already a Proc. But if you have a Proc and want to convert it to a block, you can do it as well, this way:
def my_method(greeting)
"#{greeting}, #{yield}!"
end
my_proc = proc { "Bill" }
my_method("Hello", &my_proc)
Differences between Procs and Lambdas (Procs created with lambda)
There is only two important differences -and many small ones that we are not going to talk about-
- When using explicit return (there is not difference with the implicit return):
def double(callable_object)
callable_object.call * 2
end
l = lambda { return 10 }
double(l) # => 20
In a proc, return behaves differently. Rather than return from the proc, it
returns from the scope where the proc itself was defined:
def another_double
p = Proc.new { return 10 }
result = p.call
return result * 2 # unreachable code!
end another_double # => 10
If you’re aware of this behavior, you can steer clear of buggy code like this:
def double(callable_object)
callable_object.call * 2
end
p = Proc.new { return 10 }
double(p) # => LocalJumpError
- When passing a wrong number of arguments
Procs are more forgiving than Lambdas.
So, summarizing, just use lambdas, they seem to be better in general.
Ruby metaprogramming II
class_eval and class_exec
Both evaluate a block in the context of a class. Just like instance_eval and instance_exec but with a class instead of an instance. class_exec can pass params whereas class_eval can't.
- def add_method_to(a_class)
a_class.class_eval do - def m; 'Hello!';
end
end - end
- add_method_to String
"abc".m # => "Hello!"
class_eval reopens the class and you can add or modify stuff within the class.
One difference between class and class_eval is that class opens a new scope -it's a scope gate- whereas class_eval has a flat scope.
Just as there is a class_eval and class_exec for classes, there is module_eval and module_exec for modules.
class instance variables vs class variables
class instance variables is this:
class MyClass
@var = 1
end
class variables are this:
class MyClass
@@var = 1
end
do not confuse any of this with instance variables:
class MyClass
def my_method
@var = 1
end
end
the difference is that class instance variables can't be accessed within instance methods and can't be accessed by subclasses of the class. Only in class methods.
class variables can be accessed in all those 3 places.
create a class without the keyword "class"
Class.new accepts and argument that is the superclass of the new class and a block to define what is inside the class:
c = Class.new(Array) do
def my_method
'hello'
end
end
Singleton methods
It's actually quite a complicated subject.
The basics is that you can add a method to a instance of a class on the fly:
paragraph = 'this is my string'
def paragraph.title
self.upcase == self
end
** other way to define a singleton method is "Object#define_singleton_method"
But it's actually more complicated than that, as you will see in following cards :)
When you ask an object for its class, Ruby doesn’t always tell you the whole truth. Instead of the class that you see, an object can have its own special, hidden class. That’s called the singleton class of the object. (You can also hear it called the metaclass or the eigenclass. However, “singleton class” is the official name.)
The way to find the singleton class is with the method "Object#singleton_class"
paragraph.singleton_class
Singleton classes can only have 1 instance -hence the name-. Plus it is where singleton methods live.
.class => gives you the class that the object is an instance of
.superclass => gives you the class that the class inherits from
So take a look to this diagram and I'll try to explain it as well as I can:
A singleton class of an object -an instance of a class- is a class only for that object. The superclass of that singleton class is the class of the instantiated object. Singleton methods of obj live in its singleton class.
BUT classes are objects too, so they can have their own singleton class, right? The singleton methods of a class are its class methods, and -obviously- live in its singleton class.
The singleton class of a class inherits from the singleton class of the superclass of the "parent" class -or "Object" is there is none, as Object is the superclass or the last parent class-. This is the reason why classes inherit classes methods from its superclass.
Because all this, now we know 3 different ways to create class methods:
def MyClass.a_class_method; end
class MyClass
def self.another_class_method; end
end
class MyClass class << self
def yet_another_class_method; end end
end
duck typing
In typed -or static- languages you say that an object has type T because it belongs to class T. In Ruby, as it's an non-typed -or dynamic- language, we don't care what class the object belongs to, but rather what the object responds to -if the object responds to this or that method-. So if it walks like a duck and quacks like a duck, must be a duck.
Class Macros
Class Macros are just class methods that are meant to use in a class definition
Ruby objects don't have attributes. If you want something that looks like an attribute, you have to define two Mimic Methods, a reader and a writer:
class MyClass
def my_attribute=(value)
@my_attribute = value
end
def my_attribute
@my_attribute
end
end
to avoid having to repeat this so many times we have the "attr_*" , attr_reader, attr_writer and attr_accessor
talking about modules and classes. Object#include and Object#extend
When you include a module in a class, only the "instance methods" -not the "class methods"- will be included in the class. So a way to do it is this one -this technique is called "Class extension"-:
Object#include
module MyModule
def self.module_method
'whatever'
end
end
class MyClass
class << self
include MyModule
end
end
you can do exactly the same with instances of classes. This other technique is called "Object extension":
module MyModule
def my_method
'hello'
end
end
obj = MyClass.new
class << obj
include MyModule
end
obj.my_method #hello
obj.singleton_methods # [:my_method]
this pattern is important enough to have its own method: Object#extend
module MyModule
def my_method; 'hello'; end
end
obj = Object.new
obj.extend MyModule
obj.my_method # 'hello'
class MyClass
extend MyModule
end
MyClass.my_method #"hello"
Module#alias_method -or the keyword alias, which is the same-
Changes the name of a method. The first parameter is the new name and the second the old name.
There is an interesting case:
class String
alias_method :real_length, :length
def length
real_length > 5 ? 'long' : 'short'
end
end
"War and Peace".length # => "long"
"War and Peace".real_length # => 13
The previous code redefines String#length, but the alias still refers to the original method. This gives you insight into how method redefinition works. When you redefine a method, you don’t really change the method. Instead, you define a new method and attach an existing name to that new method. You can still call the old version of the method as long as you have another name that’s still attached to it.
This leads to an interesting pattern:
- You alias a method.
- You redefine it.
- You call the old method from the new method.
Don't be shy, leave us a comment