Coerção implícita de Ruby

Vamos imaginar este cenário: seu sistema de faturamento possui uma InvoiceValueclasse que possui n entradas, uma para cada produto adquirido.

Seu InvoiceValue::Entry, expõe um método subtotalque é produto de preço e quantidade.

def subtotal
price
* quantity
end

Assim, para calcular o InvoiceValuetotal que você usaria:

def total
@entries.inject(0) {|result, e| result += e.subtotal }
end

O que é um pouco estranho: se você imaginar InvoiceValue::Entrycomo um valor puro, deve ser fácil de ser usado diretamente nas expressões matemáticas, como +=.

A primeira coisa que você é tentado a fazer é sobrescrever Numeric#+, a fim de lidar com esse caso especial, mas isso polui muito o comportamento desse método.

Felizmente, Ruby é uma linguagem tão bonita, que permite coerção implícita entre os tipos. Tudo que você precisa fazer é implementar um método:

protected
def coerce(other)
[other, subtotal]
end

Existem algumas coisas que precisam de explicação. Em primeiro lugar, #coercerequer o retorno de um array com dois elementos: o objeto recebido como argumento e o valor coagido do mesmo tipo.

O otherhere representa o outro objeto contra o qual estamos tentando lançar (neste caso, a Fixnum) e subtotalé o valor que queremos usar nas expressões aritméticas.

Isso simplifica muito a nossa implementação:

def total
@entries.inject(0, :+)
end