Project

snaps

0.0
No commit activity in last 3 years
No release in over 3 years
Revisioning and tagging of ActiveRecord models.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

Runtime

~> 4.2.1
 Project Readme

Snaps

Gem Version Build Status Test Coverage Code Climate

Revisioning and tagging of ActiveRecord models.

Installation

Add this line to your application's Gemfile:

gem 'snaps'

And then execute:

$ bundle

Run the install generator:

$ rake snaps:install:migrations

Migrate your database to create the snaps_tags table:

$ rake db:migrate

Basics

Snaps maintains a table of "named pointers" called Tags to other models. Implemented as a polymorphic association. Revisions are kept in the original table and identified by a perma_id field.

Snaps provides methods to create copies (snapshots) of models and tag these snapshots as needed.

Usage Examples

Basic Usage

A model that is to be revisioned with snaps needs an integer field called perma_id and must include the mixin created by Snaps.revision.

# db/migrate/xxx_create_posts.rb
create_table :posts do |t|
  t.integer(:perma_id)

  t.string(:title)
  t.text(:body)
end


# app/models/post.rb
class Post < ActiveRecord::Base
  include Snaps.revision
end

Now you can create snapshots of your post instances.

post = Posts.create(body: 'Some text')
post_snapshot = post.snapshot!

post.perma_id == post_snapshot.perma_id # => true

Using Snaps Tags you can assign one revision to be a 'draft'.

post.perma_id # => 25
post.snaps_tag!(:draft)

draft = Post.with_snaps_tag(:draft).find_by_perma_id(25)

Using a default Tag

It's easy to create a workflow in which a new post will be tagged by default. Also it's convenient to hide calls to with_snaps_tag in a scope on your domain models.

# app/models/post.rb
class Post < ActiveRecord::Base
  include Snaps.revision(default_tag: :draft)

  scope :drafts, -> { with_snaps_tag(:draft) }
end

# in controller
draft = Post.create(body: 'Some Text')

existing_draft = Post.drafts.find_by_perma_id(25)

draft.update(body: "New text")
draft.snapshot!

Managing Lifecycle of records with tags

# app/models/post.rb
class Post < ActiveRecord::Base
  include Snaps.revision(default_tag: :draft)

  scope :drafts, -> { with_snaps_tag(:draft) }
  scope :published, -> { with_snaps_tag(:published) }

  def publish
    snapshot!(tag: :published)
  end
end

# in controller
draft = Post.drafts.find_by_perma_id(25)
draft.publish

all_published_posts = Post.published

Revisioning composite models

# db/migrate/xxx_create_sections.rb
create_table :sections do |t|
  t.integer(:perma_id)
  t.references(:post)

  t.text(:body)
end

# app/models/section.rb
class Section < ActiveRecord::Base
  include Snaps.revision

  belongs_to :post
end

# app/models/post.rb
class Post < ActiveRecord::Base
  include Snaps.revision(default_tag: :draft,
                         components: [:sections])

  has_many :sections

  scope :drafts, -> { with_snaps_tag(:draft) }
  scope :published, -> { with_snaps_tag(:published) }

  def publish
    snapshot!(tag: :published)
  end
end

# in controller

draft = Post.drafts.find_by_perma_id(25)
draft.sections.create(body: "Section text")
post = draft.snapshot!

# snapshots of sections have been created

draft.sections.first.id != post.sections.first.id
draft.sections.first.body == post.sections.first.body

Accessing other revisions of a record

post.snaps_revisions.where('created_at < ?', post.created_at)