Fuentes Vinícius Baggio - Ruby on Rails: coloque sua aplicação web nos trilhos стр 17.

Шрифт
Фон

nele. Quando fazemos include de um módulo dentro de uma classe, seus métodos

são misturados, e portanto temos acesso como se o método estivesse implementado

diretamente na classe. O mesmo acontece com os métodos do módulo. Como a veri-

ficação da existência de métodos é apenas em tempo de execução, este contrato de

implementação não é verificado pelo interpretador, simplificando, portanto, a im-

plementação.

Uma característica importante é que, quando fazemos um mixin, o módulo acaba

se tornando um ancestral da classe que inclui este módulo. Isso nos dá uma série de

62

Casa do Código

Capítulo 2. Conhecendo Ruby

vantagens, tal como a verificação de tipos:

shipping.is_a? Shipping # true

O mesmo pode ser feito com módulos como Enumerable. Esta é uma forma de

garantir que um objeto responde a uma série de métodos de maneira uniforme.

Mixins com extend

Existe uma outra forma de fazer mixins, com a palavra-chave extend. A dife-

rença é que os métodos são incluídos a nível de classe, e não mais de instância:

module Builder

def build(attributes={})

new_object = new

attributes.each do |name, value|

# O código abaixo é o mesmo que

# new_object.name = value

new_object.send "#{name}=", value

end

new_object

end

end

class ShippingPrice

extend Builder

attr_accessor :width, :height, :depth

end

shipping = ShippingPrice.build({

:width => 0.8,

:height => 0.2,

:depth => 0.3

})

shipping.width # 0.8

O código anterior usa um pouco do que chamamos em Ruby de meta-

programação, ou seja, programação para gerar código. O módulo Builder basica-

mente cria um construtor em que um Hash é convertido para atributos de um objeto

63

2.6. Classes e módulos

Casa do Código

via métodos de escrita (métodos terminados com =). Usamos então o extend para

misturar a funcionalidade de construção ao nível da classe.

Meta-programação

Meta-programação é uma funcionalidade importante para quem escreve

bibliotecas e sistemas avançados. Como este é um livro para quem está

começando, meta-programação pode dar um nó na cabeça e portanto

não vamos ver muito mais detalhes sobre o assunto.

Porém, se você se sente um programador aventureiro, recomendo a lei-

tura do Metaprogramming Ruby [8], um livro excelente para quem quer

iniciar nas artes às vezes misteriosas da meta-programação.

Exceções

O controle de exceções em Ruby acontece em 3 fases. Primeiro, um bloco de

código, iniciado pela cláusula begin é executado. Em seguida, caso alguma exceção

aconteça (via erros de sintaxe Ruby ou pela cláusula raise), o interpretador Ruby vai

buscar em todas as possíveis cláusulas rescue qual é pertinente para o tratamento

daquela exceção. Isso é feito através da comparação de classes, ou sua hierarquia. E,

por fim, se existir, o bloco ensure é executado, independente da ocorrência ou não

de uma exceção.

Sim, muita coisa pode acontecer em um pedaço pequeno de código, então va-

mos com calma para entender cada passo. Primeiro, vejamos como tratar exceções

genericamente.

def calculate_installment_price(total_value, installments)

begin

puts "O resultado é #{total_value / installments}"

rescue

puts "Não foi possível calcular o valor da parcela"

end

end

calculate_installment_price(100, 5) # O resultado é 20.0

64

Casa do Código

Capítulo 2. Conhecendo Ruby

calculate_installment_price(100, 0) # Não foi possível calcular

# o valor da parcela

Neste caso, quando passamos um valor inválido para o número de parcelas (ins-

tallments), uma exceção é disparada, e a execução é interrompida, levando a exe-

cução do programa para o bloco associado ao rescue. Por essa razão, a impressão

do texto "O resultado é ... não é executada. É importante notar que nesse caso não

importa qual exceção seja disparada, o bloco rescue será executado.

É possível, porém, associar um bloco rescue diretamente a uma classe. Dessa

forma, dependendo do erro que ocorrer no bloco associado, podemos tratá-lo de

maneira diferente. Vamos usar essa ideia para mostrar um erro diferente para o

usuário:

def calculate_installment_price(total_value, installments)

begin

puts "O resultado é #{total_value / installments}"

rescue ZeroDivisionError

puts "Número de parcelas deve ser > 0"

rescue

puts "Não foi possível calcular o valor da parcela"

end

end

calculate_installment_price(100, 5) # O resultado é 20.0

calculate_installment_price(100, 0) # Número de parcelas

# deve ser > 0

calculate_installment_price("", 0) # Não foi possível calcular

# o valor da parcela

Dessa forma, quando enviamos 0 como número de parcelas, a exceção ge-

rada pelo interpretador é a ZeroDivisionError, que é tratada pelo primeiro bloco

rescue. No segundo caso, porém, este bloco não corresponde à exceção gerada

(NoMethodError), portanto o último bloco é executado.

É bastante comum termos que garantir que algo aconteça mesmo que haja erro

durante um processo, como liberar recursos. Por isso, temos o bloco ensure. O bloco

ensure sempre será executado, independente da ocorrência de exceções. Inclusive,

Ваша оценка очень важна

0
Шрифт
Фон

Помогите Вашим друзьям узнать о библиотеке