Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to clone a record to a different table? #54

Open
gee-forr opened this issue Aug 16, 2020 · 3 comments
Open

Is it possible to clone a record to a different table? #54

gee-forr opened this issue Aug 16, 2020 · 3 comments
Labels
docs Documentation updates

Comments

@gee-forr
Copy link

Hey there,

I've got a requirement that requires me to keep a point-in-time snapshot of some data whose associations and attributes change over time.

In order to go back to old records and understand the makeup of it at that point in time, we need to make sure that once that record is at a certain stage, it has a snapshot taken of it, and all its associations, and values of all their attributes.

I really don't want to have to do this manually, and would love to use something like Clowne. In order for me to do that though, I'll need to clone my records to their equivalent *Snapshot models instead of the standard models. Here's a non-real-world example in case I'm not making too much sense:

  • A Course has many Users
  • A Course has many CourseModules
  • A User has a Certificate per Course
  • A Certificate has many CourseModule scores

As we either release new modules for a course, or remove old modules, or update existing modules, I don't want a certificate's scores to be affected.

My goal is that once each module is completed, all the data is snapshot from its CourseModule to a CourseModuleSnapshot record. I'd like to probably do a little bit of postprocessing too, but my main goal is to clone the model to a different but similar model.

Is this at all possible?

BTW - thank you evil martians for all the amazing open source you put out ❤️

@ssnickolay
Copy link
Collaborator

Hey @gee-forr .
Hm, you have an interesting case...AFAIU you could try to do something like this:

class Course < ApplicationRecord
  has_many :course_modules

  # other associations
end
 
class CourseSnapshot < Course
  self.table_name = 'course_snapshots'
  
  # foreign key to keep tables equal between each other
  has_many :course_modules, class_name: 'CourseModuleSnapshot', foreign_key: :course_id
  # other associations with overrided class_name & foreign_key options
end

class CourseModule < ApplicationRecord; end

class CourseModuleSnapshot < CourseModule
  self.table_name = 'course_module_snapshots'
end

# Magic here:
class SnapshotAdapter < Clowne::Adapters::ActiveRecord
  class << self 
    def dup_record(record)
      # we cannot mix snapshot and origin models because of https://api.rubyonrails.org/classes/ActiveRecord/AssociationTypeMismatch.html
      # so we override dup logic to always have snap models
      # if you have witelisted models that should be cloned as is 
      # you can `return super if %w(SomeModel).include?(record.class)`
      target_class = "#{record.class.name}Snapshot".constantize

      target_class.new(record.dup.attributes).tap do |clone|
        # you can skip these lines if you dont use after_persist hook (see https://clowne.evilmartians.io/#/after_persist)
        operation = operation_class.current
        operation.add_mapping(record, clone)
      end
    end
  end
end

class CourseSnapshoter < BasePostCloner
  adapter SnapshotAdapter # override default adapter

  include_association :course_modules
end

# and use

CourseSnapshoter.call(Course.last).to_record
# => <# CourseSnapshote ... >

I checked locally with a similar example and everything seems to work

@gee-forr
Copy link
Author

OMG! This is amazing! Thank you so much!

@ssnickolay ssnickolay added the docs Documentation updates label Aug 17, 2020
@bugthing
Copy link

@ssnickolay thanks answering this 2 years back 😆 .. it has partially addressed a problem I am having trying to use clowne.
When I clone an AR model into a different one (as demoed above), I also need to rename an association.. is this possible?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation updates
Projects
None yet
Development

No branches or pull requests

3 participants