Novidades no Ruby 1.8.7
Publicado em Developer
ruby
É engraçado que alguns dias atrás saíram as versões novas do Ruby (1.8.7) e do Rais (2.1) e até agora há muitos posts por aí detalhando o que mudou no Rails mas até agora só vi alguns posts listando algumas novidades do Ruby novo, geralmente listando os métodos novos sem muitos comentários. Até parece que o Rails nem é feito em Ruby. Vai entender.
Aqui nesse post vou mostrar algumas das novidades da versão 1.8.7, já dizendo que vou seguir a ordem em que elas aparecem no arquivo http://svn.ruby-lang.org/repos/ruby/tags/v1_8_7/NEWS, mas complementando com algumas explicações.
Houve muitas alterações em vários métodos que antes recebiam blocos e agora podem ser chamados sem o bloco, fazendo-os retornar um iterator para percorrer os seus elementos. Esse tipo de iterator é um objeto do tipo Enumerable::Enumerator, cuja descrição na documentação é:
A class which provides a method `each' to be used as an Enumerable object.
Vamos ver um exemplo utilizando Array#each:
1 array = [1,2,3] 2 enum = array.each 3 loop do 4 puts enum.next 5 end
Como houve muitos métodos que assumiram esse comportamento, vou listar a grande maioria aqui e vamos detalhar alguns no decorrer desse artigo:
- Array#collect!
- Array#map!
- Array#each
- Array#each_index
- Array#reverse_each
- Array#reject
- Array#reject!
- Array#delete_if
- Dir#each
- Dir#foreach
- Enumerable#find
- Enumerable#find_all
- Enumerable#partition
- Enumerable#reject
- Enumerable#select
- Enumerable#sort_by
- Hash#delete_if
- Hash#each
- Hash#each_key
- Hash#each_pair
- Hash#each_value
- Hash#reject!
- Hash#select
- ENV.delete_if
- ENV.each
- ENV.each_key
- ENV.each_pair
- ENV.each_value
- ENV.reject!
- ENV.select
- Integer#downto
- Integer#times
- Integer#upto
- IO#each
- IO#each_line
- IO#each_byte
- IO.foreach
- ARGF.each
- ARGF.each_line
- ARGF.each_byte
- Numeric#step
- ObjectSpace.each_object
- Range#each
- Range#step
- String#each_byte
- String#each
- String#each_line
- String#gsub(pattern)
- Struct#each
- Struct#each_pair
- StringIO#each
- StringIO#each_line
- StringIO#each_byte
A classe Array teve o seu método flatten modificado, agora ele permite indicar qual o nível de "achatamento" desejado:
1 array = [1,2,[3,[4,[5]]]] 2 p array.flatten 3 p array.flatten(1) 4 p array.flatten(2)
[1, 2, 3, 4, 5] [1, 2, 3, [4, [5]]] [1, 2, 3, 4, [5]]
Os métodos index e rindex agora aceitam blocos também:
1 array = [1,2,3,4,5] 2 p array 3 puts array.index {|item| item%2==0} 4 puts array.rindex {|item| item%2==0}
[1, 2, 3, 4, 5] 1 3
Falando em index, temos o método novo find_index que aceita um parâmetro ou um bloco:
1 enum = (1..10) # eu usei arrays para todo lado mas pode ser uma Range etc. 2 puts enum.find_index(5) 3 puts enum.find_index {|item| item>7}
Um comportamento interessante pode ser visto em métodos como o map!, sem o bloco:
1 array = [1,2,3,4,5] 2 enum = array.map! 3 puts "Vou processar o array na hora que der na telha." 4 puts "Quem sabe agora?" 5 p array 6 enum.each {|item| item*2} 7 p array
Vou processar o array na hora que der na telha. Quem sabe agora? [1, 2, 3, 4, 5] [2, 4, 6, 8, 10]
Os métodos pop e shift agora permitem especificar quantos elementos processar:
1 array = [1,2,3,4,5] 2 p array.pop(2) 3 p array.shift(2) 4 p array 5 p array.pop 6 p array
[4, 5] [1, 2] [3] 3 []
Lembram-se de alguns truques que utilizávamos para pegar um elemento aleatório de uma coleção? Pois agora Array ganhou o método choice que faz isso. Uma coisa interessante que eu notei é que aparentemente chamando rand antes de usar choice leva a melhores resultados aleatórios. Experimentem comentar as linhas 2 e 3 do código a seguir:
1 array = [1,2,3,4,5] 2 puts "jeito antigo" 3 puts array.instance_eval{self[rand * size]} 4 puts "jeito novo" 5 puts array.choice 6 puts array.choice 7 puts array.choice
jeito antigo 5 jeito novo 2 5 4
É interessante notar que o Rails já tem um método rand:
1 >> a = [1,2,3,4,5] 2 => [1, 2, 3, 4, 5] 3 >> a.rand 4 => 1 5 >> a.rand 6 => 1 7 >> a.rand 8 => 3
O Array também ganhou o método combination. Vamos supor que queremos combinar os Beatles em duplas ou trios. Podemos usar:
1 beatles = %w(John Paul George Ringo) 2 puts "combinação:" 3 p beatles.combination(2).to_a 4 p beatles.combination(3).to_a
O resultado será:
combinação: [["John", "Paul"], ["John", "George"], ["John", "Ringo"], ["Paul", "George"], ["Paul", "Ringo"], ["George", "Ringo"]] [["John", "Paul", "George"], ["John", "Paul", "Ringo"], ["John", "George", "Ringo"], ["Paul", "George", "Ringo"]]
Não é por nada não, mas fico com a primeira dupla. ;-) Podemos usar blocos também:
1 puts "com bloco agora:" 2 beatles = %w(John Paul George Ringo) 3 beatles.combination(2) {|c| puts c.join(" e ")}
O resultado será:
com bloco agora: John e Paul John e George John e Ringo Paul e George Paul e Ringo George e Ringo
Podemos criar "timinhos" repetindo os integrantes mas fazendo uma permutação deixando cada um de líder do seu time usando o método novo permutation, que pode ser utilizado com blocos também:
1 puts "permutação:" 2 beatles = %w(John Paul George Ringo) 3 p beatles.permutation(2).to_a 4 p beatles.permutation(3).to_a 5 6 beatles.permutation(2) {|item| puts item.join(" e ")}
O resultado será:
permutação: [["John", "Paul"], ["John", "George"], ["John", "Ringo"], ["Paul", "John"], ["Paul", "George"], ["Paul", "Ringo"], ["George", "John"], ["George", "Paul"], ["George", "Ringo"], ["Ringo", "John"], ["Ringo", "Paul"], ["Ringo", "George"]] [["John", "Paul", "George"], ["John", "Paul", "Ringo"], ["John", "George", "Paul"], ["John", "George", "Ringo"], ["John", "Ringo", "Paul"], ["John", "Ringo", "George"], ["Paul", "John", "George"], ["Paul", "John", "Ringo"], ["Paul", "George", "John"], ["Paul", "George", "Ringo"], ["Paul", "Ringo", "John"], ["Paul", "Ringo", "George"], ["George", "John", "Paul"], ["George", "John", "Ringo"], ["George", "Paul", "John"], ["George", "Paul", "Ringo"], ["George", "Ringo", "John"], ["George", "Ringo", "Paul"], ["Ringo", "John", "Paul"], ["Ringo", "John", "George"], ["Ringo", "Paul", "John"], ["Ringo", "Paul", "George"], ["Ringo", "George", "John"], ["Ringo", "George", "Paul"]] John e Paul John e George John e Ringo Paul e John Paul e George Paul e Ringo George e John George e Paul George e Ringo Ringo e John Ringo e Paul Ringo e George
Podemos também fazer uma combinação dos elementos com o método novo product, que retorna todas as combinações da coleção da esquerda com a da direita:
1 p %w(John Paul).product(%w(George Ringo))
[["John", "George"], ["John", "Ringo"], ["Paul", "George"], ["Paul", "Ringo"]]
O método novo cycle percorre a coleção quantas vezes forem especificadas:
1 beatles = %w(John Paul George Ringo) 2 beatles.cycle(2) {|item| puts item}
O método novo drop permite excluir elementos da coleção, com uma quantidade específica indicada ou utilizando um bloco:
1 beatles = %w(John Paul George Ringo) 2 p beatles.drop(2) 3 p beatles.drop_while {|item| item.length==4}
["George", "Ringo"] ["George", "Ringo"]
Os métodos novos take e take_while retornam, respectivamente, o número de elementos especificado na coleção no primeiro caso e no segundo os elementos até que a condição não seja mais atendida:
1 beatles = %w(John Paul George Ringo) 2 p beatles.take(2) 3 p beatles.take_while {|item| item.length<=4}
["John", "Paul"] ["John", "Paul"]
O que fazíamos com sort_by combinado rand agora podemos fazer com shuffle e shuffle! e reordenar uma coleção randomicamente:
1 beatles = %w(John Paul George Ringo) 2 p beatles.sort_by { rand } # jeito antigo 3 p beatles.shuffle 4 beatles.shuffle! 5 p beatles
["Ringo", "Paul", "John", "George"] ["George", "Paul", "Ringo", "John"] ["George", "John", "Ringo", "Paul"]
Os métodos each_slice e each_cons agora estão incorporados direto (sem precisar fazer require "enumerator" como no 1.8.6):
1 enum = (1..10) 2 enum.each_slice(3) do |slice| 3 p slice 4 end
[1, 2, 3] [4, 5, 6] [7, 8, 9] [10]
Ganhamos um método Symbol#to_proc de maneira similar ao que temos no Rails:
1 enum = (1..10) 2 puts enum.inject(&:+) 3 puts enum.inject(&:*)
O método novo group_by agrupa nossa coleção de acordo com os critérios do bloco:
1 beatles = %w(John Paul George Ringo) 2 p beatles.group_by {|item| item.length} 3 p beatles.group_by {|item| item.length%2==0}
{5=>["Ringo"], 6=>["George"], 4=>["John", "Paul"]} {false=>["Ringo"], true=>["John", "Paul", "George"]}
Podemos usar alguns métodos novos para encontrar os elementos maiores e menores de acordo com os nossos critérios:
1 beatles = %w(John Paul George Ringo) 2 # encontrando o maior nome 3 p beatles.max_by {|item| item.length} 4 # encontrando o menor nome 5 p beatles.min_by {|item| item.length} 6 # encontrando o menor e o maior 7 p beatles.minmax {|a,b| a.length <=> b.length} 8 # encurtando um pouco mais para fazer a mesma coisa ... 9 p beatles.minmax_by {|item| item.length}
"George" "John" ["John", "George"] ["John", "George"]
Podemos verificar se nenhum ou apenas um elemento da coleção atende nossos critérios:
1 beatles = %w(John Paul George Ringo) 2 puts beatles.none? {|item| item.length>10} 3 puts beatles.one? {|item| item.length==6}
Finalmente ganhamos aqueles metodozinhos que sempre implementavamos quando íamos fazer uma apresentação e mostrar classes abertas, odd e even:
1 puts 1.odd? 2 puts 2.even?
Como já existia o succ, faltava o pred:
1 puts 1.pred 2 puts 1.succ
Os métodos upto, downto, step e times entraram todos na dança do Enumerable::Enumerator:
1 upto_e = 1.upto(3) 2 downto_e = 10.downto(7) 3 times_e = 5.times 4 step_e = 100.step(120,2) 5 6 3.times do 7 puts upto_e.next 8 puts downto_e.next 9 puts times_e.next 10 puts step_e.next 11 end
1 10 0 100 2 9 1 102 3 8 2 104
Agora um método bem importante para já irmos adaptando o nosso código para a versão 1.9: o método ord. Vamos dar uma olhada na versão atual, como é o comportamento com caracteres:
[taq@~]irb irb(main):001:0> "taq"[1] => 97
Porém, na versão 1.9:
[taq@~/code/ruby]irb1.9 irb(main):001:0> "taq"[1] => "a"
Aí entra todo aquele esquema de basear em caracteres blá blá blá, mas o X da coisa é q se você já quer ir adaptando onde precisa pegar o valor do caracter, pode usar ord:
[taq@]irb irb(main):001:0> "taq"[1].ord => 97 [taq@~/code/ruby]irb1.9 irb(main):001:0> "taq"[1].ord => 97
[taq@~/code/ruby]irb irb(main):001:0> ?a => 97 irb(main):002:0> ?a.ord => 97
Ganhamos uma bela economia de class << self; self; end com os novos métodos class_exec, module_exec e instance_exec (eba!):
1 class HelloWorld 2 def hello 3 print "hello, " 4 end 5 end 6 7 HelloWorld.class_exec do 8 def world 9 print "world!\n" 10 end 11 end 12 13 h = HelloWorld.new 14 h.hello; h.world 15 16 module OlaMundo 17 def ola 18 print "olá, " 19 end 20 end 21 22 OlaMundo.module_exec do 23 def mundo 24 print "mundo!\n" 25 end 26 end 27 28 class HelloWorld 29 include OlaMundo 30 end 31 h.ola; h.mundo 32 33 h.instance_exec { 34 def beleza 35 puts "beleza?" 36 end 37 } 38 h.ola; h.mundo; h.beleza
hello, world! olá, mundo! olá, mundo! beleza?
Falando em métodos, ganhamos os métodos name, owner e receiver, e de quebra o __method__ que contém o nome do método corrente evitando ter que fazer coisas desse tipo:
1 def oi 2 puts "Estou no método #{__method__}." 3 end 4 m = method(:oi) 5 puts m.name 6 puts m.owner 7 puts m.receiver 8 oi
E olhem o tap aí! Eu havia mencionado ele antes, como um recurso do 1.9 mas a galerinha já deu uma adiantada no bicho:
1 p [1,2,3,4,5].select {|item| item%2==0}.tap {|item| puts "encontrei #{item.size} números pares que vou multiplicar por 2" }.map {|item| item*2}
Nem o ObjectSpace escapou:
1 enum = ObjectSpace.each_object(String) 2 puts "#{enum.count} Strings criadas:" 3 puts enum.next 4 puts enum.next 5 puts enum.next
Expressões regulares ganharam o método union para concatenar os padrões enviados, seja por String ou por Regexps:
1 regexp = Regexp.union(%w(dog cat)) 2 p regexp 3 puts "dog" =~ regexp 4 puts "cat" =~ regexp 5 puts "Cat" =~ regexp 6 puts "rat" =~ regexp 7 8 regexp = Regexp.union(/dog/,/cat/i) 9 p regexp 10 puts "dog" =~ regexp 11 puts "cat" =~ regexp 12 puts "Cat" =~ regexp 13 puts "rat" =~ regexp
Falando em expressões regulares, a String ganhou dois métodos novos, start_with? e end_with?, mas eu particularmente prefiro usar expressões regulares para fazer a mesma coisa:
1 puts "teste".start_with?("t") 2 puts "teste".end_with?("e") 3 # mesma coisa 4 puts "teste" =~ /^t/ 5 puts "teste" =~ /e$/
A String também ganhou os métodos novos partition e rpartition, além de uma opção para o upto para especificar se o último valor deve ser incluído:
1 p "teste".partition("e") 2 3 p "teste".rpartition("t") 4 "a1".upto("a5") {|item| puts item} 5 "a1".upto("a5",true) {|item| puts item}
["t", "e", "ste"] ["tes", "t", "e"] a1 a2 a3 a4 a5 a1 a2 a3 a4
A classe IPAddr ganhou os métodos succ, <=> (navinha!) e to_range. Vamos dar uma olhada especificando um CIDR que nos permita ver os IP's da range aqui:
1 require "ipaddr" 2 3 ip1 = IPAddr.new("192.168.0.1") 4 ip2 = ip1.succ 5 p ip1 6 p ip2 7 8 puts "ip1 < ip2: #{ip1 < ip2}" 9 puts "ip2 < ip1: #{ip2 < ip1}" 10 puts "ip1 <=> ip2: #{ip1 <=> ip2}" 11 12 ip = IPAddr.new("192.168.0.1/29") 13 p ip.to_range 14 p ip.to_range.to_a.map {|item| item.to_s}
#<IPAddr: IPv4:192.168.0.1/255.255.255.255> #<IPAddr: IPv4:192.168.0.2/255.255.255.255> ip1 < ip2: true ip2 < ip1: false ip1 <=> ip2: -1 #<IPAddr: IPv4:192.168.0.0/255.255.255.248>.. #<IPAddr: IPv4:192.168.0.7/255.255.255.248> ["192.168.0.0", "192.168.0.1", "192.168.0.2", "192.168.0.3", "192.168.0.4", "192.168.0.5", "192.168.0.6", "192.168.0.7"]
A classe Net::SMTP ganhou suporte SSL. Vamos mandar um email pelo Gmail:
1 require "net/smtp" 2 3 user = "eustaquiorangel" 4 pass = "saiforajacare" # vocês tão achando que é essa mesmo? ;-) 5 6 mail = <<FIM 7 From: Eustáquio Rangel <eustaquiorangel@gmail.com> 8 Subject: Loser! 9 10 Tá no Yahoo! ainda? 11 FIM 12 13 smtp = Net::SMTP.new("smtp.gmail.com",465) 14 smtp.enable_ssl(OpenSSL::SSL::SSLContext.new) 15 smtp.start("localhost.localdomain",user,pass,:login) 16 smtp.send_message(mail,"eustaquiorangel@gmail.com","eustaquiorangel@yahoo.com") 17 smtp.finish
Os arquivos temporários podem ter sufixos agora, e temos um método para informar qual o diretório de arquivos temporários da plataforma:
1 require "tempfile" 2 3 puts Dir.tmpdir 4 temp = Tempfile.new(["bla",".txt"]) 5 puts temp.path
E, finalmente, o parse de um objeto Date agora segue o formato padrão de YYYY-MM-DD e não mais MM-DD-YYYY como anteriormente:
1 require "date" 2 3 d = Date.parse("2008-06-04") 4 puts d.strftime("%d/%m/%Y")
04/06/2008
UFA! É isso. Há mais novidades, mais eu procurei concentrar em algumas que achei mais interessantes. Para conhecer as outras, por favor deêm uma olhada na URL que eu informei no começo do artigo. Somente 0.3 para a 1.9! :-)
Comentários
Comentários fechados.
Artigos anteriores
- Pull requests em modo raiz - sex, 22 de dezembro de 2023, 09:57:09 -0300
- Qual a idade do seu repositório? - ter, 27 de dezembro de 2022, 12:50:35 -0300
- Utilizando ctags em projetos Rails mais recentes - qui, 24 de junho de 2021, 08:23:43 -0300
- Fazendo o seu projeto brotar - seg, 15 de julho de 2019, 08:57:05 -0300
- Learn Functional Programming with Elixir - sex, 02 de março de 2018, 18:47:13 -0300
- Ambiente mínimo - Driver Driven Development - qua, 23 de agosto de 2017, 15:15:03 -0300
- Ambiente mínimo - repositórios de código - dom, 16 de abril de 2017, 13:02:14 -0300
- Ambiente mínimo - terminal e navegador - dom, 02 de abril de 2017, 21:43:29 -0300
- Utilizando muitas gems no seu projeto? - sáb, 29 de outubro de 2016, 11:57:55 -0200
- Desenvolvedores e inteligência artificial - seg, 11 de julho de 2016, 09:09:38 -0300
Mereghost, fazia tempo que não ouvia essa do Jack. ;-)
Em relação à "quebradeira", o bom é que a turminha trabalha rápido para consertar os problemas que aparecem. Eu nem postei nada nesse sentido, mas se em uns 15 dias ainda continuar algum problema mais chato eu faço um "catadão" e publico aqui.
E vale aquela regra: nunca dê uma de doido e bote uma versão nova saindo do forno em um ambiente de produção! ;-)
[]'s!
Fala, grande TaQ Nicholson!
Então, alguns detalhes do release do 1.8.7 a mais: quebrou algumas coisas. Entre elas o rails. O problema é no cgi.rb mas que foi consertado do tag 1.8.7.7 ou no stable snapshot para quem gosta de "arriscar" (arriscar e stable não ficam bem na mesma frase) mais.
O require_gem foi de fato removido o que pode gerar alguns problemas com algumas gems.
Alguns plugins *parecem* quebrar também, como o will_paginate, mas pode ser algum problema da view que eu estava testando.
Tranquilo!
"Não é por nada não, mas fico com a primeira dupla." - John e Paul? Sério? Mas o melhor Beatle é o George, todo mundo sabe isso.
Fora isso, sensacional!
Muito bom o artigo TaQ. Parabéns.
Abraco.
Jackson, putz, eu "limei" ali aqueles pontos pois o comentário ficou muito grande, desculpe ... mas realmente eu deixei aqueles sem respostas pois algumas são fáceis de deduzir e algumas outras muito compridas. E também como forma de vocês ficarem curiosos e instalarem o 1.8.7 para ver as respostas! :-)
Abraço!
Adorei as novidades!
Só não entendi pq alguns ítens ficaram sem a resposta... ou foi proposital? :(
Estou enviando os ítens para se for o caso você completar..
Abraço!
vlw!
Rafael, obrigado, corrigi o erro! Isso que dá ficar escrevendo de madrugada ehehe. :-)
Muito bom o artigo, só um errinho em: "Até parece que o Rails nem é feito em Rails. Vai entender." acho que você quis dizer "Até parece que o Rails nem é feito em Ruby. Vai entender."
Abração!