Ruby

6 nov, 2018

6 dicas de ruby que você deveria conhecer

Publicidade

Há tempo, escrevi um artigo falando sobre 8 Dicas de PHP que você deveria conhecer, O público gostou bastante, principalmente por ter dicas muito úteis para o dia a dia de qualquer desenvolvedor. Seguindo essa mesma linha, juntei seis dicas de Ruby (e Rails) que você deveria conhecer.

Dica 1: Tags HTML nos ymls I18n

Por mais que não seja recomendável, é comum encontrarmos aplicações que utilizam tag html nos arquivos de traduções, como no exemplo abaixo:

# en.yml
en:
  hello: "Hello <strong>%{user_name}</strong>!"

# index.html.erb
<%= t('hello', user_name: current_user.first_name) %>

Nesse exemplo, temos um problema, pois o texto que será mostrado na tela será o seguinte “Hello <strong>Allan</strong>!”. Isso mesmo, o texto imprime as tags e não as utiliza como html. Até aí, tudo bem. Esquecemos de utilizar o html_safe, logo, o resultado seria assim:

# index.html.erb
<%= t('hello', user_name: current_user.first_name).html_safe %>

Agora, você abriu uma brecha de segurança na qual qualquer usuário poderá fazer XSS no seu sistema. Mas não se preocupe: o Rails tem uma solução para isso no yml!

Você vai fazer assim:

# en.yml
en:
  hello_html: "Hello <strong>%{user_name}</strong>!"

Ao inserir o “_html” na chave, o Rails entende que nesse texto tem html e, então, vai usar as tags corretamente. Você não precisa utilizar o “html_safe” na view. Fica assim:

# en.yml
en:
  hello_html: "Hello <strong>%{user_name}</strong>!"

# index.html.erb
<%= t('hello', user_name: current_user.first_name) %>

Dica 2: Safe operator

Essa é para quem usa ruby 2.3+ (se você usa uma versão menor, está na hora de atualizar esa app). Os safe operators foram introduzidos na versão 2.3 e têm o mesmo comportamento semelhante ao do objeto `try`.

# Caso de sucesso
2.4.1 :001 > array = [1, 2]
2.4.1 :002 > array.first.positive?
 => true

# Caso de erro
2.4.1 :001 > array = []
2.4.1 :002 > array.first.positive?
NoMethodError: undefined method `positive?' for nil:NilClass

# Caso de tratamento com safe operator
2.4.1 :001 > array = []
2.4.1 :002 > array.first&.positive?
 => nil

Assim como o try, o safe operator faz a validação antes de executar o método, impedindo que exploda um erro, e no caso de erro retorna `nil`.

Dica 3: Mapear array

Muitas vezes temos alguns arrays com elementos fixos que vamos trabalhar dentro de um método enumerable X. Em vez de ficar repetidamente chamando `array[0]`, podemos mapear o hash da seguinte forma:

names_and_companies = [
  ['Allan Klaus', 'Locaweb'],
  ['Amleto Denmarke', 'Locaweb'],
]

O mais comum seria trabalhar com esse hash da seguinte maneira:

names_and_companies.map { |element| "#{element[0]} works at #{element[1]}" }

Porém, mapeando o hash, você pode trabalhar definindo um nome para as posições do array, facilitando a leitura e entendimento do que está acontecendo.

names_and_companies.map { |(name, company)| "#{name} works at #{company}" }

Dica 4:  Keyword arguments

Há muitas formas de declarar a assinatura de um método no Ruby. A mais comum é positional arguments, que é o exemplo abaixo:

# Positional Arguments
def my_method(arg_1:, arg_2:, arg_3:)
  [arg_1, arg_2, arg_3]
end

my_method(1, 'a', Object.new)
 => [1, "a", #<Object:0x000000011f3780>]

A forma positional arguments tem um gap no fato que, sempre que for usar o método, é preciso saber as posições de argumentos da assinatura. Uma forma de evitar isso é montar a assinatura do método com keyword arguments.

def my_method(arg_1:, arg_2:, arg_3:)
  [arg_1, arg_2, arg_3]
end

my_method(arg_2: 'a', arg_3: Object.new, arg_1: 1)
 => [1, "a", #<Object:0x00000001216bb8>]

Dica 5: Transform values (hash)

Essa dica é só para quem usa Ruby 2.4+.

Quando precisamos fazer uma alteração em todos os elementos de um hash, até a versão 2.3 podemos utilizar uma das seguintes abordagens:

my_hash = { a: 1, b: 2, c: 4 }

my_hash.inject({}) { |h, (k, v)| h.merge(k => v * 2) }
 => { :a=>2, :b=>4, :b=>8 }

my_hash.each_with_object({}) { |(k, v), h| h[k] = v * 2 }
 => { :a=>2, :b=>4, :b=>8 }

my_hash.map { |k, v| [ k, v * 2 ] }.to_h
 => { :a=>2, :b=>4, :b=>8 }

Com a versão 2.4, podemos utilizar o método `transform_values`, esse método faz exatamente o que faríamos, só que em alto nível.

my_hash.transform_values { |v| v * 2 }
 => { :a=>2, :b=>4, :b=>8 }

O `transform_values` também implementa o “!”. Assim, você pode sobrescrever o valor do seu hash ao utilizar – o que não era possível de forma “tão simples” nas versões anteriores.

Dica 6:  Métodos de classe/objeto no console

Uma coisa que sempre causa dúvida é saber quais são os métodos de determinada classe. Para isso é simples, basta usar Object.methods.

Desta maneira, você estará listando todos os métodos da classe. Também podemos buscar por algum método específico:

Object.methods.grep(/exec/)
 => [:module_exec, :class_exec, :trace_execution_scoped, :trace_execution_unscoped, :instance_exec]

Você pode fazer o mesmo para objetos instanciados:

my_object = Object.new
 => #<Object:0x000000058c5848> 
my_object.methods

Também podemos aplicar a busca por métodos no objeto instanciado:

my_object.methods.grep(/exec/)
 => [:instance_exec]

É algo muito legal saber os métodos. Poderíamos pensar “sei que tem um método X, mas queria olhar para esse método, queria entender o que está por ‘baixo dos panos’”. Nesse caso, temos duas alternativas para isso:

Podemos olhar onde está localizado esse método para abrimos o arquivo. Nesse caso, podemos usar o source_location (esse método não funciona para métodos implementados em outras linguagens):

# /home/test.rb
class MyClass
  def test
    return 'test'
  end
end

2.5.1 :001 > require './test'
 => true 
2.5.1 :002 > my_object = MyClass.new
 => #<MyClass:0x00000001800bc8> 
2.5.1 :003 > my_object.method(:test).source_location
 => ["/home/test.rb", 2]

O método indica em qual arquivo está e a linha do método. É muito útil no caso de você precisar alterar o código do método em questão. Caso o nosso objetivo seja só mostrar o código do método na tela, podemos usar:

2.5.1 :004 > require 'method_source'
 => true
2.5.1 :006 > my_object.method(:test).source.display
  def test
    return 'test'
  end
 => nil 

Tem mais alguma dica que acha legal? Compartilhe conosco nos comentários!