Dúvida em Ruby on Rails.. bug ou lambança minha?

Desculpem, eu sei que não é o fórum mais apropriado, mas como existem aqui vários usuários do Rails, inclusive profissionais, e como estamos na seção “Off-topic”, além de não haver nada nas regras proibindo… resolvi tentar (e desde ontem que tento e não consigo me cadastrar no RubyOnBr… o captcha não aparece).

Observem o seguinte trecho de código.

[code]class Pedido < ActiveRecord::Base
belongs_to :pagamento
belongs_to :usuario
has_many :locacoes_midia, :dependent => :destroy
has_many :locacoes_ppv, :dependent => :destroy

validates_presence_of :data, :preco, :endereco, :pagamento, :usuario, :message => “deve ser preenchido”
validates_associated :usuario, :pagamento, :message => “deve ser válido”
end

class PedidosController < ApplicationController
def create
redirect_to :action => ‘show’ if session[:pedido].nil?
@pedido = session[:pedido]

@pagamento = Pagamento.new(params[:pagamento]) do |p|
  p.data = DateTime.now
  p.valor = @pedido.preco
end

@pedido.data = DateTime.now
@pedido.pagamento = @pagamento

if @pedido.save
  flash[:notice] = 'Pedido confirmado com sucesso!'
  session[:pedido] = nil
  redirect_to :action => 'confirmacao', :id => @pedido
else
  render :action => 'fechar'
end

[…]
end[/code]

Funciona perfeitamente. Quando o pagamento não é preenchido, o erro é mostrado e tudo corre normalmente.

Mas deixando a coisa um pouco mais complexa:

[code]class Pedido < ActiveRecord::Base
belongs_to :pagamento
belongs_to :usuario
has_many :locacoes_midia, :dependent => :destroy
has_many :locacoes_ppv, :dependent => :destroy

validates_presence_of :data, :preco, :endereco, :pagamento, :usuario, :message => “deve ser preenchido”
validates_associated :usuario, :pagamento, :message => “deve ser válido”

def save
copias = []
self.locacoes_midia.each { |locacao| copias.push(locacao.copia) }

Copia.transaction(copias) do
  Pedido.transaction(self) do
    if !super
      raise "Houve um erro ao confirmar o pedido"
    end
    
    for copia in copias
      raise "Cópia indisponível" if copia.disponivel == false
      copia.disponivel = false
      copia.save
    end
  end
end

end
end

class PedidosController < ApplicationController
def create
redirect_to :action => ‘show’ if session[:pedido].nil?
@pedido = session[:pedido]

@pagamento = Pagamento.new(params[:pagamento]) do |p|
  p.data = DateTime.now
  p.valor = @pedido.preco
end

@pedido.data = DateTime.now
@pedido.pagamento = @pagamento

begin
  @pedido.save
  flash[:notice] = 'Pedido confirmado com sucesso!'
  session[:pedido] = nil
  redirect_to :action => 'confirmacao', :id => @pedido
rescue RuntimeError => error
  render :action => 'fechar'
end

end
[…]
end[/code]

Ocorre um comportamento muito estranho: quando o pagamento é deixado em branco numa primeira tentativa, o erro pego e é mostrado corretamente. Porém, numa tentativa seguinte de fazer o salvamento do pedido, acontece um erro numa das foreign keys: o rails tenta inserir um dos objetos relacionados a “pedido” usando 0 na chave estrangeira, ao invés do id do pedido recém salvo. Não faz sentido.

(fora de tags de codigo pra não estourar horizontalmente)
ActiveRecord::StatementInvalid (Mysql::Error: #23000Cannot add or update a child row: a foreign key constraint fails (locadora_development/locacoes_midia, CONSTRAINT copia_pedido FOREIGN KEY (pedido_id) REFERENCES pedidos (id)): INSERT INTO locacoes_midia (devolucao_id, data_devolucao, multa, copia_id, preco, pedido_id) VALUES(NULL, ‘2006-09-04T21:31:21-0300’, 0.25, 50, 1.5, 0)):

Observem: esse erro ocorre apenas quando na primeira tentativa o pagamento é deixado em branco. Se for preenchido corretamente na primeira tentativa, o pedido é salvo corretamente.

Enfim… eu acho estranho colocar apenas o @pedido.save dentro da transação… será que tem a ver com isso?

Em segundo lugar: colocar apenas o laço que marca as cópias como indisponíveis dentro da transação e fazer @pedido.destroy caso a transação falhe… é uma solução… mas é uma gambiarra, não?