How do I get the raw pre-deserialized value of a model?
Asked Answered
O

2

6

I have an encrypted type in my model

attribute :name, :encrypted

Which is

class EncryptedType < ActiveRecord::Type::Text

And implements #serialize, #deserialize, and #changed_in_place?.

How can I get the raw value from the database before deserialization?

I want to create a rake task to encrypt values which are in the DB that existed before the fields were encrypted. So before the encryption, the name field contained Bob. After the code change with encryption, reading that value will produce an error (caught), returning an empty string. I want to read the raw value and set it like a normal attribute so it will encrypt it. After the encryption, the field will look like UD8yDrrXYEJXWrZGUGCCQpIAUCjoXCyKOsplsccnkNc=.

I want something like user.name_raw or user.raw_attributes[:name].

Orphanage answered 30/7, 2017 at 2:51 Comment(2)
What about defining a method called unencrypted that process the encrypted attribute? So you can refer to this method instead of the original attribute. This looks more cohesive, once the encrypt name means the data will be encrypted.Coycoyle
This is also pretty useful when printing attributes with YAML. y(instance.attributes_before_type_cast) is way better readable than y(instance.attributes).Blip
L
15

There's ActiveRecord::AttributeMethods::BeforeTypeCast which

provides a way to read the value of the attributes before typecasting and deserialization

and has read_attribute_before_type_cast and attributes_before_type_cast. Additionally,

it declares a method for all attributes with the *_before_type_cast suffix

So for instance:

User.last.created_at_before_type_cast # => "2017-07-29 23:31:10.862924"
User.last.created_at_before_type_cast.class # => String
User.last.created_at # => Sat, 29 Jul 2017 23:31:10 UTC +00:00
User.last.created_at.class # => ActiveSupport::TimeWithZone
User.last.attributes_before_type_cast # => all attributes before type casting and such

I imagine this would work with your custom encrypted type

Leyden answered 30/7, 2017 at 3:11 Comment(0)
O
0

As SimpleLime suggested...

namespace :encrypt do
  desc "Encrypt the unencrypted values in database"
  task encrypt_old_values: :environment do
    User.all.each do |user|
      if user.name.blank? && ! user.name_before_type_cast.blank?
        User.class_variable_get(:@@encrypted_fields).each do |att|
          user.assign_attributes att => user.attributes_before_type_cast[att.to_s]
        end
        user.save validate: false
      end
    end
Orphanage answered 30/7, 2017 at 4:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.