Conceitos na Prática: Programação funcional com Ruby

Se você acompanhou os posts anteriores deve ter visto um pouco sobre como funções podem ser utilizadas como qualquer outro tipo de variável usando lambdas, blocos e Procs.

Ruby é uma linguagem orientada a objetos. Tudo em ruby é um objeto ou pode ser representado como tal. O que faz ruby ter um toque de programação funcional é que funções são cidadãos de primeira classe [1].

Vamos explorar alguns exemplos que mostram a diferença de código entre programar puramente orientado a objetos e usar um pouco de programação funcional em Ruby.

Alguns exemplos simples

Vamos adicionar todos os números de um array e calcular o valor total. Em uma linguagem como Java, podemos fazer algo do tipo:

int soma = 0;
for(int i=0 ; i< array.length ; i++){
  soma += array[i];
}
return soma;

E no final teríamos, na variável soma, o valor total dos elementos do array. O exemplo acima ilustra bem um código orientado a objetos, estamos lendo o estado do objeto array, executando um certo processamento e armazenando o valor em um outro lugar.

Em ruby também é possível fazer esse mesmo laço:

soma = 0
for i in (0..array.length) do
  soma += array[i]
end
soma

Exatamente o mesmo laço. As mesmas observações são válidas. Não existe nenhum problema com esse código, a lógica implementada funciona perfeitamente. No entanto podemos melhorar bastante esse código aplicando algumas das funções que são padrões em ruby.

each

O each é a forma mais simples de iterar sobre um array (ou vários outros objetos). No entanto ele faz uso de funções como parâmetros, o que pode deixar o nosso código mais interessante.

soma = 0
array.each do |e|
  soma += e
end
soma

Não foi uma grande mudança, mas já dá pra notar que o código reduziu. E também conseguimos eliminar uma variável! Nesse caso não é lá grande coisa, mas num cenário real essa redução de complexidade ajuda bastante na manutenção do código.

Nós conseguimos nos livrar da variável que mantém o índice pois agora não precisamos acessar indiretamente (usando o índice) o elemento do array. Dentro do nosso lambda nós temos acesso direto ao elemento e simplesmente dizemos o que deve ser feito e não o como.

inject

Ainda temos uma variável temporária no nosso código que mantém o total da soma dos elementos do array. Podemos usar o inject para nos livrarmos dela:

array.inject(0) do |soma, e|
  soma += e
end

O que o inject faz é, você passa um lambda que aceita dois parâmetros. Um será o acumulador, que guarda o resultado das operações, e o outro será o elemento em si. No final de cada execução do bloco, o resultado do bloco será passado como o acumulador na execução seguinte. Como passamos o 0 como parâmetro, o primeiro valor do acumulador será 0 e em cada interação será o valor atual mais o elemento.

Não nos livramos realmente daquela variável soma, pois ela ainda está ali no nosso código, mas note que agora ela faz parte do lambda, então será gerenciada pelo próprio interpretador e não por nós. Uma coisa a menos para se preocupar.

Além disso, perceba que não precisamos mais dizer que estamos retornando a variável soma no final do método. Primeiro que agora ela pertence ao bloco que é passado ao inject e segundo que o inject já retorna pra gente o valor do acumulador.

De novo, em um caso real isso faz uma boa diferença na manutenção, mas para esse exemplo talvez não seja tão interessante.

O inject, assim como outras funções que aceitam funções como parâmetro pode ser simplificado utilizando um recurso do ruby que transforma um símbolo em uma função:

array.inject(&:+)

Agora sim o código está bem menor. Mas do ponto de vista da manutenção, talvez essa opção não seja muito boa, principalmente se sua equipe não tem muita experiência com ruby. Vamos tentar entender o que está acontecendo aqui com um outro exemplo de uma outra função.

select/reject

Ruby provê duas funções bem interessantes chamadas select e reject. Como o nome diz elas vão selecionar ou rejeitar os elementos do conjunto baseado em um determinado critério:

array = [1,2,3,4]
array.select do |e|
  e.even? # verifica se o número é par
end

O código acima vai retornar um novo array contendo apenas os elementos em que a chamada do método even? retorna verdadeiro. Podemos reduzir esse código utilizando a mesma técnica anterior:

array = [1,2,3,4]
array.select(&:even?)

O que acontece aqui é que estamos passando um símbolo (:even?) com o operador &, a implementação do select sabe que, quando receber um símbolo, deve ir em cada elemento do conjunto e executar o método cujo nome seja o mesmo do símbolo. Assim podemos diminuir nossa implementação e simplificar as coisas.

Com o reject seria a mesma coisa. Para obter apenas os números pares podemos rejeitar os números ímpares:

array = [1,2,3,4]
array.reject(&:odd?)

Voltando para o inject

O que o inject faz quando passamos o símbolo com o & é criar o acumulador com 0 e atribuir para ele o valor atual adicionando o resultado da chamada do método que passamos como símbolo.

No exemplo, como passamos &:+ será feita a adição do elemento ao acumulador. Seria mais ou menos como se estivéssemos executando esse código:

array.inject do |soma, e|
  soma += e
end

É como se, além de ter um valor default para a variável soma (0) o método também tivesse um bloco default que muda apenas qual método será chamado.

Um exemplo mais interessante é o cálculo do fatorial de um número, que pode ser feito em uma linha com a ajuda do inject:

