acts_as_versionable 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in acts_as_versionable.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+
6
+ desc 'Default: run unit tests.'
7
+ task :default => :test
8
+
9
+ desc 'Test the acts_as_versionable plugin.'
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.libs << 'test'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "acts_as_versionable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "acts_as_versionable"
7
+ s.version = ActsAsVersionable::VERSION
8
+ s.authors = ["Carlos Segura"]
9
+ s.email = ["csegura@ideseg.com"]
10
+ s.homepage = ""
11
+ s.summary = "Minimalist engine for versions"
12
+ s.description = ""
13
+
14
+ s.rubyforge_project = "acts_as_versionable"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'acts_as_versionable')
2
+ ActiveRecord::Base.class_eval { include ActsAsVersionable }
@@ -0,0 +1,81 @@
1
+ require 'acts_as_versionable/version'
2
+
3
+ # ActsAsVersionable
4
+ module ActsAsVersionable
5
+
6
+ class NoSuchVersionError < Exception; end
7
+
8
+ def self.included(base)
9
+ base.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def create_class(name, superclass, &block)
15
+ klass = Class.new superclass, &block
16
+ Object.const_set name, klass
17
+ end
18
+
19
+ def acts_as_versionable(opts = {})
20
+ has_many :versions, :class_name => "#{self}Version", :dependent => :destroy
21
+ after_save :apply_versioning
22
+
23
+ attr_accessor :local_changes
24
+
25
+ class << create_class("#{self}Version", ActiveRecord::Base)
26
+ def actual_columns
27
+ content_columns.reject { |c| c.type == :datetime || c.name == 'versioned_as' }
28
+ end
29
+ end
30
+
31
+ include InstanceMethods
32
+ end
33
+
34
+ end
35
+
36
+ module InstanceMethods
37
+
38
+ def version
39
+ current_version || 0
40
+ end
41
+
42
+ def revert_to(version)
43
+ revision = versions.find_by_versioned_as(version)
44
+
45
+ raise NoSuchVersionError, "Couldn't find #{version} version" if revision.blank?
46
+
47
+ versions.actual_columns.each do |column|
48
+ self[column.name] = revision[column.name]
49
+ end
50
+
51
+ self.current_version = version
52
+ self.local_changes = true
53
+ self.save
54
+ end
55
+
56
+ private
57
+
58
+ def apply_versioning
59
+ unless self.local_changes
60
+ version_content = {}
61
+ last_version = version + 1
62
+
63
+ versions.actual_columns.each do |column|
64
+ version_content[column.name] = self[column.name]
65
+ end
66
+
67
+ version_content.merge!(:versioned_as => last_version)
68
+
69
+ if versions.create(version_content)
70
+ self.local_changes = true
71
+ self.update_attribute(:current_version, last_version)
72
+ end
73
+ end
74
+
75
+ self.local_changes = false
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
@@ -0,0 +1,3 @@
1
+ module ActsAsVersionable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ <%
2
+ model = model_name.constantize
3
+ model_name = model_name.downcase # model_name.downcase! - can't modify frozen string
4
+ versions_table = model_name + '_versions'
5
+ columns = model.content_columns.reject { |c| c.type == :datetime }
6
+ %>
7
+ class <%= migration_name %> < ActiveRecord::Migration
8
+ def self.up
9
+ create_table :<%= versions_table %> do |t|
10
+ <% columns.each do |column| %>
11
+ t.column :<%= column.name %>, :<%= column.type %>
12
+ <% end %>
13
+
14
+ t.integer :<%= model_name + '_id' %>
15
+ t.integer :versioned_as
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :<%= versions_table %>, :versioned_as
20
+ add_column :<%= model_name.pluralize %>, :current_version, :integer
21
+ end
22
+
23
+ def self.down
24
+ drop_table :<%= versions_table %>
25
+ remove_column :<%= model_name.pluralize %>, :current_version
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ class VersionizeGenerator < Rails::Generator::NamedBase
2
+ attr_accessor :model_name
3
+
4
+ def initialize(args, opts = {})
5
+ super
6
+ @model_name = args.first
7
+ end
8
+
9
+ def manifest
10
+ model_name_for_migration = @model_name.downcase.pluralize
11
+
12
+ record do |m|
13
+ m.migration_template('migration.rb', 'db/migrate',
14
+ :assigns => {
15
+ :model_name => @model_name,
16
+ :migration_name => "CreateVersionsFor#{@model_name.pluralize}"
17
+ },
18
+ :migration_file_name => "create_versions_for_#{model_name_for_migration}")
19
+ end
20
+ end
21
+
22
+ protected
23
+
24
+ def banner
25
+ "Usage: #{$0} versionize ModelName"
26
+ end
27
+
28
+ end
@@ -0,0 +1,109 @@
1
+ require 'rubygems'
2
+ gem 'activerecord'
3
+ require 'active_record'
4
+ require 'test/unit'
5
+ require "#{File.dirname(__FILE__)}/../init"
6
+
7
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
8
+
9
+ def setup_db
10
+ ActiveRecord::Schema.define(:version => 1) do
11
+ create_table :articles do |t|
12
+ t.string :title
13
+ t.text :body
14
+
15
+ t.integer :user_id
16
+ t.integer :current_version
17
+ t.timestamps
18
+ end
19
+
20
+ create_table :article_versions do |t|
21
+ t.string :title
22
+ t.text :body
23
+ t.integer :article_id
24
+ t.integer :versioned_as
25
+ t.timestamps
26
+ end
27
+ end
28
+ end
29
+
30
+ def teardown_db
31
+ ActiveRecord::Base.connection.tables.each do |table|
32
+ ActiveRecord::Base.connection.drop_table(table)
33
+ end
34
+ end
35
+
36
+ class Article < ActiveRecord::Base
37
+ acts_as_versionable
38
+ end
39
+
40
+ class VersionableTest < Test::Unit::TestCase
41
+ def setup
42
+ setup_db
43
+
44
+ (1..3).each { |c|
45
+ Article.create!(:title => c.to_s, :body => c.to_s)
46
+ }
47
+ end
48
+
49
+ def teardown
50
+ teardown_db
51
+ end
52
+
53
+ def test_if_mixed_methods_present
54
+ article = Article.first
55
+ [:versions, :version, :revert_to].each do |method|
56
+ assert_equal true, article.respond_to?(method)
57
+ end
58
+ end
59
+
60
+ def test_initial_versions_of_the_articles
61
+ Article.all.each do |a|
62
+ assert_equal 1, a.versions.size
63
+ assert_equal 1, a.version
64
+ end
65
+ end
66
+
67
+ def test_version_change_to_2
68
+ article = Article.first
69
+
70
+ assert_not_nil article
71
+ assert_equal '1', article.title
72
+ assert_equal 1, article.version
73
+
74
+ article.update_attributes(:title => '4', :body => '4')
75
+
76
+ assert_equal 2, article.version
77
+ assert_equal 2, article.versions.size
78
+ assert_equal '4', article.title
79
+ assert_equal '4', article.body
80
+ end
81
+
82
+ def test_revert_to
83
+ article = Article.first
84
+ assert_not_nil article
85
+
86
+ article.update_attributes(:title => '4', :body => '4')
87
+ assert_equal 2, article.versions.size
88
+
89
+ article.revert_to(1)
90
+ assert_equal 1, article.version
91
+ assert_equal '1', article.title
92
+ assert_equal '1', article.body
93
+
94
+ article.revert_to(2)
95
+ assert_equal 2, article.version
96
+ assert_equal '4', article.title
97
+ assert_equal '4', article.body
98
+
99
+ assert_raise ActsAsVersionable::NoSuchVersionError do
100
+ article.revert_to(3)
101
+ end
102
+ end
103
+
104
+ def test_dependent_destroy
105
+ Article.destroy_all
106
+ assert_equal [], ArticleVersion.all
107
+ end
108
+ end
109
+
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_versionable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Carlos Segura
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-18 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: ''
15
+ email:
16
+ - csegura@ideseg.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - Rakefile
24
+ - acts_as_versionable.gemspec
25
+ - init.rb
26
+ - lib/acts_as_versionable.rb
27
+ - lib/acts_as_versionable/version.rb
28
+ - lib/generators/versionize/templates/migration.rb
29
+ - lib/generators/versionize/versionize_generator.rb
30
+ - test/acts_as_versionable_test.rb
31
+ homepage: ''
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project: acts_as_versionable
51
+ rubygems_version: 1.8.10
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: Minimalist engine for versions
55
+ test_files:
56
+ - test/acts_as_versionable_test.rb