Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Ruby
Branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
files
spec
.rspec
Gemfile
Gemfile.lock Migrated tests to RSpec. Added exercises for Day 2.
README
Rakefile

README

Ruby Metaprogramming
====================

Aqui estão os exercícios do workshop de Ruby Metaprogramming.
Para executá-los, você precisará ter o Ruby instalado. Utilize,
preferencialmente, a versão 1.9; afinal, não queremos que o Ruby
se torne o PHP4 pelos próximos 5 anos.

Todos os exercícios possuem testes, que foram divididos por dia de
workshop. O arquivo test_day_1.rb se refere ao dia 1, o arquivo
test_day_2.rb se refere ao dia 2, e assim sucessivamente.

Os testes podem ser executados com o comando

  rake spec

Cada um dos arquivos pode ser executado individualmente com

  rake spec:day_1
  rake spec:day_2
  rake spec:day_3
  rake spec:day_4
  rake spec:day_5

Faça os testes passarem, criando arquivos equivalentes como

  files/day_1.rb
  files/day_2.rb

e assim sucessivamente.

Você pode ver os testes para fazer a sua implementação. As soluções
propostas dos exercícios estão no branch `solution`.

-----------------------------------------------
Atualizando para a última versão dos exercícios
-----------------------------------------------

Você não deve editar nenhum arquivo para fazer os testes passarem.
Apenas adicione as respostas aos arquivos `files/day_*.rb`.

Primeiro, faça um commit com suas respostas. Execute os comandos abaixo:

  git add files
  git commit -m "Uma mensagem de commit"

Depois, para aplicar as últimas atualizações dos exercícios à sua cópia local,
execute o comando abaixo à partir da raíz do diretório dos exercícios.

  git pull --rebase

Rode os testes e corrija sua implementação caso algum deles falhe.

--------------------------
Instalando as dependências
--------------------------