def fatorial_de n
  (1..n).inject(&:*) || 1
end

O que o inject vai fazer nesse caso é jogar no acumular o valor atual dele multiplicado pelo valor do elemento do conjunto. Nesse caso o nosso conjunto vai de 1 até o valor do parâmetro n. O código “|| 1” está ali para evitar que quando um valor menor que 1 é passado seja retornado nil.

Próximos passos

As funções que foram mostradas aqui são apenas um pequeno conjunto. Existem várias outras que valem a pena serem estudadas como o map ou collect, que mapeia os valores do conjunto atual em outro conjunto. Além disso existem várias outras características funcionais que podem ajudar a simplificar o seu código.

Um conjunto de posts que tem uma visão mais aprofundada sobre técnicas de programação funcional aplicadas em Ruby é http://www.sitepoint.com/functional-programming-techniques-with-ruby-part-i/.

Referências

[1] http://en.wikipedia.org/wiki/First-class_function

Conceitos na prática: Ruby Procs

Nos posts anteriores, foi mostrado um pouco sobre lambdas e blocos em Ruby. No entanto existe mais uma outra estrutura que possui funcionalidade bem similar, mas com diferenças sutis e importantes. Nesse post vou falar um pouco sobre Proc e as as suas diferenças.

O que são Procs?

Se você já leu os posts anteriores sobre lambdas e blocos, o conceito de ter um pedaço de código que pode ser executado e passado como parâmetro para outros métodos não deve ser novidade. Bom, isso é o que um Proc é.

Veja no exemplo a seguir como é a sintaxe para criação de procs:

hello_proc = Proc.new do
  puts "Hello World"
end

hello_proc.call

Ou seja, é bem semelhante a lambdas. E para ficar mais parecido ainda, podemos usar apenas “proc” ao invés de “Proc.new”:

hello_proc = proc do
  puts "Hello World"
end

hello_proc.call

Além da sintaxe, as propriedades de blocos e lambdas são bem semelhantes também, já que um lambda é uma versão especial de um bloco.

hello_lambda = lambda {}
hello_proc = proc {}

hello_proc.class #=> Proc
hello_lambda.class #=> Proc

Então todos aqueles exemplos que vimos no post anterior sobre conversões entre blocos e lambdas também se aplica a Procs. Dito isto, vou tentar focar mais nas diferenças entre blocos, lambdas e procs [1].

Blocos vs. Procs/Lambdas

A primeira grande diferença é que apenas um bloco pode ser passado para um método.

Nos exemplos do post sobre blocos nós vimos que não é necessário explicitar que o bloco será passado, a menos que você precise referenciá-lo dentro do método (para passar para outro método, por exemplo). No entanto não é possível passar mais de um bloco para um mesmo método. Precisamos usar lambdas ou procs para alcanças este objetivo.

Uma outra diferença é a sintaxe. Blocos não podem ser executados chamando o método “call”, mas sim utilizando a palavra chave yield. Tudo bem, essa não é uma grande diferença.

Procs vs. Lambdas

Agora as diferenças começam a ficar interessantes!

Vamos tentar utilizar alguns exemplos, observe os dois pedaços de código abaixo, um usa lambda e outro usa proc:

hello_proc = proc do |msg|
  puts msg
end

hello_proc.call
hello_lambda = lambda do |msg|
  puts msg
end

hello_lambda.call

Qual deve ser o resultado de cada pedaço de código? O mesmo? Note que agora o proc e o lambda estão recebendo um argumento que será exibido no terminal.

Se você tentar executar o primeiro código (com proc) verá que a saída é uma linha vazia, pois o parâmetro “msg” não foi passado. Assim, podemos ver que procs automaticamente atribuem “nil” para variáves cujo valor não tenha sido definido.

Já o segundo código (com lambda) lançará uma exceção “ArgumentError: wrong number of arguments (0 for 1)”! Ou seja, os argumentos são necessários para a execução de um lambda. No entanto, podemos contornar esse problema utilizando valores default para o lambda, como mostra o exemplo a seguir

hello_lambda = lambda do |msg = nil|
  puts msg
end

hello_lambda.call

Outra grande diferença está em como o “return” é tratado. O “return” em um lambda retorna o fluxo de execução para o método onde o lambda foi chamado:

hello_lambda = lambda do |msg|
  return msg
end

def run_hello_block(block)
  puts "Preparando para executar lambda..."
  puts "Lambda em execução: #{block.call("Hello World")}"
  return "Lambda executado com sucesso!"
end

puts run_hello_block(hello_lambda)

Nesse caso, tudo funciona como esperado e todas as mensagens são exibidas. No entanto, o mesmo pedaço de código, agora utilizando proc, tem um resultado bem diferente:

hello_proc = proc do |msg|
  return msg
end

def run_hello_block(block)
  puts "Preparando para executar proc..."
  puts "Proc em execução: #{block.call("Hello World")}"
  return "Proc executado com sucesso!"
end

puts run_hello_block(hello_proc)

Ao tentar executar este código veremos uma exceção “LocalJumpError: unexpected return”. Esta exceção é lançada quando tentamos executar um return dentro de um argumento [2], exceto quando o argumento é um lambda.

Uma maneira de resolver o problema seria definir o proc dentro do método, assim ele não seria mais um parâmetro e poderia retornar algo:

