PaperTrail-AssociationTracking

repository·master·Indexed 41 days ago

https://github.com/westonganger/paper_trail-association_tracking

A plugin for the PaperTrail gem that enables tracking and reifying (restoring) ActiveRecord associations, including Has-One, Has-Many, Has-Many-Through, and Has-And-Belongs-To-Many (HABTM) relationships.

Tokens
2.4K
Snippets
12
Records
13
Agent score
50%

What's inside PaperTrail-AssociationTracking

  1. Limitations of PaperTrail-AssociationTracking

    master

    Users should be aware of the following constraints:

    1. Single Level Only: The gem only reifies the first level of associations. To include nested associations, you must use :through relationships.
    2. Single Table: Currently, only one version_associations table can be used to store versions for all related models.
    3. Callback Dependency: Reification relies on ActiveRecord callbacks on the association model (and the :through model for Has-Many-Through). If associations are changed without triggering these callbacks (e.g., using certain direct ID assignments), reification will not work correctly.
    4. Test Compatibility: The gem is not compatible with transactional tests. You may need to use the transactional_capybara gem to mitigate this.
  2. How to save reified associations automatically

    master

    By default, reifying an association does not automatically save the associated records to the database. To ensure that calling .save on the parent model also saves the reified associations, you must set autosave: true on those associations in your model definition.

    Example with autosave:

    class Product
      has_many :photos, autosave: true
    end
    
    # Reifying and saving will now persist the photos
    product = Product.first.versions.last.reify(has_many: true, has_one: true)
    product.save!

    Example without autosave (Manual handling): If autosave is false (the default), you must manually handle the saving or destruction of reified records, especially if you use mark_for_destruction: true.

    class Product < ActiveRecord::Base
      has_paper_trail
      has_many :photos, autosave: false
    end
    
    # ... after reifying ...
    reified_product = product.versions.last.reify(has_many: true, mark_for_destruction: true)
    reified_product.save!
    
    # Manually process marked records
    reified_product.photos.each do |photo| 
      photo.marked_for_destruction? ? photo.destroy! : photo.save! 
    end
    class Product
      has_many :photos, autosave: true
    end
    
    product = Product.first.versions.last.reify(has_many: true, has_one: true)
    product.save! # Saves reified photos automatically
  3. Install PaperTrail-AssociationTracking

    master

    To install the gem, add both paper_trail and paper_trail-association_tracking to your Gemfile:

    gem 'paper_trail'
    gem 'paper_trail-association_tracking'

    After installing the gems, run the following Rails generator to create the necessary version_associations table and enable association tracking in the PaperTrail configuration:

    rails generate paper_trail_association_tracking:install
    # Gemfile
    gem 'paper_trail'
    gem 'paper_trail-association_tracking'
    rails generate paper_trail_association_tracking:install
  4. Configure custom version association tables

    master

    If you need to use a different table for version associations (for example, to separate tracking for different models), you can define a custom class inheriting from PaperTrail::VersionAssociation and specify it in the model's has_paper_trail call.

    Example:

    class ProductionVersionAssociation < PaperTrail::VersionAssociation
      self.table_name = "product_version_associations"
    end
    
    class Product < ActiveRecord::Base
      has_paper_trail version_associations: { class_name: "ProductionVersionAssociation" }
    end
  5. Install PaperTrail-AssociationTracking via Rails generator

    master

    Run the Rails generator to set up the necessary migrations and configuration for association tracking. This generator creates two migrations: one to create the version_associations table and another to add a transaction_id column to the versions table. It also creates a config/initializers/paper_trail.rb file with default settings.

    rails generate paper_trail_association_tracking:install
  6. Generate migration to add foreign_type to version_associations

    master

    If you are upgrading from paper_trail-association_tracking version 1.0 or earlier, you must add the foreign_type column to your version_associations table. You can use the Rails generator to create the necessary migration file. This generator creates the migration but does not run it automatically.

    rails generate paper_trail_association_tracking:add_foreign_type_to_version_associations
  7. Handle HasOne reification errors

    master

    When reifying a has_one association, the gem may find multiple candidates and raise a PaperTrailAssociationTracking::Reifiers::HasOne::FoundMoreThanOne error.

    If you are not using Single Table Inheritance (STI) and want to suppress this error by assuming the first result is correct, you can configure the error behavior in an initializer:

    PaperTrail.config.association_reify_error_behaviour = :warn

    Valid options:

    • :error (default)
    • :warn (logs a warning and uses the first result)
    • :ignore (silently uses the first result)

    Note: If using STI, even with :warn enabled, you may encounter ActiveRecord::AssociationTypeMismatch. It is recommended to avoid STI if possible.

    # In an initializer
    PaperTrail.config.association_reify_error_behaviour = :warn
  8. Reify associations using the reify method

    master

    To restore associations as they were at the time of a specific version, you must pass specific options to the reify method on a PaperTrail version object.

    Prerequisites: Ensure that has_paper_trail is added to both your main model and all associated models you wish to track.

    Available Options:

    • has_many: true: Restores Has-Many and Has-Many-Through associations.
    • has_one: true: Restores Has-One associations.
    • belongs_to: true: Restores Belongs-To associations.

    Example:

    item.versions.last.reify(has_many: true, has_one: true, belongs_to: false)
  9. Configure PaperTrail association tracking settings

    master

    After running the installation generator, a new initializer is created at config/initializers/paper_trail.rb. You can use this file to configure how associations are tracked and how errors are handled during reification.

    By default, the generator sets:

    • PaperTrail.config.track_associations = true to enable association tracking.
    • PaperTrail.config.association_reify_error_behaviour = :error to define the error handling strategy when reifying associations.
    PaperTrail.config.track_associations = true
    PaperTrail.config.association_reify_error_behaviour = :error
  10. Configure the version association class name

    master

    By default, the gem uses PaperTrail::VersionAssociation to store association changes. You can customize this class name during the model setup process by passing the :version_associations option with a :class_name key.

    This is useful if you want to use a custom model to store the association versions instead of the default one.

    # Example of passing a custom class name during setup
    # Note: This is typically handled internally by the gem's setup logic
    setup(version_associations: { class_name: 'MyCustomVersionAssociation' })
  11. Track Has And Belongs To Many (HABTM) associations

    master

    The gem can automatically track changes to has_and_belongs_to_many associations. To enable this, you must provide the join tables used by these associations during the setup process via the :join_tables option.

    When configured, the gem adds callbacks to detect when records are added to or removed from these associations, allowing the previous state of the association to be reconstructed in PaperTrail versions.

    Note: If an association is explicitly listed in the skip option of your PaperTrail configuration, it will not be tracked.

    # Example configuration concept
    # Providing join tables allows the gem to track HABTM changes
    setup(join_tables: [:users_roles, :posts_tags])