Rinaldi Fonseca

Rubismos – Configurações

13 de abril de 2011

Muitas Gems que usamos no dia a dia, utilizam o seguinte formato para realizar configurações:

MyLib.configure do |config|  
  config.my_property = 25  
end

Ou seja, estamos setando uma propriedade com o valor 25.

A Gem Capybara utiliza esta mesma abordagem. Veja abaixo:

Capybara.configure do |config|             	
  config.default_wait_time = 2  
end

Vamos ver abaixo, como funciona esta implementação. Considere o seguinte código abaixo:

module MyLib
  class << self
    attr_accessor :my_property
    def configure
      yield self
    end
  end
end
 
MyLib.configure do |config|
  config.my_property = 2011
end
 
puts MyLib.my_property

Basicamente estamos definindo um módulo chamado MyLib. Dentro dele, estamos definindo um accessor chamado my_property e também um método chamado configure.
Porém o accessor e o método, são definidos na singleton class do objeto MyLib.

Se você não entendeu muito bem esta questão de singleton classes, eu expliquei com mais detalhes no post “Ruby: relações e interações entre classes e objetos”.

A grande “sacada” está no método configure. Ele executa o método yield passando o self como parâmetro. Mas neste caso, quem será self? Self será o próprio módulo MyLib. Esta é a “chave” da questão.

É importante lembrar que o yield é o responsável por executar o bloco passado como parâmetro. E no exemplo acima, nosso bloco é:

do |config| config.my_property = 2011 end

Seguindo a ideia de como os blocos funcionam em Ruby, o módulo MyLib(que foi passado como self para o bloco) será mapeado para o config do bloco. Logo, quando fazemos config.my_property = 2011 de dentro do bloco, é como se estivéssemos fazendo MyLib.my_property = 2011.

Isso só foi possível pelo fato de termos definido um accessor para a nossa propriedade.

Uma outra abordagem que poderíamos utilizar para deixar a configuração um pouco mais modular seria com o seguinte código abaixo:

class Configuration
    attr_accessor :my_property
 
    def initialize
      @my_property = 'default value'
    end
end
 
module MyLib
  class << self
    attr_accessor :configuration
  end
 
  def self.configure
    self.configuration ||= Configuration.new
    yield(configuration)
  end
end
 
MyLib.configure do |config|
  config.my_property = "new value"
end
 
puts MyLib.configuration.my_property

Basicamente estamos armazenando um objeto de configuração dentro do nosso módulo.

6 Comentários »

  1. Boa!

    Comentário por Lucas Catón — 13 de abril de 2011 @ 2:39

  2. Acho muito verboso, prefiro o jeito Inploy :P :

    propriedade1 = valor
    propriedade2 = valor

    Comentário por Diego Carrion — 13 de abril de 2011 @ 2:58

  3. Cara, muito legal o conteúdo. Gostei do exemplo quando tu passa uma instancia da classe Configuration pro bloco.

    De fato, é uma prática muito comum em projetos populares.

    Comentário por Matias Leidemer — 13 de abril de 2011 @ 16:49

  4. Ótimo post!! Isso deixa seu código mais legível e organizado. Nunca tinha parado pra fazer algo desse tipo, vai ser muito útil pra mim. Obrigado!

    Comentário por Mário Ramos — 13 de abril de 2011 @ 17:02

  5. Carrion Marketeiro rs!! =)

    Realmente, usar chave valor fica mais clean.

    Acho que a maioria do pessoal que usa esta abordagem que eu citei acima, não gosta muito usar “eval” para setar “coisas”

    Já via muita gente que ao criar DLS em Ruby por exemplo, evita ao máximo usar instance_eval nos blocos.

    Comentário por Rinaldi Fonseca — 13 de abril de 2011 @ 17:34

  6. Muito bom o post, agnt ve isso todo dia em projetos(rubistas), e nem se liga na metaprogramação que tem nisso. Congrats ;P

    Comentário por Adimir Colen — 14 de abril de 2011 @ 6:47

Feed RSS dos comentários deste post URL de TrackBack

Deixe um comentário

Spam protection by WP Captcha-Free