rich-acts_as_revisable 0.6.0 → 0.9.8
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/LICENSE +1 -1
- data/{README → README.rdoc} +54 -25
- data/Rakefile +44 -0
- data/lib/acts_as_revisable/acts/common.rb +170 -23
- data/lib/acts_as_revisable/acts/deletable.rb +29 -0
- data/lib/acts_as_revisable/acts/revisable.rb +362 -127
- data/lib/acts_as_revisable/acts/revision.rb +94 -21
- data/lib/acts_as_revisable/acts/scoped_model.rb +28 -4
- data/lib/acts_as_revisable/base.rb +6 -3
- data/lib/acts_as_revisable/clone_associations.rb +8 -0
- data/lib/acts_as_revisable/gem_spec_options.rb +18 -0
- data/lib/acts_as_revisable/quoted_columns.rb +14 -9
- data/lib/acts_as_revisable/version.rb +2 -2
- data/spec/associations_spec.rb +22 -0
- data/spec/branch_spec.rb +42 -0
- data/spec/find_spec.rb +38 -0
- data/spec/{acts_as_revisable_spec.rb → general_spec.rb} +21 -26
- data/spec/quoted_columns_spec.rb +19 -0
- data/spec/revert_spec.rb +42 -0
- data/spec/spec_helper.rb +41 -5
- metadata +29 -16
- /data/spec/{aar_options_spec.rb → options_spec.rb} +0 -0
@@ -2,10 +2,22 @@ require 'acts_as_revisable/clone_associations'
|
|
2
2
|
|
3
3
|
module FatJam
|
4
4
|
module ActsAsRevisable
|
5
|
+
# This module is mixed into the revision classes.
|
6
|
+
#
|
7
|
+
# ==== Callbacks
|
8
|
+
#
|
9
|
+
# * +before_restore+ is called on the revision class before it is
|
10
|
+
# restored as the current record.
|
11
|
+
# * +after_restore+ is called on the revision class after it is
|
12
|
+
# restored as the current record.
|
5
13
|
module Revision
|
6
|
-
def self.included(base)
|
14
|
+
def self.included(base) #:nodoc:
|
7
15
|
base.send(:extend, ClassMethods)
|
8
|
-
|
16
|
+
|
17
|
+
class << base
|
18
|
+
attr_accessor :revisable_revisable_class, :revisable_cloned_associations
|
19
|
+
end
|
20
|
+
|
9
21
|
base.instance_eval do
|
10
22
|
set_table_name(revisable_class.table_name)
|
11
23
|
acts_as_scoped_model :find => {:conditions => {:revisable_is_current => false}}
|
@@ -13,27 +25,48 @@ module FatJam
|
|
13
25
|
CloneAssociations.clone_associations(revisable_class, self)
|
14
26
|
|
15
27
|
define_callbacks :before_restore, :after_restore
|
16
|
-
|
17
|
-
belongs_to :current_revision, :class_name => revisable_class_name, :foreign_key => :revisable_original_id
|
18
|
-
belongs_to revisable_class_name.downcase.to_sym, :class_name => revisable_class_name, :foreign_key => :revisable_original_id
|
19
|
-
|
20
28
|
before_create :revision_setup
|
29
|
+
after_create :grab_my_branches
|
30
|
+
|
31
|
+
[:current_revision, revisable_association_name.to_sym].each do |a|
|
32
|
+
belongs_to a, :class_name => revisable_class_name, :foreign_key => :revisable_original_id
|
33
|
+
end
|
34
|
+
|
35
|
+
[[:ancestors, "<"], [:descendants, ">"]].each do |a|
|
36
|
+
# Jumping through hoops here to try and make sure the
|
37
|
+
# :finder_sql is cross-database compatible. :finder_sql
|
38
|
+
# in a plugin is evil but, I see no other option.
|
39
|
+
has_many a.first, :class_name => revision_class_name, :finder_sql => "select * from #{quoted_table_name} where #{quote_bound_value(:revisable_original_id)} = \#{revisable_original_id} and #{quote_bound_value(:revisable_number)} #{a.last} \#{revisable_number} and #{quote_bound_value(:revisable_is_current)} = #{quote_value(false)} order by #{quote_bound_value(:revisable_number)} #{(a.last.eql?("<") ? "DESC" : "ASC")}"
|
40
|
+
end
|
21
41
|
end
|
22
42
|
end
|
23
|
-
|
24
|
-
def
|
43
|
+
|
44
|
+
def find_revision(*args)
|
45
|
+
current_revision.find_revision(*args)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the revision prior to this one.
|
49
|
+
def previous_revision
|
50
|
+
self.class.find(:first, :conditions => {:revisable_original_id => revisable_original_id, :revisable_number => revisable_number - 1})
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return the revision after this one.
|
54
|
+
def next_revision
|
55
|
+
self.class.find(:first, :conditions => {:revisable_original_id => revisable_original_id, :revisable_number => revisable_number + 1})
|
56
|
+
end
|
57
|
+
|
58
|
+
# Setter for revisable_name just to make external API more pleasant.
|
59
|
+
def revision_name=(val) #:nodoc:
|
25
60
|
self[:revisable_name] = val
|
26
61
|
end
|
27
62
|
|
28
|
-
|
63
|
+
# Accessor for revisable_name just to make external API more pleasant.
|
64
|
+
def revision_name #:nodoc:
|
29
65
|
self[:revisable_name]
|
30
66
|
end
|
31
|
-
|
32
|
-
def revision_number
|
33
|
-
self[:revisable_number]
|
34
|
-
end
|
35
67
|
|
36
|
-
|
68
|
+
# Sets some initial values for a new revision.
|
69
|
+
def revision_setup #:nodoc:
|
37
70
|
now = Time.now
|
38
71
|
prev = current_revision.revisions.first
|
39
72
|
prev.update_attribute(:revisable_revised_at, now) if prev
|
@@ -43,24 +76,64 @@ module FatJam
|
|
43
76
|
self[:revisable_type] = current_revision[:type]
|
44
77
|
self[:revisable_number] = (self.class.maximum(:revisable_number, :conditions => {:revisable_original_id => self[:revisable_original_id]}) || 0) + 1
|
45
78
|
end
|
46
|
-
|
79
|
+
|
80
|
+
def grab_my_branches
|
81
|
+
self.class.revisable_class.update_all(["revisable_branched_from_id = ?", self[:id]], ["revisable_branched_from_id = ?", self[:revisable_original_id]])
|
82
|
+
end
|
83
|
+
|
84
|
+
def from_revisable
|
85
|
+
current_revision.for_revision
|
86
|
+
end
|
87
|
+
|
88
|
+
def reverting_from
|
89
|
+
from_revisable[:reverting_from]
|
90
|
+
end
|
91
|
+
|
92
|
+
def reverting_from=(val)
|
93
|
+
from_revisable[:reverting_from] = val
|
94
|
+
end
|
95
|
+
|
96
|
+
def reverting_to
|
97
|
+
from_revisable[:reverting_to]
|
98
|
+
end
|
99
|
+
|
100
|
+
def reverting_to=(val)
|
101
|
+
from_revisable[:reverting_to] = val
|
102
|
+
end
|
103
|
+
|
47
104
|
module ClassMethods
|
48
|
-
|
105
|
+
# Returns the +revisable_class_name+ as configured in
|
106
|
+
# +acts_as_revisable+.
|
107
|
+
def revisable_class_name #:nodoc:
|
49
108
|
self.revisable_options.revisable_class_name || self.class_name.gsub(/Revision/, '')
|
50
109
|
end
|
51
110
|
|
52
|
-
|
53
|
-
|
111
|
+
# Returns the actual +Revisable+ class based on the
|
112
|
+
# #revisable_class_name.
|
113
|
+
def revisable_class #:nodoc:
|
114
|
+
self.revisable_revisable_class ||= revisable_class_name.constantize
|
54
115
|
end
|
55
116
|
|
56
|
-
|
117
|
+
# Returns the revision_class which in this case is simply +self+.
|
118
|
+
def revision_class #:nodoc:
|
57
119
|
self
|
58
120
|
end
|
59
121
|
|
60
|
-
def
|
122
|
+
def revision_class_name #:nodoc:
|
123
|
+
self.name
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the name of the association acts_as_revision
|
127
|
+
# creates.
|
128
|
+
def revisable_association_name #:nodoc:
|
129
|
+
revisable_class_name.downcase
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns an array of the associations that should be cloned.
|
133
|
+
def revision_cloned_associations #:nodoc:
|
61
134
|
clone_associations = self.revisable_options.clone_associations
|
62
135
|
|
63
|
-
|
136
|
+
self.revisable_cloned_associations ||= if clone_associations.blank?
|
64
137
|
[]
|
65
138
|
elsif clone_associations.eql? :all
|
66
139
|
revisable_class.reflect_on_all_associations.map(&:name)
|
@@ -8,7 +8,7 @@ module FatJam
|
|
8
8
|
SCOPED_METHODS = %w(construct_calculation_sql construct_finder_sql update_all delete_all destroy_all).freeze
|
9
9
|
|
10
10
|
def call_method_with_static_scope(meth, args)
|
11
|
-
return send(meth, *args) unless self.scoped_model_enabled
|
11
|
+
return send(meth, *args) unless self.scoped_model_enabled?
|
12
12
|
|
13
13
|
with_scope(self.scoped_model_static_scope) do
|
14
14
|
send(meth, *args)
|
@@ -22,7 +22,7 @@ module FatJam
|
|
22
22
|
end
|
23
23
|
EVAL
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def without_model_scope
|
27
27
|
return unless block_given?
|
28
28
|
|
@@ -36,14 +36,38 @@ module FatJam
|
|
36
36
|
rv
|
37
37
|
end
|
38
38
|
|
39
|
+
def disable_model_scope!
|
40
|
+
self.scoped_model_disable_count += 1
|
41
|
+
end
|
42
|
+
|
43
|
+
def enable_model_scope!
|
44
|
+
self.scoped_model_disable_count -= 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def scoped_model_enabled?
|
48
|
+
self.scoped_model_disable_count == 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def scoped_model_enabled
|
52
|
+
self.scoped_model_enabled?
|
53
|
+
end
|
54
|
+
|
55
|
+
def scoped_model_enabled=(value)
|
56
|
+
if value == false
|
57
|
+
disable_model_scope!
|
58
|
+
else
|
59
|
+
enable_model_scope!
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
39
63
|
def acts_as_scoped_model(*args)
|
40
64
|
class << self
|
41
|
-
attr_accessor :scoped_model_static_scope, :
|
65
|
+
attr_accessor :scoped_model_static_scope, :scoped_model_disable_count
|
42
66
|
SCOPED_METHODS.each do |m|
|
43
67
|
alias_method_chain m.to_sym, :static_scope
|
44
68
|
end
|
45
69
|
end
|
46
|
-
self.
|
70
|
+
self.scoped_model_disable_count = 0
|
47
71
|
self.scoped_model_static_scope = args.extract_options!
|
48
72
|
end
|
49
73
|
end
|
@@ -2,6 +2,7 @@ require 'acts_as_revisable/options'
|
|
2
2
|
require 'acts_as_revisable/acts/common'
|
3
3
|
require 'acts_as_revisable/acts/revision'
|
4
4
|
require 'acts_as_revisable/acts/revisable'
|
5
|
+
require 'acts_as_revisable/acts/deletable'
|
5
6
|
|
6
7
|
module FatJam
|
7
8
|
# define the columns used internall by AAR
|
@@ -22,25 +23,27 @@ module FatJam
|
|
22
23
|
def acts_as_revisable(*args, &block)
|
23
24
|
revisable_shared_setup(args, block)
|
24
25
|
self.send(:include, Revisable)
|
26
|
+
self.send(:include, Deletable) if self.revisable_options.on_delete == :revise
|
25
27
|
end
|
26
28
|
|
27
29
|
# This +acts_as+ extension provides for making a model the
|
28
30
|
# revision model in an acts_as_revisable pair.
|
29
31
|
def acts_as_revision(*args, &block)
|
30
32
|
revisable_shared_setup(args, block)
|
31
|
-
self.send(:include, Revision)
|
33
|
+
self.send(:include, Revision)
|
32
34
|
end
|
33
35
|
|
34
36
|
private
|
35
37
|
# Performs the setup needed for both kinds of acts_as_revisable
|
36
38
|
# models.
|
37
39
|
def revisable_shared_setup(args, block)
|
38
|
-
self.send(:include, Common)
|
39
40
|
class << self
|
40
41
|
attr_accessor :revisable_options
|
41
42
|
end
|
42
43
|
options = args.extract_options!
|
43
|
-
|
44
|
+
self.revisable_options = Options.new(options, &block)
|
45
|
+
|
46
|
+
self.send(:include, Common)
|
44
47
|
end
|
45
48
|
end
|
46
49
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# This module encapsulates the methods used by ActsAsRevisable
|
2
|
+
# for cloning associations from one model to another.
|
1
3
|
module FatJam
|
2
4
|
module ActsAsRevisable
|
3
5
|
module CloneAssociations
|
@@ -22,6 +24,12 @@ module FatJam
|
|
22
24
|
def clone_belongs_to_association(association, to)
|
23
25
|
to.send(association.macro, association.name, association.options.clone)
|
24
26
|
end
|
27
|
+
|
28
|
+
def clone_has_many_association(association, to)
|
29
|
+
options = association.options.clone
|
30
|
+
options[:association_foreign_key] ||= "revisable_original_id"
|
31
|
+
to.send(association.macro, association.name, options)
|
32
|
+
end
|
25
33
|
end
|
26
34
|
end
|
27
35
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FatJam #:nodoc:
|
2
|
+
module ActsAsRevisable
|
3
|
+
class GemSpecOptions
|
4
|
+
HASH = {
|
5
|
+
:name => "fatjam-acts_as_revisable",
|
6
|
+
:version => FatJam::ActsAsRevisable::VERSION::STRING,
|
7
|
+
:summary => "acts_as_revisable enables revision tracking, querying, reverting and branching of ActiveRecord models. Inspired by acts_as_versioned.",
|
8
|
+
:email => "cavanaugh@fatjam.com",
|
9
|
+
:homepage => "http://github.com/fatjam/acts_as_revisable/tree/master",
|
10
|
+
:has_rdoc => true,
|
11
|
+
:authors => ["Rich Cavanaugh of JamLab, LLC.", "Stephen Caudill of JamLab, LLC."],
|
12
|
+
:files => %w( LICENSE README.rdoc Rakefile ) + Dir["{spec,lib,generators,rails}/**/*"],
|
13
|
+
:rdoc_options => ["--main", "README.rdoc"],
|
14
|
+
:extra_rdoc_files => ["README.rdoc", "LICENSE"]
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,7 +1,19 @@
|
|
1
|
+
# This module is more about the pretty than anything else. This allows
|
2
|
+
# you to use symbols for column names in a conditions hash.
|
3
|
+
#
|
4
|
+
# User.find(:all, :conditions => ["? = ?", :name, "sam"])
|
5
|
+
#
|
6
|
+
# Would generate:
|
7
|
+
#
|
8
|
+
# select * from users where "users"."name" = 'sam'
|
9
|
+
#
|
10
|
+
# This is consistent with Rails and Ruby where symbols are used to
|
11
|
+
# represent methods. Only a symbol matching a column name will
|
12
|
+
# trigger this beavior.
|
1
13
|
module FatJam::QuotedColumnConditions
|
2
14
|
def self.included(base)
|
3
15
|
base.send(:extend, ClassMethods)
|
4
|
-
|
16
|
+
|
5
17
|
class << base
|
6
18
|
alias_method_chain :quote_bound_value, :quoted_column
|
7
19
|
end
|
@@ -12,14 +24,7 @@ module FatJam::QuotedColumnConditions
|
|
12
24
|
if value.is_a?(Symbol) && column_names.member?(value.to_s)
|
13
25
|
# code borrowed from sanitize_sql_hash_for_conditions
|
14
26
|
attr = value.to_s
|
15
|
-
|
16
|
-
# Extract table name from qualified attribute names.
|
17
|
-
if attr.include?('.')
|
18
|
-
table_name, attr = attr.split('.', 2)
|
19
|
-
table_name = connection.quote_table_name(table_name)
|
20
|
-
else
|
21
|
-
table_name = quoted_table_name
|
22
|
-
end
|
27
|
+
table_name = quoted_table_name
|
23
28
|
|
24
29
|
return "#{table_name}.#{connection.quote_column_name(attr)}"
|
25
30
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe FatJam::ActsAsRevisable do
|
4
|
+
after(:each) do
|
5
|
+
cleanup_db
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@project = Project.create(:name => "Rich", :notes => "this plugin's author")
|
10
|
+
@project.update_attribute(:name, "one")
|
11
|
+
@project.update_attribute(:name, "two")
|
12
|
+
@project.update_attribute(:name, "three")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a pretty named association" do
|
16
|
+
lambda { @project.sessions }.should_not raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return all the revisions" do
|
20
|
+
@project.revisions.size.should == 3
|
21
|
+
end
|
22
|
+
end
|
data/spec/branch_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
class Project
|
4
|
+
validates_presence_of :name
|
5
|
+
end
|
6
|
+
|
7
|
+
describe FatJam::ActsAsRevisable, "with branching" do
|
8
|
+
after(:each) do
|
9
|
+
cleanup_db
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@project = Project.create(:name => "Rich", :notes => "a note")
|
14
|
+
@project.update_attribute(:name, "Sam")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should allow for branch creation" do
|
18
|
+
@project.should == @project.branch.branch_source
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should branch without saving" do
|
22
|
+
@project.branch.should be_new_record
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should branch and save" do
|
26
|
+
@project.branch!.should_not be_new_record
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not raise an error for a valid branch" do
|
30
|
+
lambda { @project.branch!(:name => "A New User") }.should_not raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should raise an error for invalid records" do
|
34
|
+
lambda { @project.branch!(:name => nil) }.should raise_error
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should not save an invalid record" do
|
38
|
+
@branch = @project.branch(:name => nil)
|
39
|
+
@branch.save.should be_false
|
40
|
+
@branch.should be_new_record
|
41
|
+
end
|
42
|
+
end
|
data/spec/find_spec.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe FatJam::ActsAsRevisable do
|
4
|
+
after(:each) do
|
5
|
+
cleanup_db
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "with a single revision" do
|
9
|
+
before(:each) do
|
10
|
+
@project1 = Project.create(:name => "Rich", :notes => "a note")
|
11
|
+
@project1.update_attribute(:name, "Sam")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should just find the current revision by default" do
|
15
|
+
Project.find(:first).name.should == "Sam"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should accept the :with_revisions options" do
|
19
|
+
lambda { Project.find(:all, :with_revisions => true) }.should_not raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should provide find_with_revisions" do
|
23
|
+
lambda { Project.find_with_revisions(:all) }.should_not raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should find current and revisions with the :with_revisions option" do
|
27
|
+
Project.find(:all, :with_revisions => true).size.should == 2
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should find current and revisions with the find_with_revisions method" do
|
31
|
+
Project.find_with_revisions(:all).size.should == 2
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should find revisions with conditions" do
|
35
|
+
Project.find_with_revisions(:all, :conditions => {:name => "Rich"}).should == [@project1.find_revision(:previous)]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,43 +1,34 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
|
4
|
-
acts_as_revisable do
|
5
|
-
revision_class_name "Session"
|
6
|
-
except :unimportant
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class Session < ActiveRecord::Base
|
11
|
-
acts_as_revision do
|
12
|
-
revisable_class_name "Project"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
describe FatJam::ActsAsRevisable do
|
17
|
-
before(:all) do
|
18
|
-
setup_db
|
19
|
-
end
|
20
|
-
|
3
|
+
describe FatJam::ActsAsRevisable do
|
21
4
|
after(:each) do
|
22
5
|
cleanup_db
|
23
6
|
end
|
24
|
-
|
25
|
-
after(:all) do
|
26
|
-
teardown_db
|
27
|
-
end
|
28
|
-
|
7
|
+
|
29
8
|
before(:each) do
|
30
9
|
@project = Project.create(:name => "Rich", :notes => "this plugin's author")
|
31
10
|
end
|
32
11
|
|
33
12
|
describe "without revisions" do
|
34
13
|
it "should have a revision_number of zero" do
|
35
|
-
@project.revision_number.should
|
14
|
+
@project.revision_number.should be_zero
|
36
15
|
end
|
37
16
|
|
38
|
-
it "should
|
17
|
+
it "should be the current revision" do
|
18
|
+
@project.revisable_is_current.should be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should respond to current_revision? positively" do
|
22
|
+
@project.current_revision?.should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should not have any revisions in the generic association" do
|
39
26
|
@project.revisions.should be_empty
|
40
27
|
end
|
28
|
+
|
29
|
+
it "should not have any revisions in the pretty named association" do
|
30
|
+
@project.sessions.should be_empty
|
31
|
+
end
|
41
32
|
end
|
42
33
|
|
43
34
|
describe "with revisions" do
|
@@ -49,10 +40,14 @@ describe FatJam::ActsAsRevisable do
|
|
49
40
|
@project.revision_number.should == 1
|
50
41
|
end
|
51
42
|
|
52
|
-
it "should have a single revision" do
|
43
|
+
it "should have a single revision in the generic association" do
|
53
44
|
@project.revisions.size.should == 1
|
54
45
|
end
|
55
46
|
|
47
|
+
it "should have a single revision in the pretty named association" do
|
48
|
+
@project.sessions.size.should == 1
|
49
|
+
end
|
50
|
+
|
56
51
|
it "should return an instance of the revision class" do
|
57
52
|
@project.revisions.first.should be_an_instance_of(Session)
|
58
53
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe "the quoted_columns extension" do
|
4
|
+
after(:each) do
|
5
|
+
cleanup_db
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should quote symbols matching column names as columns" do
|
9
|
+
Project.send(:quote_bound_value, :name).should == %q{"projects"."name"}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should not quote symbols that don't match column names" do
|
13
|
+
Project.send(:quote_bound_value, :whatever).should == "'#{:whatever.to_yaml}'"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should not quote strings any differently" do
|
17
|
+
Project.send(:quote_bound_value, "what").should == Project.send(:quote_bound_value_with_quoted_column, "what")
|
18
|
+
end
|
19
|
+
end
|
data/spec/revert_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe FatJam::ActsAsRevisable, "with reverting" do
|
4
|
+
after(:each) do
|
5
|
+
cleanup_db
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@project = Project.create(:name => "Rich", :notes => "a note")
|
10
|
+
@project.update_attribute(:name, "Sam")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should let you revert to previous versions" do
|
14
|
+
@project.revert_to!(:first)
|
15
|
+
@project.name.should == "Rich"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should accept the :without_revision hash option" do
|
19
|
+
lambda { @project.revert_to!(:first, :without_revision => true) }.should_not raise_error
|
20
|
+
@project.name.should == "Rich"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should support the revert_to_without_revision method" do
|
24
|
+
lambda { @project.revert_to_without_revision(:first).save }.should_not raise_error
|
25
|
+
@project.name.should == "Rich"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should support the revert_to_without_revision! method" do
|
29
|
+
lambda { @project.revert_to_without_revision!(:first) }.should_not raise_error
|
30
|
+
@project.name.should == "Rich"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should let you revert to previous versions without a new revision" do
|
34
|
+
@project.revert_to!(:first, :without_revision => true)
|
35
|
+
@project.revisions.size.should == 1
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should support the revert_to method" do
|
39
|
+
lambda{ @project.revert_to(:first) }.should_not raise_error
|
40
|
+
@project.should be_changed
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -19,6 +19,15 @@ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memo
|
|
19
19
|
|
20
20
|
def setup_db
|
21
21
|
ActiveRecord::Schema.define(:version => 1) do
|
22
|
+
create_table :people do |t|
|
23
|
+
t.string :name, :revisable_name, :revisable_type
|
24
|
+
t.text :notes
|
25
|
+
t.boolean :revisable_is_current
|
26
|
+
t.integer :revisable_original_id, :revisable_branched_from_id, :revisable_number, :project_id
|
27
|
+
t.datetime :revisable_current_at, :revisable_revised_at, :revisable_deleted_at
|
28
|
+
t.timestamps
|
29
|
+
end
|
30
|
+
|
22
31
|
create_table :projects do |t|
|
23
32
|
t.string :name, :unimportant, :revisable_name, :revisable_type
|
24
33
|
t.text :notes
|
@@ -30,14 +39,41 @@ def setup_db
|
|
30
39
|
end
|
31
40
|
end
|
32
41
|
|
33
|
-
|
34
|
-
ActiveRecord::Base.connection.tables.each do |table|
|
35
|
-
ActiveRecord::Base.connection.drop_table(table)
|
36
|
-
end
|
37
|
-
end
|
42
|
+
setup_db
|
38
43
|
|
39
44
|
def cleanup_db
|
40
45
|
ActiveRecord::Base.connection.tables.each do |table|
|
41
46
|
ActiveRecord::Base.connection.execute("delete from #{table}")
|
42
47
|
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Person < ActiveRecord::Base
|
51
|
+
belongs_to :project
|
52
|
+
|
53
|
+
acts_as_revisable do
|
54
|
+
revision_class_name "OldPerson"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class OldPerson < ActiveRecord::Base
|
59
|
+
acts_as_revision do
|
60
|
+
revisable_class_name "Person"
|
61
|
+
clone_associations :all
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Project < ActiveRecord::Base
|
66
|
+
has_many :people
|
67
|
+
|
68
|
+
acts_as_revisable do
|
69
|
+
revision_class_name "Session"
|
70
|
+
except :unimportant
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Session < ActiveRecord::Base
|
75
|
+
acts_as_revision do
|
76
|
+
revisable_class_name "Project"
|
77
|
+
clone_associations :all
|
78
|
+
end
|
43
79
|
end
|