Olá pessoal,
Hoje será um pouco diferente, pois o texto não foi escrito por mim e sim pelo @wycats (Yehuda Katz) do blog “Katz Got Your Tongue?”. Ele é um membro da equipe do Ruby on Rails e desenvolvedor líder do projeto Merb. Ele é um membro do time principal do jQuery e um grande contribuidor para o DataMapper. Ele contribui para muitos projetos de código aberto, como Rubinius e Johnson, e trabalha em alguns que ele mesmo criou, como Thor. Yehuda gentilmente concedeu que eu traduzisse seu post e o colocasse no blog e, além dele, tive também a ajuda do grande @elomar, que fez a revisão completa da tradução com seus ajustes essenciais. Então, aí vai o post! (Link do original: My 10 Favorite Things About the Ruby Language)
OBS: Infelizmente, o código disposto aqui não está sendo tabulado corretamente, tornando difícil sua visualização. Peço desculpas a todos por isso e, assim que possível, corrigirei o problema.
“Eu trabalho com Ruby todos os dias e ao longo do tempo comecei a realmente gostar de usá-lo. Aqui está uma lista de algumas coisas específicas que eu realmente gosto em Ruby. Algumas delas são óbvias e algumas também existem em outras linguagens. A proposta é compartilhar coisas que eu gosto em Ruby, não para comparar e competir com alguma linguagem específica.
1. Tipagem Dinâmica
Existem coisas muito boas sobre linguagens de tipagem estática, como verificação em tempo de compilação e suporte a IDE. No entanto, na minha experiência, tipagem dinâmica realmente ajuda a conseguir projetos concisos e limpos durante as mudanças, especialmente nos estágios inicial e mediano deles.
Eu estou muito feliz que eu não preciso criar uma interface formal para meus novos objetos implementarem, simplesmente para me possibilitar trocar facilmente as classes por outras, no futuro.
2. Duck Typing
(nota: como é um termo técnico, não há tradução que seja interessante colocar)
Isso é efetivamente só uma extensão de Tipagem Dinâmica. Em Ruby, métodos que esperam ser aptos a operar objetos String não checam se é uma String (is_a?(String)). Eles checam se o objeto responde a mensagem to_str (respond_to?(:to_str)) e então chamam o to_str naquele objeto se ele permitir. Similarmente, objetos que representam Paths em Ruby podem implementar um método to_path para proporcionar uma representação de path.
Em Rails, nós usamos essa técnica para objetos que tem características de “model” ao esperar deles o respond_to?(:to_model). Isso permite-nos suportar qualquer objeto em contextos relevantes, se aqueles objetos puderem nos fornecer uma representação “model” deles.
3. Módulos Incríveis
Ruby fornece um benefício similar ao “traits” em Scala, Squeak e Perl. Efetivamente, os módulos de Ruby permitem a adição dinâmica de novos elementos da hierarquia de classe em tempo de execução. O uso de super é dinamicamente avaliado em tempo de execução para considerar qualquer módulo que possa ter sido acrescentado, tornando fácil extender funcionalidade em uma superclass quantas vezes você desejar, sem ser forçado a decidir onde super estará ao declarar sua classe.
Além disso, os módulos de Ruby fornecem os eventos append_features e included, que tornam possível usar módulos robustamente para isolar extensões uma da outra e para dinamicamente extender classes na base da característica de inclusão.
4. Corpos de classes não são especiais
Em Ruby, corpos de classes são tem um contexto especial. Eles são simplesmente um contexto onde self aponta para o objeto da classe. Se você já usou Rails, você provavelmente viu um código como esse:
class Comment < ActiveRecord::Base
validates_presence_of :post_id
end
Pode parecer que validates_presence_of é uma característica da linguagem, mas na verdade é um método sendo chamado em Comment que é fornecido por ActiveRecord::Base.
Esse método pode executar um código arbitrário, também no contexto da classe, incluindo criar novos métodos, executar outras partes de código ou atualizar a variável de instância da classe. Ao contrário das “Java annotations”, que devem ser executadas em tempo de compilação, corpos de classes em Ruby podem pegar informações em tempo de execução para considerar, como opções fornecidas dinamicamente ou os resultados da avaliação de outro código.
5. String Eval
Eu não estou me referindo a “String eval” arbitrários em tempo de execução, mas a String eval que são usados para criar métodos no processo de boot de uma aplicação Ruby.
Isso torna possível pegar estruturas definidas pelo Ruby, como Rails routes ou AOP-definitions, e compilá-las em métodos Ruby. Claro, é possível implementar essas coisas como complementos para outras linguagens, todavia Ruby torna possível implementar esse tipo de coisa em Ruby puro. É, numa visão ampla, uma linguagem “self-hosting” (que compila seu próprio código fonte).
6. Blocos e “Lambdas”
Eu já disse isso algumas vezes e repetirei: eu não considero linguagens sem “lambdas” anônimos poderosas o bastante para meu usuo diário. Essas construções são, na verdade, extremamente comuns e encontradas em linguagens como Ruby, JavaScript, Scala, Clojure e Lisp.
Eles tornam possível implementar construções de escopo de bloco que parecem características da linguagem. O exemplo mais comum é o de operações com arquivos. Em linguagens sem “lambdas”, usuários são forçados a utilizar um bloco no mesmo escopo que ele abriu o arquivo para assegurar que ele está fechado.
Em Java:
static void run(String in)
throws FileNotFoundException {
File input = new File(in);
String line; Scanner reader = null;
try {
reader = new Scanner(input);
while(reader.hasNextLine()) {
System.out.println(reader.nextLine());
}
} finally { reader.close(); }
}
Entre outras coisas, o Java precisa colocar a criação do Scanner em um bloco TRY para garantir que será fechado. Em contraste, a versão Ruby:
def run(input)
File.open(input, “r”) do |f|
f.each_line {|line| puts line }
end
end
Por causa da existência de blocos, é possível abstrair a necessidade de fechar o arquivo em um único local, minimizando erros do programador e reduzindo duplicação.
7. Ataque Combo: Linguagem que se “auto-compila”
A combinação de diversas das características acima produz exemplos reais de formas que podemos “extender” o Ruby em Rails. Considere o seguinte:
respond_to do |format|
if @user.save
flash[:notice] = ‘User was successfully created.’
format.html { redirect_to(@user) }
format.xml { render
ml => @user, :status =>ted, :location => @user }
else
format.html { render :action => “new” }
format.xml { render
ml => @user.errors, :status => :unprocessable_entity }
end
end
Nesse caso, nós estamos aptos a misturar métodos (respond_to) com código Ruby (if e else) para produzir um novo escopo de bloco. A semântica do Ruby para blocos permite-nos retornar ou executar blocos de dentro deles, mas misturando mais as fronteiras de blocos de código e construções como if ou while.
Em Rails 3, nós introduzimos:
class PeopleController < ApplicationController
respond_to :html,
ml, :json
def index
@people = Person.find(:all)
respond_with(@people)
end
end
Aqui, respond_to é fornecido em um nível de classe. Isso diz ao Rails que respond_with (índice) deve aceitar HTML, XML ou JSON como formatos. Se o usuário solicitar um formato diferente, é automaticamente retornado um erro 406 (não aceito).
Se você for um pouco mais profundo, você pode ver que o método respond_to é definido como:
def respond_to(*mimes)
options = mimes.extract_options!
only_actions = Array(options.delete(:only))
except_actions = Array(options.delete(:except))
mimes.each do |mime|
mime = mime.to_sym
mimes_for_respond_to[mime] = {}
mimes_for_respond_to[mime][:only] = only_actions unless only_actions.empty?
mimes_for_respond_to[mime][:except] = except_actions unless except_actions.empty?
end
end
Esse método é definido no módulo ActionController::MimeResponds::ClassMethods, que é colocado no ActionController::Base. Além disso, mimes_for_respond_to é definido usando class_inheritable_reader. O método do class_inheritable_reader (macro?) usa class_eval para adicionar métodos numa classe em questão para emular a funcionalidade attr_accessor.
Entender todos os detalhes não é importante. O que é importante é que usando as características do Ruby descritas acima, é possível criar camadas de abstração que podem aparecer para adicionar funcionalidades na linguagem.
Um desenvolvedor olhando para o ActionController::MimeResponds não precisa entender como class_inheritable_reader trabalha – ele só precisa entender a função básica. E um desenvolvedor olhando para a documentação da API não precisa entender como o respond_to a nível de classe é implementado – ele só precisa entender a funcionalidade fornecida. Dito isso, descamando cada camada leva a simplificar as abstrações que controem outras abstrações. Não há necessidade de descamar tudo de uma vez só.
8. Bons Literais
(nota: literais são tipo valores básicos, que você pode criar sem chamar um método .new)
Eu geralmente esqueço isso quando programo em Ruby, somente para reclamar quando uso uma linguagem com menos e pouco expressivos literais.
Ruby tem literais para quase tudo:
- Strings: single-line (uma linha), double-line (duas linhas), interpolated (interpolado)
- Números: binary, octal, decimal, hex
- Null: nil
- Boolean: true (verdadeiro), false (falso)
- Vetores: [1,2], %w(cada palavra é um elemento)
- Dicionários: {chave => valor} e no Ruby 1.9 {chave: valor}
- Expressões regulares: /hello/, %r{hello/path}, %r{hello#{interpolated}}
- Símbolos: :name e :”string qualquer”
- Bloco: {bloco literal}
E eu penso que estou perdendo algo. Enquanto isso pode parecer acadêmico, bom, legível, literais podem aumentar a habilidade do programador de escrever um código curto, mas extremamente expressivo. É claramente possível conseguir as mesmas coisas que você pode com um Dicionário “literal” ao instanciar um novo objeto Dicionário e colocar as chaves e valores um por vez, mas isso reduz a capacidade deles como parâmetros de algum método, por exemplo.
A concisão de um Dicionário “literal” permitiu os programadores de Ruby a efetivamente adicionar um recurso de argumentos limitados por palavras-chave à linguagem sem precisar da aprovação dos criadores dela. Ainda é outro pequeno exemplo de auto-compilação.
9. Tudo é um Objeto e Todo Código é Executado e tem um Self
Eu mostrei isso um pouco atrás, mas muito da razão dos corpos das Classes trabalharem como eles trabalham é uma consequência da infalível orientação a objeto da linguagem Ruby. Dentro do corpo de uma classe, Ruby é simplesmente executado com um self apontando para a classe. Além disso, nada é especial no contexto da classe; é possível avaliar o código de um contexto de classe de qualquer localidade. Veja:
module Util
def self.evaluate(klass)
klass.class_eval do
def hello
puts “#{self} says Hello!”
end
end
end
end
class PersonName < String
Util.evaluate(self)
end
Isso é exatamente equivalente a:
class PersonName < String
def hello
puts “#{self} says Hello!”
end
end
Ao remover as fontreiras artificiais entre código em diferentes locais, Ruby reduz a sobrecarga conceitual de criar abstrações. E isso é o resultado de um forte e consistente modelo de objeto.
Mais um exemplo nesse tópico. Essa expressão é muito comum em Ruby: possivelmente_nulo && possivelmente_nulo.nome_do_metodo. Já que nil é somente um objeto em Ruby, mandar mensagens a ele que não serão entendidas resultará em um NoMethodError. Alguns desenvolvedores recomendam a seguinte sintaxe: possivelmente_nulo.try(:nome_do_metodo). Isso pode ser implementado em Ruby como a seguir:
class Object
alias_method :try, :__send__
end
class NilClass
def try
nil
end
end
Essencialmente, isso adiciona o método try a qualquer Object. Quando o Object é nil, try simplesmente retorna nil. Quando o Object não é nil, try simplesmente chama o método em questão.
Usando aplicações segmentadas das classes abertas de Ruby, combinado com o fato que tudo em Ruby, incluindo nil, é um objeto, nós estamos aptos a criar um novo recurso de Ruby. Novamente, isso não é grande coisa, mas é outro caso no qual as escolhas certas na linguagem podem permitir que nós criemos abstrações muito úteis.
10. Rack
Eu irei trapacear um pouco porque Rack não faz parte da linguagem Ruby, mas ele demonstra algumas coisas úteis sobre a linguagem. Primeiramente, a biblioteca Rack só chegou ao 1.0 no começo desse ano e já todo framework web de Ruby suporta Rack. Se você usa um framework Ruby, você pode ser garantido que ele usa Rack e qualquer “Rack middleware” padrão irá funcionar.
Isso foi tudo feito sem qualquer sacrifício de compatibilidade anterior, um tributo à flexibilidade da linguagem Ruby.
Rack também traz recursos do Ruby para fazer seu trabalho. A API Rack parece com isso:
Rack::Builder.new do
use Some::Middleware, param
use Some::Other::Middleware
run Application
end
Nesse breve trecho de código, muitas coisas estão trabalhando. Primeiro, um bloco é passado para Rack::Builder. Segundo, esse bloco é avaliado no contexto de uma nova instância de Rack::Builder, o que dá acesso a usar e rodar os métodos. Terceiro, o parâmetro passado para usar e rodar é uma classe “literal”, que em Ruby é um simples objeto. Isso permite Rack chamar passed_in_middleware.new(app, param), em que new é somente uma chamada de método no objeto da classe Some::Middleware.
E no caso de você pensar que implementar o código seria hediondo, aqui está:
class Rack::Builder
def initialize(&block)
@ins = []
instance_eval(&block) if block_given?
end
def use(middleware, *args, &block)
@ins << lambda { |app| middleware.new(app, *args, &block) }
end
def run(app)
@ins << app #lambda { |nothing| app }
end
end
Isso é todo o necessário para implementar o código que eu mostrei acima que cria uma nova aplicação Rack. Instanciando o middleware é uma simples questão:
def to_app
inner_app = @ins.last
@ins[0...-1].reverse_each { |app| inner_app = app.call(inner_app) }
inner_app
end
def call(env)
to_app.call(env)
end
Primeiro, nós pegamos o último elemento, que é nosso ponto final. Nós, então, iteramos os elementos restantes com reverse, instanciando cada middleware com o próximo elemento da cadeia e retornando o objeto resultante.
Finalmente, nós definimos uma chamada de método no Construtor, que é requerida pelo Rack especificamente, que chama to_app e passa o ambiente por ele, retirando a cadeia.
Através do uso de um bom número de técnicas descritas no post, nós estamos aptos a criar uma aplicação compatível com Rack que suporta o “Rack middleware” em menos que duas dúzias de linhas de código.”
Posts interessantes:
- Manifesto ágil
- Getting Real e mais sobre a 37signals
- Aprenda Ruby on Rails com os experts no assunto
- Ruby on Rails, a nova sensação da Web
Vamos lá, conte-nos o que achou desse nosso primeiro Guest Post! Já está com todo o gás para entrar no Ruby? Comente e pergunte!
Helton de Melo Duarte
“Em Deus está a minha salvação e a minha glória; a rocha da minha fortaleza e o meu refúgio estão em Deus.” Salmos 62.7
“Ouve, ó Deus, a minha voz na minha oração; livra a minha vida do horror do inimigo.” Salmos 64.1

julho 24th, 2010 at 12:10
[...] Minhas 10 coisas favoritas sobre Ruby [...]