Página Inicial > Ruby > Obtendo dados originais de atributos modificados no ActiveRecord

Obtendo dados originais de atributos modificados no ActiveRecord

Como todo bom framework de Mapeamento Objeto-Relacional (ORM), o ActiveRecord do Ruby on Rails mantém os dados originais dos atributos que são alterados. Com isso, você obter esses valores antes de salvar os novos.

Vamos ver alguns métodos que lhe ajudam a saber quais atributos foram alterados, bem como seus valores novos e originais.

Como exemplo usaremos um classe de modelo chamada Order com os seguintes atributos:

Imaginando que já temos pedidos cadastrados no banco de dados, através do console, vamos recuperar um pedido e atribuí-lo a uma variável chamada order:
> order = Order.first
=> #<Order id: 8567520, user_id: 506715721, number: "R222442710", total: #<BigDecimal:4607440,'0.3498E2',8(8)>, state: "new", completed_at: "2010-08-07 17:24:37">
> y order
--- !ruby/object:Order
attributes:
id: "8567520"
number: R222442710
total: "34.98"
state: new
completed_at: 2010-08-07 17:24:37
user_id: "506715721"
attributes_cache: {}
=> nil

Através do método changed?, sabemos se houve alguma modificação nessa instância de Order:
> order.changed?
=> false

Vamos modificar os atributos number e state:
> order.number = "NEW012345"
=> "NEW012345"
> order.state = "in_progress"
=> "in_progress"
> order.changed?
=> true

Agora o método changed? retorna true, mas não informa quais atributos foram alterados. Para obter uma lista de todos atributos alterados, seus valores originais e seus valores novos, usamos o método changes:
> order.changes
=> {"number"=>["R222442710", "NEW012345"], “state”=>["new", "in_progress"]}

O retorno é um hash onde as chaves são o nome dos atributos alterados e os valores são um array com dois itens: o valor original do atributo e o novo valor do atributo.

Por exemplo, você poderia fazer isso para recuperar o valor original do atributo state:
> order.changes["state"][0]
=> “new”

Existe também o método privado changed_attributes que retorna uma hash contendo somente os valores originais dos atributos modificados. Temos acesso a ele usamos o método Object#send:
> order.send :changed_attributes
=> {"number"=>"R222442710", "state"=>"new"}

E o método (também privado) attribute_was, que recebe como parâmetro o nome de um atributo como string:
> order.send :attribute_was, "state"
=> "new"

Então agora você está pensando em tornar esse último método público? Esqueça! O ActiveRecord já fez melhor.

O módulo ActiveRecord::AttributesMethods declara para cada atributo do modelo os métodos existentes na classe em que seu nome se inicia com “attribute”. Vamos listar os métodos privados da nossa instância da classe Order que atendem esse padrão:
> y order.private_methods.grep /^attribute_/
---
- attribute_change
- attribute_before_type_cast
- attribute_changed?
- attribute_was
- attribute_will_change!
=> nil

Para cada método acima, existe seu correspondente por atributo no modelo. Basta substituir “attribute” pelo nome do atributo. Veja os exemplos com o atributo state na classe Order:
> order.state
=> "in_progress"
> order.state_changed?
=> true
> order.state_change
=> ["new", "in_progress"]
> order.state_was
=> “new”

Você pode criar novos métodos que ficam disponíveis para todos os atributos. Basta registrar o sufixo do método na classe modelo usando o método de classe attribute_method_suffix e criar um método privado com o padrão attribute_[sufixo] que recebe como parâmetro o nome do atributo.

Com um exemplo fica mais fácil de entender:

class Order < ActiveRecord::Base
  attribute_method_suffix "_cool?"

  private
  def attribute_cool?(attr)
    puts "Yes, #{attr} with value #{read_attribute(attr)} is cool!"
  end
end

> order.total_cool?
Yes, total with value 34.98 is cool!
=> nil

No final das contas, o que você precisa fazer para obter o valor original de um atributo modificado é chamar o método [nome_do_atributo]_was:
> order.number_was
=> "R222442710"
> order.state_was
=> "new"

Lembrando que depois que salvamos as alterações no banco de dados, todo esse estado de alterações é removido:
> order.save
=> true
> order.changed?
=> false
> order.changes
=> {}


Ruby , , , ,

  1. Nenhum comentário ainda.
  1. Nenhum trackback ainda.