Para instalar as dependências, instale o
Bundler (http://rubygems.org/gems/bundler) e execute o comando

  bundle install

-------------------------------------
Dia 1: self, classe Singleton e Class
-------------------------------------

1.      O Ruby 1.9 implementa o método BasicObject#singleton_class,
        que retorna a classe Singleton de um objeto. Adicione o método
        Object#metaclass de modo que ele faça a mesma coisa.



2.      Defina um módulo que implemente os métodos App.description e App.description= utilizando
        a classe Singleton.



3.      Implemente um método no namespace global chamado new_class, que deve retornar
        uma classe criada dinamicamente. Esta classe deve herdar de uma outra classe
        chamada Person. O comportamente seria semelhante a

          class SomeClass < Person
          end



4.      Crie uma nova classe dinâmica, cujo nome deve ser "Awesome".



5.      Crie um método no namespace global chamado new_method, que deve adicionar
        um método chamado hello à instância do objeto que ele receberá como argumento.
        Este método hello deve retornar uma string como "Hello from <CLASSE> instance".
        A string <CLASSE> deve retornar o nome da classe do objeto.

        Você deve utilizar apenas o receiver; não use os métodos instance_eval, class_eval,
        eval ou define_method.

        A assinatura deste método é

          def new_method(object)
          end



--------------
Dia 2: Methods
--------------

1.      Crie um método no namespace global chamado send_message. Este método deverá
        instanciar a classe Message e executar o método Message#deliver dinamicamente.

        Esta classe é definida pelo teste.

        A assinatura deste método é

          def send_message
          end



2.      Crie um método no namespace global chamado send_message_with_args. Este método deverá
        executar dinamicamente o método Message.send, que sobrescreveu a implementação
        original.

        O método Message.send espera dois argumentos; uma instância da classe Message e um
        número inteiro de 1 a 3, que indica a prioridade da mensagem.

        A assinatura deste método é

          def send_message_with_args
          end



3.      Crie um método no namespace global chamado send_private_method. Este método deverá
        executar dinamicamente o método privado Message#prepare.

        A assinatura deste método é

          def send_private_method
          end



4.      Crie um método no namespace global chamado send_public_method. Este método deverá
        executar dinamicamente apenas métodos públicos; você deve executar o método
        Message#prepare e esta chamada deverá lançar a exceção `NoMethodError`.

        A assinatura deste método é

          def send_public_method
          end



5.      Crie uma classe chamada Song, que possui 3 atributos:

        - title
        - artist
        - duration

        O método construtor desta classe pode receber um hash e cada um dos itens
        desse hash deve ser definir o atributo de mesmo nome.

        Um exemplo de uso desta classe pode ser

          Song.new(
            :title => "Linoleum",
            :artist => "NOFX",
            :duration => "2:10"
          )



6.      Adicione um método String#to_leet, que irá converter uma string para sua versão
        Leet (http://en.wikipedia.org/wiki/Leet).
        Utilize o mapeamento abaixo para substituir cada um dos caracteres pelo seu equivalente.

          LEET = {
            "a" => "4",
            "e" => "3",
            "i" => "1",
            "o" => "0",
            "u" => "μ"
          }

        Ao executar `"banana".to_leet` devemos receber como resultado a string `b4n4n4`.



7.      Adicione o método Object.leet_attr, que permitirá criar atributos como os
        exemplos abaixo.

          class Person
            leet_attr :name, :blog, :twitter
          end

        seria o equivalente a definir os atributos como

          class Person
            attr_accessor :name, :blog, :twitter

            def n4me
              name.to_s.to_leet
            end

            def bl0g
              blog.to_s.to_leet
            end

            def tw1tt3r
              twitter.to_s.to_leet
            end
          end

        Você deve utilizar o método define_method para adicionar o método que retorna a string em
        leet.



8.      Semelhante ao exercício anterior, adicione o método Object.reverse_attr, que permitirá criar
        atributos como os exemplos abaixo.

          class Person
            reverse_attr :name, :blog, :twitter
          end

        seria o equivalente a definir os atributos como

          class Person
            attr_accessor :name, :blog, :twitter

            def eman
              name.to_s.reverse
            end

            def golb
              blog.to_s.reverse
            end

            def rettiwt
              twitter.to_s.reverse
            end
          end

        Você deve utilizar o método class_eval para adicionar o método que retorna a string reversa.



9.      Remova o método Car.useless, já que ele não faz nada.

        A classe Car foi definida pelo teste.



10.     A classe Ferrari, que herda da classe Car, implementa o método color, sobrescrevendo a
        implementação original.
        Remova o método Ferrari#color de modo que o retorno seja a implementação original.



11.     Assim como o exercício anterior, a classe Ferrari também implementa o método Ferrari#engine.
        Remova o método Ferrari#engine de qualquer chamada ao método, mesmo que presente em uma
        superclasse, lance a exceção NoMethodError.



12.     O método Calc.sum, implementado pelo teste, soma dois números. No entanto, este método lança
        uma exceção se passando um valor nil. Redefina este método convertendo qualquer valor não
        numérico 0 antes de enviar estes números para a implementação original.

        Utilize o método define_method em conjunto com os métodos instance_method e bind.



----------------------------------------
Dia 3: Code evaluation, Modules & Mixins
----------------------------------------

1.      Adicione dinamicamente um novo método de instância usando o método Module.class_eval.
        Este método deve receber uma string e retornar uma versão em letras maiúsculas.
        Você deve definir o nome do arquivo e linha onde esse método foi criado.

        A assinatura deste método é

          module Helper
            def upcase(string)
            end
          end



2.      Crie um método chamado extend_object que deve receber um objeto e um módulo. Este módulo
        deve ser usado para estender o objeto.

        A assinatura deste método é

          def extend_object(object, mod)
          end



3.      Adicione dinamicamente um novo método de classe no módulo Helper usando o método
        Module.class_eval e a classe Singleton. Este método deve receber uma string e retornar uma
        versão em letras minúsculas.

        A assinatura deste método é

          module Helper
            def self.downcase(string)
            end
          end



4.      Crie um método chamado execute_block que deve receber um objeto e um bloco.
        Este bloco deve ser executado no contexto do objeto. Qualquer objeto pode ser passado
        para este método: classes, módulos, instâncias de classes...

        A assinatura deste método é

          def execute_block(object, block)
          end



5.    Crie um método chamado execute_private que irá executar o método privado Tools.prepare sem
      utilizar o método send.

      A assinatura deste método é

        def execute_private
        end

        module Tools
          private

          def self.prepare
          end
        end



6.    Crie um módulo Multipler que implementa um único método multiply. Este método deve multiplicar
      cada um dos itens de um array por um número.

      A assinatura deste método é

        module Multiplier
          def multiply(multiplier)
          end
        end

      Se eu injetar este módulo na classe Array, posso utilizá-lo como

        [1,2,3].multiply(2)

      e o resultado será

        [2,4,6]



------------------------------------------
Dia 4: Constantes, Callable Object e Hooks
------------------------------------------

1.    Crie um método set_constant que receberá um símbolo. Este símbolo deverá ser normalizado como
      uma constante e adicionando ao namespace Object.

      A assinatura do método é

        def set_constant(name, value)
        end

      Uma chamada como

        set_constant(:my_const, 1)

      Deverá criar a constante MY_CONST com o valor 1. Se eu tentar atribuir esta constante com o
      mesmo valor, o retorno do método deverá ser false.

      Se eu tentar atribuir com um valor diferente, a constante deverá receber este novo valor, sem
      lançar nenhuma mensagem de warning.



2.    Crie um mapeamento para o hash abaixo, onde a chave é o nome do método. Caso uma chave
      inexistente seja passada, a exceção NoMethodError deverá ser lançada.

        module Color
          COLORS = {
            :red => "ff0000",
            :blue => "0000ff",
            :green => "00ff00",
            :black => "000000",
            :white => "ffffff"
          }
        end

      Os métodos deverão estar disponíveis como sendo de classe.

        Color.red
        #=> "ff0000"

      Lembre-se de atualizar o método respond_to?.



3.    Rastreie todas as subclasses que herdaram da classe Monster. Os nomes destas subclasses devem
      estar disponíveis através do método Monster.ugly_monsters.



4.    Toda vez que um método de instância contendo a palavra "fuck" for adicionado à classe Monster,
      lance a exceção Monster::NoBadWordsError.



5.    Implemente a DSL à seguir.

        car = Car.new do
          price 225_000
          name "Ferrari 458 Italia"
          wheels 4
          color :red
          brand "Ferrari"
          maximum_speed 321
        end

        car.name
        #=> "Ferrari 458 Italia"

        car.respond_to?(:doors)
        #=> false

        car.doors = 2

        car.respond_to?(:doors)
        #=> true

        car.doors
        #=> 2

      A DSL acima não deve possuir nenhum atributo pré-definido. Você deve interceptar o hook
      method_missing e adicionar cada um dos atributos em tempo de execução.
Something went wrong with that request. Please try again.