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 +4 -0
- data/Gemfile +4 -0
- data/Rakefile +15 -0
- data/acts_as_versionable.gemspec +24 -0
- data/init.rb +2 -0
- data/lib/acts_as_versionable.rb +81 -0
- data/lib/acts_as_versionable/version.rb +3 -0
- data/lib/generators/versionize/templates/migration.rb +27 -0
- data/lib/generators/versionize/versionize_generator.rb +28 -0
- data/test/acts_as_versionable_test.rb +109 -0
- metadata +56 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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,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,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
|