Giter Site home page Giter Site logo

dm-is-versioned's Introduction

dm-is-versioned

DataMapper plugin enabling simple versioning of models.

When a model is versioned, and updated, instead of the previous version being lost in the mists of time, it is saved in a subsidiary table, so that it can be restored later if needed.

Please Note! this gem behaves differently to how AR’s :acts_as_versioned works. There is currently no multi version storage possible.

Installation

Stable

Install the dm-is-versioned gem.

$ (sudo)? gem install dm-is-versioned

Edge

Download or clone dm-is-versioned from Github.

$ cd /path/to/dm-is-versioned

$ rake install            # will install dm-is-versioned

Getting started

To start using this gem, just require dm-is-versioned in your app.

Lets say we have a Post class, and we want to retain the previous version of a post. Just do this:

class Post
  include DataMapper::Resource

  property :id,         Serial
  property :title,      String
  property :body,       Text
  property :updated_at, DateTime

  is_versioned :on => :updated_at

  ## this syntax also works
  # is :versioned, :on => :updated_at

end

That simple snippet will automatically create a second Post::Version model and a (post_versions) table in your DB when you auto migrate or auto upgrade:

Post.auto_migrate! # => will run auto_migrate! on Post::Version, too
Post.auto_upgrade! # => will run auto_upgrade! on Post::Version, too

# or
DataMapper.auto_migrate! # => will run auto_migrate! on Post::Version, too
DataMapper.auto_upgrade! # => will run auto_upgrade! on Post::Version, too

# don't forget to require 'dm-migrations' before you migrate.

OK, now that we have a versioned model, let’s see what we can do with it.

Usage

We start with creating a new Post:

post = Post.create(:title => 'A versioned Post title', :body => "A versioned Post body")
# automatically saved

When we create this Post, no secondary version is created.

The versioning only takes place when we update our Post:

post.title = "An updated & versioned Post title"
post.save

In the Post::Version (post_versions) table we would now find the previous version of the Post.

This is how it would look like:

# db.post table
---------------------------------------------------------------------------------------
| id | title                                | body                       | updated_at |
---------------------------------------------------------------------------------------
| 1 | 'An updated & versioned Post title'   | 'A versioned Post body'    | DateTime   |

# db.post_versions table
---------------------------------------------------------------------------------------
| id | title                                | body                       | updated_at |
---------------------------------------------------------------------------------------
| 1 | 'A versioned Post title'              | 'A versioned Post body'    | DateTime   |

#versions

Once you have a versioned model, you can retrieve the previous version, like this:

old_post = post.versions.first
  => #<Post::Version @id=1 @title="A versioned Post title" @body=... @updated_at=...>

Please Note! that the #versions method returns an array by default.

That’s basically what dm-is-versioned does.

Gotchas

Now there are some gotcha’s that might not be entirely obvious to everyone, so let’s clarify them here.

Make sure the versioned trigger has a value

In this type of scenario:

class Post
  <snip...>
  property :updated_at, DateTime

  is_versioned :on => :updated_at
end

You must ensure that the versioned trigger always has a value, either through:

# using the dm-timestamps gem, which automatically updates the :updated_at attribute
timestamps :at

# or a callback method, that updates the value before save
before(:save) { self.updated_at = Time.now }

Without this, things just don’t work.

Ensure you use dm-migrations gem

The post_versions table is NOT created unless you migrate your models through:

DataMapper.auto_migrate! / Post.auto_migrate!
DataMapper.auto_upgrade! / Post.auto_upgrade!

That’s about it.

Errors / Bugs

If something is not behaving intuitively, it is a bug, and should be reported. Report it here: datamapper.lighthouseapp.com/

TODOs

  • Make it work more like AR’s :acts_as_versioned plugin, with support for multiple versions and so on.

  • Enable replacing a current version with an old version.

  • Anything else missing?

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so we don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history.

    • (if you want to have your own version, that is fine but bump version in a commit by itself we can ignore when we pull)

  • Send us a pull request. Bonus points for topic branches.

Copyright © 2011 Timothy Bennett. Released under the MIT License.

See LICENSE for details.

Credits

Credit also goes to these contributors.

dm-is-versioned's People

Contributors

agate avatar bernerdschaefer avatar david avatar dkubb avatar gix avatar jamie avatar kematzy avatar michaelklishin avatar myabc avatar namelessjon avatar ndarilek avatar paul avatar postmodern avatar sam avatar skade avatar snusnu avatar solnic avatar somebee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

dm-is-versioned's Issues

assumes serial primary keys

If you try to version a resource that does not user a serial primary key, you run into integrity issues due to the tight coupling between property option values and the construction of the primary key (https://github.com/datamapper/dm-is-versioned/blob/master/lib/dm-is-versioned/is/versioned.rb#L88). If a natural key is used, then the original key is ignored in the version table.

There is no method used for the consuming class to override this behaviour - this could be easily remedied by extracting the property argument creation logic to a new method that can be overrriden (https://github.com/datamapper/dm-is-versioned/blob/master/lib/dm-is-versioned/is/versioned.rb#L78-L88), eg:

module ClassMethods

  def define_version_property(property)
    type = case property
      when DataMapper::Property::Discriminator then Class
      when DataMapper::Property::Serial        then Integer
    else
      property.class
    end
    options = property.options.merge(:key => property.name == @on)
    options[:key] = true if options.delete(:serial)
    return property.name, type, options
  end

  def const_missing(name)
    if name == :Version
      model = DataMapper::Model.new(name, self)
      properties.each do |property|
        name, type, options = define_version_property property
        model.property(property.name, type, options)
      end
      model
    else
      super
    end
  end

end # ClassMethods

And then the consumer need only override define_version_property to adjust the options hash

dm-is-versioned doesn't work with dm-timestamps

Due to changes in the way dm-timestamps updates the timestamps, dm-is-versioned will no longer trigger the saving of new versions when updated_at changes. This is because dm-timestamps bypasses the property accessor methods dm-is-versioned uses to determine changes and instead uses property#set.

See the attached script for the effect this has.


Created by Jonathan Stott (namelessjon) - 2010-01-20 13:46:29 UTC

Original Lighthouse ticket: http://datamapper.lighthouseapp.com/projects/20609/tickets/1184

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.