Bike's brake

Como contornar erros no brakeman: “User controlled method execution” e “Unsafe reflection method constantize called with parameter value”

Eu tenho dois models diferentes mas que ambos respondem aos mesmos métodos, no esquema duck typing, e preciso criar uma action show pra eles.

Porém, se eles respondem aos mesmos métodos, pra que vou criar 2 actions show em 2 controllers diferentes se elas serão exatamente iguais?

Desenvolvedor Ruby malandro que é, você cria uma rota que vai te passar uma referência ao model, além do ID, e me lança a seguinte action:

# routes.rb
get 'clients/:contactable/:id', to: 'clients#show', as: :contactable

# ClientsController
def show
  contactable_model = params[:contactable]
  @contactable = current_company.send(contactable_model).find(params[:id])
  ...
end

E aí vem o nosso querido Brakeman, sempre preocupado com as falhas de segurança da nossa aplicação, lança o erro: “User controlled method execution”!

Você relê o código que escreveu e na mesma hora vê o mole que deu!  Usando send com qualquer coisa que o usuário preencher!?

Como resolver isso? Você pensa em usar o constantize porque se o usuário tentar marretar qualquer outra coisa vai dar exception de constante não existe, easy:

def show
  contactable_model = params[:contactable].singularize.classify.constantize
  @contactable = contactable_model.where(company: current_company).find(params[:id])
end

Aí vem o Brakeman e lança: “Unsafe reflection method constantize called with parameter value”…

Você para um pouco e reflete sobre isso… verdade, ainda é muita informação que você tá dando pro possível atacante… como resolver então?

A forma que mais curti foi a sugerida pelo Paul Kwiatkowski:

class ClientsController < ApplicationController
  CONTACTABLE_MODELS = {
    'contacts' => Contact,
    'connections' => Connection
  }.freeze

  def show
    contactable_model = CONTACTABLE_MODELS.fetch(params[:contactable])
    @contactable = contactable_model.where(company: current_company).find(params[:id])
  end
end

Dessa forma, se o atacante tentar qualquer outro valor além dos que estão nas chaves da constante, uma exception será levantada de cara pelo fetch. 👍

Someone coding

How to delete local git branches after the merge

From time to time we all get annoyed by the git branch autocomplete, which used to be so useful, but now makes us practically write the whole branch name! 😡

Why it keeps happening? Because we are awesome developers who use feature branch flow and now, after working so hard, we have dozens of branches on our local machine.

Yogo, can you tell me an easy way to clear these branches? I don’t wanna delete each one manually! 😩

I sure can, my friend! Here is an alias that I use to delete all my merged branches:

alias clear_merged_branches="git branch --merged | egrep -v '(^\*|master|production)' | xargs git branch -d"

It will only leave the “master” and “production” branches, which you can easily change on that code. Besides the branches that haven’t been merged into master, of course.

Remember to use this alias while you are on the “master” branch 😉

Update

As lpmusix pointed out, you can also use it as a git alias. Add to the alias section of your ~/.gitconfig:

cb = "!git branch --merged | egrep -v '(^\\*|master|production)' | xargs git branch -d"

Melhore o seu Ruby semanalmente com Ruby Tapas

Ruby Tapas é um serviço criado pelo Avdi Grimm onde ele semanalmente posta um vídeo ensinando algo relacionado a Ruby.

O conteúdo é intermediário pra avançado, mas inclusive iniciantes conseguem acompanhar, são vídeos geralmente bem curtos, em média com 3-10 minutos onde ele aborda um tema bem específico que pode ser:

  • técnica de refatoração
  • explicação mais a fundo de alguma classe Ruby
  • dica de performance
  • dica de arquitetura de software
  • estratégia de implementação
  • design pattern
  • etc

O Avdi é muito famoso na comunidade e os vídeos desse curso fazem jus ao seu nome, recomendo a todo desenvolvedor Ruby.

Como os planos são em dólar, não sai muito barato pra gente, mas dê uma olhada nos vídeos que ele liberou gratuitamente pra você ter uma idéia da qualidade.

fast red car

Dicas de performance pra Ruby: modifique as variáveis usando os métodos com bang!

Métodos com bang (!)

