VegaStudios
VegaStudios

Reputation: 324

Rails Copying attributes from User object to new object

In the User model I have an archive! method that is called when a User is destroyed. This action creates a new ArchivedUser in separate table.

The ArchivedUser is successfully created, but the way I am manually setting each value is pretty dirty; if a new column is added to the User table it must be added here as well.

I tried to select and slice the attributes, but got undefined local variable or methoduser'`

ArchivedUser.create(user.attributes.select{ |key, _| ArchivedUser.attribute_names.include? key })

ArchivedUser.create(user.attributes.slice(ArchivedUser.attribute_names))

How can I iterate through each attribute in the User table when creating an ArchivedUser with self?

def archive!
    if ArchivedUser.create(
        user_id: self.id,
        company_id: self.company_id,
        first_name: self.first_name,
        last_name: self.last_name,
        email: self.email,
        encrypted_password: self.encrypted_password,
        password_salt: self.password_salt,
        session_token: self.session_token,
        perishable_token: self.perishable_token,
        role: self.role,
        score: self.score,
        created_at: self.created_at,
        updated_at: self.updated_at,
        api_key: self.api_key,
        device_id: self.device_id,
        time_zone: self.time_zone,
        device_type: self.device_type,
        verified_at: self.verified_at,
        verification_key: self.verification_key,
        uninstalled: self.uninstalled,
        device_details: self.device_details,
        is_archived: self.is_archived,
        registered_at: self.registered_at,
        logged_in_at: self.logged_in_at,
        state: self.state,
        creation_state: self.creation_state,
        language_id: self.language_id,
        offer_count: self.offer_count,
        expired_device_id: self.expired_device_id,
        unique_id: self.unique_id,
        best_language_code: self.best_language_code,
        offer_id: self.offer_id,
        vetted_state: self.vetted_state,
        photo_path: self.photo_path
      )

      self.is_archived = true

      self.email = "#{self.email}.archived#{Time.now.to_i}"
      self.encrypted_password = nil
      self.password_salt      = nil
      self.session_token      = nil
      self.perishable_token   = nil
      self.device_id          = nil
      self.verification_key   = nil
      self.save!

      self.update_column(:api_key, nil)
      UserGroup.delete_all(:user_id => self.id)
    else
      # handle the ArchivedUser not being created properly
    end
  end

Thanks for viewing :)

Update:

We were able to figure out the reasons why ArchivedUser.create(self.attributes.slice!(ArchivedUser.attribute_names) wasn't working. The first reason is the create method requires "bang" to write the object. The second reason was that ArchivedUser has a user_id field, that User doesn't receive until after create. We have to set the user_id: manually with merge(user_id: self.id)

The final output looks like ArchivedUser.create!(self.attributes.slice!(ArchivedUser.attribute_names).merge(user_id: self.id))

Upvotes: 1

Views: 1158

Answers (2)

chrisgeeq
chrisgeeq

Reputation: 149

If you just want to have a copy of the user that is being archived, I think an elegant way would be to do

archived_user = user_to_be_archived.dup

or you can take a look at the amoeba gem, this will do all the heavy lifting including associations if you want. https://github.com/amoeba-rb/amoeba

Upvotes: 0

Ehren Murdick
Ehren Murdick

Reputation: 436

You were on the right track with the first implementation. You just have to use self instead of user.

ArchivedUser.create(self.attributes.slice(ArchivedUser.attribute_names))

Upvotes: 1

Related Questions