Pergunta

Eu ainda sou novo para Ruby e basicamente apenas escrevendo meu primeiro micro-programa depois de terminar o livro de Cooper. Eu estava apontado para a direção de evitar monkey patch, mas o problema é que eu não sei quais são as alternativas para alcançar o mesmo comportamento. Basicamente, eu quero adicionar um novo método que é acessível por todos os objetos string. O caminho-remendar macaco óbvia é:

class String
  def do_magic
    ...magic...
  end
end

Lembro-me de que há uma maneira usando String.send. Mas eu não me lembro como ele é feito, nem onde eu lê-lo. Pode apontar ninguém de fora quaisquer alternativas que ainda me deixava fazer esse método disponível para os objetos da classe String e criança?

Foi útil?

Solução

Qualquer outra maneira de fazer isso seria apenas uma sintaxe mais difícil para patching macaco. Existem maneiras que envolvem send e eval e todos os tipos de coisas, mas por ? Vá em frente e fazê-lo da maneira óbvia.

Você quer ser cuidadoso de patching macaco em grandes projetos ou quando você tem dependências, porque você pode acabar com conflitos quando várias mãos estão todos brincando no mesmo lugar. Isso faz olhar não média para uma sintaxe alternativa que realiza a mesma coisa - isso significa que ter cuidado quando você está fazendo mudanças que poderiam afetar o código que não é sua. Isso provavelmente não é uma preocupação no seu caso particular. É apenas algo que necessidade poder ser abordadas em projetos maiores.

Uma alternativa em Ruby é que você pode adicionar métodos a um único objeto.

a = "Hello"
b = "Goodbye"
class <<a
  def to_slang
    "yo"
  end
end
a.to_slang # => "yo"
b.to_slang # NoMethodError: undefined method `to_slang' for "Goodbye":String

Outras dicas

Se você quiser adicionar um novo método que seja acessível a todos os objetos corda, em seguida, fazê-lo da maneira que você tem é como fazê-lo.

Uma boa prática é colocar suas extensões objetos centrais em um arquivo separado (como string_ex.rb) ou um sub-diretório (como extensions ou core_ext). Desta forma, é óbvio que foi estendido, e é fácil para alguém para ver como eles têm sido alargado ou alterado.

Onde macaco patching pode ir ruim é quando você mudar algum comportamento existente de um objeto de núcleo que faz com que algum outro código que espera que a funcionalidade original portar-se mal.

A classe object define send, e todos os objetos herdam essa. Você "enviar" um objeto do método send. Os parâmetros do método send são o método-você-quer-a-invocam o nome de um símbolo, seguido por quaisquer argumentos e um bloco opcional. Você também pode usar __send__.

>> "heya".send :reverse
=> "ayeh"

>> space = %w( moon star sun galaxy )
>> space.send(:collect) { |el| el.send :upcase! }
=> ["MOON", "STAR", "SUN", "GALAXY"]

Editar ..

Você provavelmente vai querer usar o método define_method:

String.class_eval {
  define_method :hello do |name|
    self.gsub(/(\w+)/,'hello') + " #{name}"
  end
}

puts "Once upon a time".hello("Dylan")
# >> hello hello hello hello Dylan

Isso adiciona métodos de instância. Para adicionar métodos de classe:

eigenclass = class << String; self; end
eigenclass.class_eval {
  define_method :hello do
    "hello"
  end
}

puts String.hello
# >> hello

Você não pode definir métodos que esperam um bloco embora.

Pode ser uma boa coisa para ter uma leitura de este capítulo do Guia do Por Poignant , você pode pular até "array de Dwemthy" para chegar ao material de meta-programação.

Obrigado rapazes.

Todo o trabalho de implementação sugerido. Mais importante, eu aprendi a pesar no caso em questão e decidir se núcleo re-abertura (ou biblioteca) aulas é uma boa idéia ou não.

FWIW, um amigo apontou a implementação send que eu estava procurando. Mas agora que eu olhar para ele, é ainda mais perto de monkeypatching do que todas as outras implementações:)

module M
    def do_magic
    ....
    end
end
String.send(:include, M)

Como uma alternativa para anexar funções para classes ou objetos, você pode sempre ir a rota funcional:

class StringMagic
  def self.do(string)
     ...
  end
end

StringMagic.do("I'm a String.") # => "I'm a MAGIC String!"

O "monkey patch" você descreve poderia realmente ser um problema se alguém quer para exigir o seu código (como uma jóia, por exemplo). Quem pode dizer que eles também não vai querer adicionar um método String que é chamado do_magic? Um método irá substituir o outro, e isso pode ser um desafio para depurar. Se há alguma chance de seu código será open source, então é melhor para criar sua própria classe:

class MyString < String
  def initialize(str)
    @str = str
  end
  def do_magic
    ...magic done on @str
    @str
  end
end

Agora, se você precisa do_magic você pode

magic_str = MyString.new(str).do_magic
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top