def run_hello_block
  hello_proc = proc do |msg|
    return msg
  end

  puts "Preparando para executar proc..."
  puts "Proc em execução: #{hello_proc.call("Hello World")}"
  return "Proc executado com sucesso!"
end

puts run_hello_block

Com o código acima podemos ver que é possível utilizar o return dentro de um proc. O problema é como o escopo é gerenciado. O proc foi criado fora do escopo do método “run_hello_block”, assim quando ele tenta retornar, e o escopo mudou, a exceção é lançada.

Outra solução bem simples é não utilizar a palavra-chave “return”, já que em ruby a última linha sempre é retornada:

hello_proc = proc do |msg|
  msg
end

def run_hello_block(block)
  puts "Preparando para executar proc..."
  puts "Proc em execução: #{block.call("Hello World")}"
  return "Proc executado com sucesso!"
end

puts run_hello_block(hello_proc)

Existem várias outras diferenças entre Blocos, Procs e Lambdas que não vou falar aqui, mas essas são as principais. Espero que os exemplos possam lhe ajudar a entender melhor essas diferenças.

Referências

[1] Ahsan Sharafuddin: Closures in Ruby, http://technicalecstasy.net/closures-in-ruby-blocks-procs-and-lambdas/

[2] Robert Sosinski: Understanding Ruby Blocks, Procs and Lambdas http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/

Conceitos na prática: Ruby lambda

Olá! Neste post vou falar sobre lambdas em ruby e mostrar alguns exemplos de sua utilidade. No post anterior, falamos um pouco sobre como blocos podem ser úteis para generalizar a implementação de um código. Hoje vamos brincar um pouco com lambdas e como utilizá-las como blocos.

O que são Lambdas

Em ruby é possível criar métodos. Um método contém um conjunto de instruções que serão executadas quando este for invocado. Nesse sentido, lambdas são bem parecidos com métodos. Vamos ver um pouco da sintaxe:

puts_lambda = lambda do
  puts "lambda"
end

puts_lambda.call

Uma primeira observação é que o lambda foi atribuído para uma variável. Isso mostra que podemos armazenar os lambdas em variáveis para executar depois.

A sintaxe para criação de um lambda é bem simples, basta chamar “lambda” e passar um bloco qualquer. Mais tarde, para executar o bloco, basta chamar o método “call”.

Nas verões mais recentes de ruby, podemos criar lambdas utilizando o operador “->”, que é menor do “lambda” mas que não tão legível. O código a seguir é equivalente ao anterior:

puts_lambda = -> do
  puts "lambda"
end

puts_lambda.call

Lambdas e argumentos

Lambdas também podem receber argumentos, assim como métodos. A diferença é que precisamos adicionar argumentos ao bloco, semelhante a como fizemos com o método “for_each” no post anterior.

puts_lambda = lambda do |msg|
  puts "#{msg}"
end

puts_lambda.call "Lambda!"

Agora qualquer objeto que for passado ao lambda será exibido no console. Se tentarmos executar o lambda sem parâmetros, uma exceção “ArgumentError: wrong number of arguments (0 for 1)” será lançada.

Assim como métodos também podemos atribuir um valor default para os parâmetros que devem ser opcionais.

puts_lambda = lambda do |msg = "lambda"|
  puts "#{msg}"
end

puts_lambda.call
puts_lambda.call "Lambda!"

Lambdas como blocos

Agora que conhecemos a sintaxe do lambda, vamos brincar um pouco. No post anterior vimos que é possível passar um bloco para o método “each” de um array. Pois bem, como lambdas armazenam um bloco, também podemos passar lambdas como blocos:

puts_lambda = lambda do |msg|
  puts "#{msg}"
end

[1,2,3,4,5].each puts_lambda #=>ArgumentError: wrong number of arguments (1 for 0)

Ao executar o código acima, uma exceção será lançada. Pela mensagem podemos ver que o método each não aceita argumentos, pois passamos 1 argumento enquanto nenhum era esperado. Então como podemos passar um bloco como argumento para o “each” se ele não aceita nenhum argumento?

Lembra que no post anterior vimos que não é necessário explicitar que um método em ruby recebe um bloco como argumento? Que basta passar o bloco e executar um “yield” para que o bloco seja chamado?

Pois bem, ao criar um lambda usando o irb, podemos ver que o terminal mostra “Proc:0x007ff52397a8b8@(irb):6 (lambda)” como retorno. Ou seja, lambdas são na verdade objetos da classe Proc! Se tentarmos chamar o método “class” em um objeto lambda temos como retorno “Proc”.

No entanto existem algumas pequenas diferenças entre Proc e lambdas que serão faladas em posts futuros. Por hora precisamos passar o lambda como bloco para o método “each” de um array.

A maneira como podemos converter lambdas (e procs) em blocos é utilizando o caractere “&” logo atrás do objeto. Como no seguinte código:

puts_lambda = lambda do |msg|
  puts "#{msg}"
end

[1,2,3,4,5].each &puts_lambda

Operado Ampersand ou “&”

O operador “&” vai converter objetos proc em bloco ou bloco em proc. A útilidade de converter um bloco em proc é quando você tem um método, que deve receber um bloco como parâmetro e precisa passar esse bloco para outro método.

Imagine que o método “for_each” do post anterior agora vai simplesmente passar o bloco para o método each, ao invés de chamar o yield:

def for_each(array, &block)
  array.each &block
end
 
