Alessandro Dias

O Parâmetro & (Ampersand)

| Comments

Para que serve o & antes de um parâmetro em Ruby?

O & antes de algum parâmetro é utilizado basicamente de duas formas:

1 Na definição de um método:

1
2
3
def awesome_method &block
  yield if block_given?
end

2 Antes da chamada de um método:

1
  awesome_method &parameter

A primeira forma serve para deixar explícito que o seu método recebe um bloco.
Já a segunda forma funciona assim:
pega o parâmetro, chama o método to_proc e o passa como bloco, idependente de o método ter deixado o bloco explícito ou não. Então basta que o objeto passado responda a to_proc. Você ja deve ter visto parecido com isso:

1
  @users.map &:name

E uma coisa que sempre quis saber é “como um símbolo passado como parâmetro irá no exemplo acima irá retornar o nome de todos usuários?”.
Pesquisei um pouco, e achei como isso é feito Símbolos implementam o to_proc, tá e dai, como funciona esse to_proc`?

1
2
3
4
5
6
7
8
  class Symbol
    # ...
    def to_proc
      Proc.new do |obj, *args|
        obj.send self, *args
      end
    end
    # ...

Ainda um pouco confuso? Vamos destrinchar esse código então. vamos chamar o to_proc para o símbolo :name

1
2
3
4
5
  #...
  Proc.new do |obj, *args|
    obj.send :name, *args
  end
  #...

o map passa como parâmetro para o bloco um item da coleção, nesse caso um user, vamos adicioná-lo no código, e já que apenas um argumento é passado podemos remover o *args para simplificar.

1
2
3
4
5
  #...
  Proc.new do |user|
    user.send :name # => equivalente a user.name
  end
  #...

Mais simples agora? O método send envia para o user a mensagem :name, porque na verdade em Ruby metódos não são chamados, são mensagens enviadas ao objeto. o resultado final seria algo equivalente a isso:

1
2
3
  @users.map do |user|
    user.send :name # ou user.name
  end

Caso queira saber mais sobre o ampersand: Ruby ampersand parameter demystified

Classes São Objetos

| Comments

No último post falei sobre a construção de objetos em Ruby, nesse vamos falar sobre classes.
Uma caracteristica das classes é que elas também são objetos. Então você pode definir uma classe da forma mais comum.

1
2
3
4
class MyClass < SuperClass
  def my_method
  end
end

ou pode criar uma instância da classe [Class](http://www.ruby-doc.org/core-2.1.0/Class.html)

1
2
3
4
MyClass = Class.new(SuperClass) do
  def my_method
  end
end

As duas abordagens são válidas, a primeira é apenas um syntax sugar.
Outra coisa interessante sobre classes é que elas também são módulos

Construtores Em Ruby

| Comments

Você ja deve ter percebido que diferente do C#, Java e outras linguagens OO, em Ruby a criação de um novo objeto é feita pelo método new, mas como é feita a criação de um objeto em Ruby? Primeiro faremos uma lista do que precisamos para para instanciar um novo objeto.

  1. Alocar espaço para um objeto vazio
  2. Setar o estado inicial do objeto
  3. Retornar a referência para o objeto alocado

Após feita a lista vamos criar um objeto com um construtor personalizado com cada item da lista

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Book
  def self.custom_new(title, author)
    instance = allocate # Aloca espaço para um novo objeto vazio
    instance.custom_initialize(title, author)
    instance # Retorna a referência do objeto
  end

  def custom_initialize(title, author)
    @title, @author = title, author
  end
end

Book.custom_new('Title','Alessandro')
 => #<Book:0x00000003607978 @author="ale", @title="Title">

No caso acima criamos uma classe Book que possui um construtor personalizado, claro que poderíamos sobrescrever o método new e/ou initialize, mas fiz dessa forma apena para mostrar que é possivel fazer assim.

Caso queira saber sobre o método allocate aqui está a documentação

Cuidado Ao Chamar Métodos Da Super Classe

| Comments

Lendo um pouco sobre herança em Ruby descobri uma coisa interessante, ao usar o método super em algum método, pode ocorrer da erro caso o desenvolvedor não preste atenção. Quando você redefine um método e chama super sem parênteses e sem argumentos, o Ruby irá pegar os parâmetros passados para o método na subclasse passar de forma implícita para o método da superclasse, se os parâmetros do método na superclasse forem diferentes dos parâmetros no método redefinido irá resultar em erro. Exemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Dad
  def hi
    puts "Hello "
  end
end

Class Child < Dad
  def hi(name)
    super
    puts name
  end
end

Child.new('alessandro').hi

=> ArgumentError: wrong number of arguments (1 for 0)

Como resolver isso? Simples. Caso o método da superclasse não receba parâmetros chame o método super com parênteses super() Exemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Dad
  def hi
    puts "Hello "
  end
end

Class Child < Dad
  def hi(name)
    super()
    puts name
  end
end

Child.new('alessandro').hi
=> Hello
=> alessandro

O Que é O Splat Operator Do Ruby?

| Comments

Esses dias olhando código no GitHub vi um trecho como esse: def example(*b) O que esse código faz não vem ao caso, mas olhei o *b e pensei, oque que é esse * antes da variável me veio na hora ponteiro, mas ruby não possui ponteiros, então oque *b faz? pesquisei sobre e resumindo é a sintaxe para splat operator em ruby, ex:

1
2
3
4
5
6
7
8
def example(*b)
  puts b
end

example 1,2,3,4 # => 1 2 3 4

arr = [5,6,7,8]
example *arr # => 5 6 7 8

na ultima linha o *arr faz com que cada item do array seja passado como parâmetro para o método example, você pode usar dessa forma também.

1
2
3
4
5
6
def example2(v1,v2,3)
  puts v1,v2,v3
end

arr = [2, 3, 1]
example2 *arr # => 2 3 1

Nessa segunda abordagem você de ter cuidado para que a quantidade de elementos no array seja igual a quantidade de parâmetros requeridos pelo método.

Outra forma válida é o próximo exemplo:

1
2
3
4
5
6
7
def example3(v1,*v2)
  puts v2
end

arr = [2, 3, 1]

example3 *arr #=> 3 1

Nesse caso o primeiro valor da variavel arr é atribuido ao primeiro parâmetro e os demais ao parâmetro v2.