No Ruby temos as versões com bang (!) de vários métodos das classes principais, como: collect!, downcase!, flatten!, reverse! e capitalize!.

Essas variações dos métodos, na maioria das vezes, indica que você vai modificar o objeto ao invés de fazer uma cópia do objeto, altera-la e retorna-la.

Como economizar memória

Pela explicação você já deve ter percebido essa prática forma de economizar memória no Ruby: usar os métodos com bang sempre que possível!

Vamos exemplificar com código. Começaremos criando uma string com 50 megas:

huge_string = 'a' * 1024 * 1024 * 50

Agora, se usarmos o método upcase pra colocar todas as letras para maiúscula, o sistema precisará alocar mais 50 megas de memória só para fazer essa alteração:

huge_string = huge_string.upcase

Porém, usando a versão com bang, upcase!, essa enorme string não precisará ser replicada e evitaremos esse uso extra de memória!

Vale só para strings?

Nope! Isso serve pra tudo: hash, array, enumerable, etc…

A regra é simples: se não precisa copiar a variável, procure usar o método que faz a modificação in place ;D

Usando field type daterange com Rails e PostgreSQL

Outro dia precisei criar um model que permitisse ao freelancer marcar sua disponibilidade no Bonsai. A idéia é que ele pudesse marcar que está disponível full-time, part-time ou indisponível durante um certo período de dias.

Uma boa alternativa para a clássica combinação de campos “start_date” e “end_date” foi usar o field type daterange que o PostgreSQL possui e o Rails suporta muito bem.

Como defini-lo na migration:

create_table :availabilities do |t|
  t.daterange :period
  ...
end

Trabalhando com campos daterange

Filtrar os records com períodos que comecem a partir do início da atual semana:

scope :from_current_week_onward, -> { where("lower(period) >= :beginning_of_week", beginning_of_week: Date.current.beginning_of_week) }

Pesquisar por um período exato:

Availability.where("period && daterange(:start, :end)", start: Date.current, end: 3.days.from_now)

O campo tem todos os métodos de um Range do Ruby:

a.period # => Mon, 02 Jan 2017...Mon, 09 Jan 2017
a.period.begin # => Mon, 02 Jan 2017
a.period.end # => Mon, 09 Jan 2017

Por que eu uso Date.current? Leia esse post que escrevi no blog da HE:Labs: Evitando problemas com datas e timezones no Rails.

Gotcha

Atenção pra um detalhe importante: o Rails sempre salva o range usando a versão exclusiva dela:

availability.period = Date.current.beginning_of_week..Date.current.end_of_week # => Mon, 02 Jan 2017..Sun, 08 Jan 2017
availability.save # => true
availability.reload.period # => Mon, 02 Jan 2017...Mon, 09 Jan 2017

Isso é um saco, apanhei bastante até perceber isso então espero que esse post ajude a poupar o seu tempo 😁

Quer poder definir horários nos ranges também?

Se você precisar usar horas e minutos no seu range, também é simples, basta usar o field type tsrange ao invés do daterange. 😉

Ruby already has its own regular expression to validate emails

I’ve searched and written a lot of regular expressions to validate email on our models and forms but recently I’ve found out that Ruby already has a good one and its easy to access and use.

All you have to do require uri library and use its constant:

require 'uri'
'user@gmail.com'.match(URI::MailTo::EMAIL_REGEXP).present?

It’s also easy to use on an Active Record format validation:

validates :email, format: { with: URI::MailTo::EMAIL_REGEXP, message: "only allows valid emails" }

It even accepts emails with “+” as gmail enables.

 

Security - Private

Como private methods funcionam no Ruby

Uma confusão comum em Ruby é com métodos de classe privados. Veja o código a seguir:

class TwitterAccount
  def self.exibition_name
    "Cayo Medeiros #{username}"
  end

  private
    def self.username
      "yogodoshi"
    end
end 

Muitas pessoas acreditam que o método self.username está privado devido a declaração private logo antes dele. Porém, essa declaração só funciona para métodos de instância, não para os de classe.

Para transformar um método de classe em privado, utilize:

private_class_method :username

Para maiores detalhes, leia nesse post a resposta do criador do Ruby sobre o porque ter sido feito dessa forma.