for_each [1,2,3,4,5] do |e|
  puts e
end

No parâmetro, recebemos o bloco com o operador “&”, dizendo que o bloco deve ser convertido em proc. Depois, com o objeto block em mãos, passamos para o método “each” também utilizando o operador “&”, pois queremos que o objeto proc seja convertido em bloco para o método each.

Agora, se quisermos que o método “for_each” receba um lambda como argumento, não é necessário o primeiro “&” (na lista de argumentos). Como lambda já são procs, não precisamos converter nada e a implementação seria a seguinte:

puts_lambda = lambda do |msg|
  puts "#{msg}"
end

def for_each(array, lambda)
  array.each &lambda
end
 
for_each [1,2,3,4,5], puts_lambda

Apesar de parecer complicado, na prática lambdas ajudam bastante a generalizar código para que fique mais fácil reutilizá-lo. Vale a pena quebrar um pouco a cabeça para entender lambdas.

No próximo post vou falar um pouco sobre Procs e mostrar algumas diferenças dos lambdas. Até lá!

Referências

[1] Ahsan Sharafuddin: Closures in Ruby, http://technicalecstasy.net/closures-in-ruby-blocks-procs-and-lambdas/

Conceitos na prática: Ruby block (yield)

Olá! Esta será uma série de posts que vão apresentar alguns conceitos bem legais da linguagem Ruby e situações onde poderemos aplicá-los para obter um código bem melhor. E o primeiro post será sobre blocos em ruby e a famosa palavra chave yield.

O que são blocos

Em Ruby existem várias definições diferentes de coisas bem semelhantes. Blocos, Lambda e Procs são referenciados de maneira geral como “closure”. Além de dar nome a uma linguagem, closure é uma funcionalidade que permite escrever um pedaço de código que pode ser [1]:

  • Utilizado como um valor (podendo ser atribuído, passado como parâmetro, etc.)
  • Pode ser executado em qualquer lugar
  • Referenciam variáveis no contexto onde são criados

Nos próximos posts eu falo um pouco mais sobre os benefícios da utilização de closure, agora vamos focar na parte prática da coisa.

Passando blocos como argumento

Blocos em ruby são pedaços de código que estão entre “do end” ou entre “{}”. Veja um exemplo:

array.each do |e|
  puts e
end

O código “puts e” que está entre o “do end” é um bloco que foi passado como parâmetro para o each.

Recebendo blocos como argumento

Para exemplificar, vamos tentar criar o nosso próprio método “for_each”, que vai receber um array um bloco que será executado em cada um dos elementos do array:

def for_each(array)
  array.each do |e|
    yield e
  end
end

for_each [1,2,3,4,5] do |e|
  puts e
end

A saída desse código serão os números dentro do array. O que o “for_each” fez foi executar um yield em cada elemento do array. O yield para a execução do método (no caso “for_each”) e passa o controle para o bloco que foi passado como parâmetro pelo método.

Generalização com blocos

Vamos para mais um exemplo. Suponha que você precisa implementar uma lógica para tentar executar um mesmo método (ou qualquer pedaço de código). Digamos que você precise se conectar em um host, mas caso a conexão não possa ser estabelecida vamos tentar 3 vezes.

Ruby já tem um “retry” que pode ser utilizado dentro de um “begin/rescue”, mas não vamos utilizá-lo neste exemplo. Um código para resolver esse problema seria:

retry_count = 0
successful = false
while(!successful or (retry_count < 3))
  begin
    page = Nokogiri::HTML(open("brizeno.wordpress.com"))
    successful = true
  rescue Errno::ECONNRESET => e
    retry_count += 1
  end
end

Com esse código podemos tentar a conexão por 3 vezes. Agora, caso seja necessário aplicar essa lógica em vários lugares, será necessário quebrar um pouco mais a cabeça e garantir que o código é reutilizável.

Graças aos blocos, podemos extrair esse comportamento para um método, que vai receber como parâmetro o número de tentativas a serem realizadas e o que deve ser feito. Seria algo do tipo:

def retry_this times
  retry_count = 0
  successful = false
  while(!successful or (retry_count < times))
    begin
      yield
      successful = true
    rescue Errno::ECONNRESET => e
      retry_count += 1
    end
  end
  raise "Couldn't establish connection" unless sucessful
end

A utilização desse método seria:

retry_this 3 do
  page = Nokogiri::HTML(open("brizeno.wordpress.com"))
end

Um pouco mais sobre blocos

Repare que não é necessário explicitar que o bloco é um parâmetro do método “retry_this”. Em ruby, todo método pode executar um yield e passar o controle para um bloco, caso ele exista. E o que acontece quando um bloco não é passado?

No exemplo anterior, uma exceção seria lançada “no block given (yield) (LocalJumpError)”. Podemos resolver esse problema facilmente, basta adicionar uma outra palavra chave: “block_given?”.

def retry_this times
...
  yield if block_given?
...
end

Agora o bloco só será chamado se existir. Essa é uma boa prática para desenvolver métodos que precisam de blocos, a não ser que o objetivo realmente seja avisar que o bloco não foi passado.

Uma outra curiosidade sobre o blocos é que, no primeiro exemplo, nós fizemos uma chamada ao yield passando um parâmetro. Um bloco pode receber parâmetros utilizando os caracteres “| |”. Ao chamar o “each” nós precisamos receber um argumento: o elemento do array que está sendo iterado atualmente.

Para iterar sobre uma hash por exemplo, precisamos de dois argumentos: a chave e o valor.

my_hash.each do |key, value|
  puts "#{key} => #{value}"
end

O yield dentro do metodo each de hashes provavelmente se parece com o seguinte:

yield key, value if block_given?

Ruby possui vários métodos para iterar em um conjunto de valores, todos eles se utilizam de blocos. O “inject” por exemplo utiliza dois argumentos, um contador e o elemento do conjunto, e adiciona o retorno do bloco ao contador:

acc = [1,2,3,4,5].inject do |accumulator, number|
  accumulator +  number
end
puts acc # => 15 (1 + 2 + 3 + 4 + 5)

Martin Fowler escreveu um artigo [2] bem interessante (na categoria old but gold), sobre a facilidade que os método de iteração proporcionam e os benefícios de se utilizar lambdas. E lambdas serão o tema do próximo post!

Referências

[1] Ahsan Sharafuddin: Closures in Ruby, http://technicalecstasy.net/closures-in-ruby-blocks-procs-and-lambdas/
[2] Martin Fowler: Lambdas, http://martinfowler.com/bliki/Lambda.html

Exemplos de metaprog (Java e Ruby)

Quem desenvolve com Ruby pode até estar acostumado a fazer coisas como utilizar os attr_accessor, attr_writer, etc. que são bons exemplos de metaprog. Mas como é que isso funciona? Vamos ver alguns exemplos em Java e Ruby e mostrar quais são as diferenças entre essas duas linguagens.

class Attrs
  attr_accessor :accessor
  attr_reader :reader
  attr_writer :writer
  attr :attr
end

Esse exemplo mostra os quatro métodos que criam variáveis de instância e métodos para acessar essas variáveis. Lembrando que eles são métodos, ou seja, aquelas linhas de código estão na verdade chamando um método. Esse método, definido dentro da classe Module do Ruby [1], cria uma variável de instância com o nome que você passa como um símbolo, por isso precisamos passar algo da forma “:accessor”, e alguns métodos para oferecer acesso a essa variável de instância utilizando a metaprogramação.

Considerando a classe acima podemos chamar, dentro do escopo dela, qualquer um dos atributos como “@accessor”. Para instâncias dessa classe podemos chamar os métodos “accessor” que retorna o valor daquela variável e “accessor=” que altera o valor da variável.

Podemos então utilizar o seguinte método, que, através de reflexão, recebe um objeto e um atributo, e mostra a acessibilidade do atributo no método.

def get_accessibility(object, attribute)
  puts &quot;Can read? #{object.respond_to?(&quot;#{attribute}&quot;.to_sym)}&quot;
  puts &quot;Can write? #{object.respond_to?(&quot;#{attribute}=&quot;.to_sym)}&quot;
end

Esse método utiliza os método “respond_to?” que pode ser chamado em um objeto qualquer, toma como parametro um símbolo e retorna se o objeto tem ou não aquele método. No exemplo estamos construindo o símbolo utilizando o nome do atributo, para o método de leitura apenas precisamos chamar o nome do atributo, para o método de escrita precisamos chamar com um “=” na frente. O resultado da chamada do método nos atributos de um objeto da classe acima mostra a visibilidade dos atributos.

=== Accessor ===
Can read? true
Can write? true
=== Reader ===
Can read? true
Can write? false
=== Writter ===
Can read? false
Can write? true
=== Attribute ===
Can read? true
Can write? false

Vale lembrar que esses métodos são bem diferentes dos modificadores de acesso do Java, pois os modificadores apenas configuram o objeto para não permitir acesso as variáveis. Os métodos attrs do Ruby definem métodos e a variável de instância, e nada lhe impede de fazer qualquer modificação ou escrever métodos de leitura/escrita. No Java também não é tão complicado modificar a acessibilidade dos atributos. Os exemplos a seguir mostram como modificar o acesso de variáveis com Java e Ruby.

Considere a classe java a seguir, com um método privado e uma variável privada.

public class HelloYou {
  private String name;

  public HelloYou(String name){
    this.name = name;
  }

  private String sayHello() {
    return &quot;Hello &quot; + name + &quot;!&quot;;
  }
}

O código a seguir mostra como acessar o método privado, e como ler e escrever na classe privada, utilizando a api de reflection do java. Os objetos Method e Field permitem acessar métodos e atributos de uma classe.

import java.util.Scanner;
import java.lang.reflect.*;

public class Main{
  public static void main(String arg[]){
    Scanner scanner = new Scanner(System.in);
    System.out.print(&quot;Quem é você? &quot;);
    String name = scanner.nextLine();
    Method privateMethod;
    Field privateField;
    HelloYou hello = new HelloYou(name);
    try {
      privateMethod = hello.getClass().getDeclaredMethod(&quot;sayHello&quot;);
      privateMethod.setAccessible(true);
      System.out.println(privateMethod.invoke(hello));
      privateField = hello.getClass().getDeclaredField(&quot;name&quot;);
      privateField.setAccessible(true);
      privateField.set(hello, ((String)privateField.get(hello)).toUpperCase());
      System.out.println(privateMethod.invoke(hello));
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
}

A ideia é pegar um objeto java.lang.reflect.Method e atribuir a ele um dos métodos do objeto que você quer acessar, utilizan o “getClass()” para acessar a classe Class do seu objeto. Uma vez que temos a classe de um objeto, podemos acessar os métodos através do nome do método e os tipos dos parâmetros, bem como os atributos através do nome.

Chamando o método “setAccessible()” nós podemos modificar a visibilidade dos métodos e atributos. Em seguida basta chamar “invoke()” para chamar o método no objeto passado como parâmetro. Para os atributos apenas chamamos “set()” e “get()” de um objeto java.lang.reflect.Field para manipular os valores dos atributos no objeto.

No java, as possibilidades de metaprogramação são bem reduzidas pois não permitem adicionar métodos na classe ou alterar os métodos em tempo de execução. Mas essa é a filosofia da linguagem, reduzir a possibilidade de erros dos programadores através da remoção de poder da linguagem. Mas mesmo assim, alguém com conhecimento na linguagem pode ignorar os modificadores de visibilidade caso seja necessário.

O mesmo exemplo acima em poderia ser algo assim se fosse escrito em Ruby:

class HelloYou
  attr_reader :name

  def initialize(name)
    @name = name
  end

  private
  def say_hello
    &quot;Hello #{@name}&quot;
  end
end

puts &quot;Quem é você?&quot;
name = gets.chomp

hello = HelloYou.new(name)
puts hello.send(:say_hello)
def hello.name=(name)
  @name = name
end
hello.name = name.upcase
puts hello.send(:say_hello)

Para acessar métodos privados, a única coisa que precisamos é chamar o método “send()” e passar o símbolo que representa o método. Já para permitir escrever em um atributo sem um método de escrita, basta definir o método que dá acesso a ele. No exemplo acima, o método foi definido diretamente no objeto, e não na classe.

Ou seja, com um pouco de conhecimento, podemos simplesmente ignorar as regras de visibilidade e acesso de atributos e métodos. Definir um método como privado então serve apenas como documentação do código, ou seja, apenas diz que aquele método não depende de nada fora do objeto e, da mesma forma, não altera nada que esteja fora desse objeto.

Em Ruby a metaprogramação é ainda mais poderosa pois podemos adicionar, modificar, remover, executar, etc. qualquer código em tempo de execução. No entanto esse “poder” exige cuidado para evitar complicações de manutenção de código. Um dos pontos negativos da forte utilização de metaprogramação em Ruby é que, quando se abusa dela, fica muito difícil debugar e manter o código, pois você pode ver no stack trace que ocorreu um erro em um determinado método e, ao procurar por esse método, não encontrar nada.

Na verdade, apesar de ser interessante, dificilmente você vai precisar alterar a visibilidade de métodos e atributos. Geralmente faz mais sentido repensar no design e alterar a visibilidade pelos meios convencionais do que oferecer isso em algum método obscuro. Uma das utilidades é para testar métodos privados, sem entrar na questão de testar ou não métodos privados.

Nos próximos posts vou procurar mais alguns exemplos legais e que fazem mais sentido do que só testar métodos privados. Para conhecer um pouco mais sobre metaprog e reflection em Ruby, vale a pena dar uma olhada no post sobre Reflexão em Ruby [2] e Metaprogramação em Ruby [3].

Referências:

[1] http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr
[2] https://brizeno.wordpress.com/2012/03/25/reflexao-em-ruby/
[3] https://brizeno.wordpress.com/2012/03/27/metaprogramacao-em-ruby/

Metaprogramação em Ruby

Continuando a série de posts sobre a linguagem Ruby, vamos ver como funciona a metaprogramação de uma maneira bem prática. Se você perdeu os outros posts dê uma conferida aqui: Classes, escopos e convenções em Ruby e Reflexão em Ruby.

Adicionando código em tempo de execução

Vamos criar duas classes, Pai e Filho. A classe filho não possui nenhum método, já a classe Pai possui um método que vai ensinar o filho a andar de bicicleta:

1.9.3p125 :001 > class Filho
1.9.3p125 :002?> end
 => nil 
1.9.3p125 :003 > class Pai
1.9.3p125 :004?>   def ensina(filho)
1.9.3p125 :005?>     def filho.andar_de_bicicleta
1.9.3p125 :006?>       puts "sei andar de bicicleta"
1.9.3p125 :007?>     end
1.9.3p125 :008?>   end
1.9.3p125 :009?> end
 => nil

Agora vamos instanciar um filho e verificar se ele sabe andar de bicicleta:

1.9.3p125 :010 > filho = Filho.new
 => #<Filho:0x9e37610> 
1.9.3p125 :011 > filho.respond_to? :andar_de_bicicleta
 => false

Agora vamos fazer com que o pai ensine ao filho andar de bicicleta:

1.9.3p125 :012 > pai = Pai.new
 => #<Pai:0x9e42858> 
1.9.3p125 :013 > pai.ensina filho
 => nil 
1.9.3p125 :014 > filho.respond_to? :andar_de_bicicleta
 => true 
1.9.3p125 :015 > filho.andar_de_bicicleta
sei andar de bicicleta
 => nil

Maravilha! Adicionamos um método em um objeto em tempo de execução!

Alterando código em tempo de execução

Mas, e se o objeto já possuir um método? Por exemplo, vamos supor que o filho já possua um método “andar_de_bicicleta”?

1.9.3p125 :001 > class Filho
1.9.3p125 :002?>   def andar_de_bicicleta
1.9.3p125 :003?>     puts "nao sei andar de bicicleta"
1.9.3p125 :004?>   end
1.9.3p125 :005?> end
 => nil 
1.9.3p125 :006 > class Pai
1.9.3p125 :007?>   def ensina(filho)
1.9.3p125 :008?>     def filho.andar_de_bicicleta
1.9.3p125 :009?>       puts "sei andar de bicicleta"
1.9.3p125 :010?>     end
1.9.3p125 :011?>   end
1.9.3p125 :012?> end
 => nil 
1.9.3p125 :013 > filho = Filho.new
 => #<Filho:0x9f36ca0> 
1.9.3p125 :014 > pai = Pai.new
 => #<Pai:0x9ef5bb0> 
1.9.3p125 :015 > filho.andar_de_bicicleta
nao sei andar de bicicleta
 => nil

Então se agora o pai ensinar ao filho, o que acontece?

1.9.3p125 :016 > pai.ensina filho
 => nil 
1.9.3p125 :017 > filho.andar_de_bicicleta
sei andar de bicicleta
 => nil

Como era de se esperar, o método foi alterado. Como definido lá no começo, metaprogramação é a capacidade de adicionar ou modificar o código em tempo de execução. Pelo exemplo ficou bem claro não? Vale notar também que apenas aquela instância da classe “Filho” foi alterada. Se forem instanciados outros filhos, nenhum deles vai ter as alterações feitas pelo pai, a menos que o pai ensino o filho.

Acessores ruby

Certo, então vamos ver um exemplo bem mais útil, os acessores ruby. Vimos antes os escopos das variáveis em Ruby, um destes escopos é o das variáveis de instância, que começam com ‘@’. Como é possível então ler/alterar os valores destas variáveis? No Java existem os famosos getters e setters, que são métodos para ler/alterar valores das variáveis. Em ruby é possível fazer da mesma maneira, veja o código a seguir:

1.9.3p125 :001 > class Produto
1.9.3p125 :002?>     def initialize(nome, preco)
1.9.3p125 :003?>         @nome = nome
1.9.3p125 :004?>         @preco = preco
1.9.3p125 :005?>       end
1.9.3p125 :006?>     
1.9.3p125 :007 >       def get_nome
1.9.3p125 :008?>         @nome
1.9.3p125 :009?>       end
1.9.3p125 :010?>     
1.9.3p125 :011 >       def set_nome(nome)
1.9.3p125 :012?>         @nome = nome
1.9.3p125 :013?>       end
1.9.3p125 :014?>   end
 => nil 
1.9.3p125 :015 > 
1.9.3p125 :016 >   refri = Produto.new("refri", 2.50)
 => #<Produto:0x8bfd670 @nome="refri", @preco=2.5> 
1.9.3p125 :017 > refri.get_nome
 => "refri" 
1.9.3p125 :018 > refri.set_nome "refrigerante"
 => "refrigerante" 
1.9.3p125 :019 > refri.get_nome
 => "refrigerante"

Mas a linguagem ruby também possui uma característica bem forte, que é buscar o máximo de legibilidade. Então, que tal se no lugar de get_nome e set_nome nós fizermos o seguinte:

#...
1.9.3p125 :007 >       def nome
1.9.3p125 :008?>         @nome
1.9.3p125 :009?>       end
1.9.3p125 :010?>     
1.9.3p125 :011 >       def nome=(nome)
1.9.3p125 :012?>         @nome = nome
1.9.3p125 :013?>       end
#...

Agora poderíamos utilizar o código de uma maneira bem mais legível:

#...
1.9.3p125 :016 >   refri = Produto.new("refri", 2.50)
 => #<Produto:0x8bfd670 @nome="refri", @preco=2.5> 
1.9.3p125 :017 > refri.nome
 => "refri" 
1.9.3p125 :018 > refri.nome = "refrigerante"
 => "refrigerante" 
1.9.3p125 :019 > refri.nome
 => "refrigerante"

Legal não? Tá, mas onde entra a metaprogramação na história? Bom, isto que acabamos de fazer foi criar acessores para o atributo “nome” da classe “Produto”. Em ruby podemos fazer isto de maneira bem mais simples, utilizando os “attr_”. Vejam o código primeiro:

1.9.3p125 :001 > class Produto
1.9.3p125 :002?>   
1.9.3p125 :003 >       attr_accessor :nome
1.9.3p125 :004?>     
1.9.3p125 :005 >       def initialize(nome, preco)
1.9.3p125 :006?>         @nome = nome
1.9.3p125 :007?>         @preco = preco
1.9.3p125 :008?>       end
1.9.3p125 :009?>   end
 => nil 
1.9.3p125 :010 > 
1.9.3p125 :011 >   refri = Produto.new("refri", 2.50)
 => #<Produto:0x94474ac @nome="refri", @preco=2.5> 
1.9.3p125 :012 > refri.nome
 => "refri" 
1.9.3p125 :013 > refri.nome = "refrigerante"
 => "refrigerante" 
1.9.3p125 :014 > refri.nome
 => "refrigerante"

Então o que é aquele “attr_accessor”? Nada mais é do que um método, que recebe como parâmetros um conjunto de símbolos, que representam variáveis de instância, e vão adicionar na sua classe os getters e setters daqueles atributos. Veja que passamos como parâmetro “:nome”, então o método vai criar “def nome” e “def nome=(nome)” para que possamos acessar este atributo de fora da classe. Sensacional não? Conseguimos diminuir consideravelmente a quantidade de código para escrever e ler.

Além do “attr_accessor” existem o “attr_reader”, que cria apenas o método para ler o atributo e o “attr_writer” que cria apenas o método para alterar o atributo. Este tipo de abordagem é muito utilizada (sério) no ruby, se quer um exemplo procure como o Rails faz para criar relacionamentos entre entidades. E não só o Rails, várias outras gems utilizam esta prática para reduzir o código.

Reflexão em Ruby

Neste post, vamos analisar como é possível utilizar reflexão na linguagem ruby.

A Linguagem Ruby

Ruby é classificado como Dinamicamente Tipado, ou seja, os objetos ruby possuem tipos, por exemplo um objeto Carro é um Carro, no entanto as variáveis, que por sua vez referenciam estes objetos, não possuem tipo, então o código abaixo funcionaria sem nenhum problema:

carro = Carro.new
carro = 2
carro = "Carro"
carro = nil

Outro aspecto importante sobre ruby é que a linguagem é dinâmica, e por dinâmica entenda que é possível adicionar ou modificar código, em tempo de execução (metaprogramação) e obter informações sobre os próprios objetos, também em tempo de execução (reflexão).

Reflexão

Já vimos, meio que sem querer, como funciona a Reflexão em ruby, no post anterior. Lembra que fizemos “self.class”? Este é um método implementado na classe Object, que retorna qual a classe do objeto que variável está referenciando. No ruby, assim como no Java, todas as classes herdam de Object, então todas as classe possuem este método. Vamos analisar alguns exemplos deste método:

1.9.3p125 :011 > 1.class
 => Fixnum 
1.9.3p125 :012 > "String".class
 => String

Objetos e Mensagens

Calma ai, como é que 1 tem o método “class”? 1 não é um número? Sim, e por isso ele É um objeto da classe Fixnum, que por sua vez, herda da classe object. Ou seja, tudo é um objeto. E toda chamada de método é na verdade um envio de mensagem! Vamos ver na prática como funciona:

1.9.3p125 :001 > 1.class
 => Fixnum 
1.9.3p125 :002 > 1.send(:class)
 => Fixnum 

O método “send” envia uma mensagem ao objeto. O parâmetro “:class” é um símbolo ruby (todo símbolo começa com ‘:’ na frente). Pense num símbolo como sendo uma string constante que o valor é ela mesma, então :class é uma string constante com valor “class”. Ao executar o método “send”, enviamos um símbolo e o método vai tentar executar um método que possua o mesmo nome daquele símbolo (por isso os métodos também precisam seguir a convenção snake_case). Então quando executamos “1.class” é o mesmo que estarmos enviando uma mensagem ao objeto “1” para executar o método “class”.

Veja mais alguns exemplos, caso ainda tenha dúvidas:

1.9.3p125 :001 > 1 + 3
 => 4 
1.9.3p125 :002 > 1.send(:+, 3)
 => 4 
1.9.3p125 :003 > "hello world".upcase
 => "HELLO WORLD" 
1.9.3p125 :004 > "hello world".send(:upcase)
 => "HELLO WORLD"

Um pouco mais sobre Reflexão

O método “respond_to?” verifica se aquele objeto pode responder a uma determinada mensagem. Por exemplo:

1.9.3p125 :006 > "hello world".respond_to?(:upcase)
 => true

Para finalizar a parte de reflexão, vamos ver um método que mostra todos os métodos, ou todas as mensagens, que um objeto pode executar, é o “methods”:

1.9.3p125 :007 > "hello world".methods
 => [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, :%, :[], :[]=, :insert, :length, :size, :bytesize, :empty?, :=~, :match, :succ, :succ!, :next, :next!, :upto, :index, :rindex, :replace, :clear, :chr, :getbyte, :setbyte, :byteslice, :to_i, :to_f, :to_s, :to_str, :inspect, :dump, :upcase, :downcase, :capitalize, :swapcase, :upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split, :lines, :bytes, :chars, :codepoints, :reverse, :reverse!, :concat, :<<, :prepend, :crypt, :intern, :to_sym, :ord, :include?, :start_with?, :end_with?, :scan, :ljust, :rjust, :center, :sub, :gsub, :chop, :chomp, :strip, :lstrip, :rstrip, :sub!, :gsub!, :chop!, :chomp!, :strip!, :lstrip!, :rstrip!, :tr, :tr_s, :delete, :squeeze, :count, :tr!, :tr_s!, :delete!, :squeeze!, :each_line, :each_byte, :each_char, :each_codepoint, :sum, :slice, :slice!, :partition, :rpartition, :encoding, :force_encoding, :valid_encoding?, :ascii_only?, :unpack, :encode, :encode!, :to_r, :to_c, :>, :>=, :<, :<=, :between?, :nil?, :!~, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]

Meio confuso né? Bom, na verdade ele retorna um array contendo todos os métodos que um determinado objeto possui, por isso que fica bem bagunçada a saída.

Por hoje é só, espero que a OO do ruby tenha ficado um pouco mais clara. No próximo post vamos ver a metaprogramação. Se gostou do post compartilhe com seus amigos e colegas, senão, comente o que pode ser melhorado. Encontrou algum erro no código? Comente também. Possui alguma outra opinião ou alguma informação adicional? Comenta ai! 😀