kb-acts_as_revisable 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ module WithoutScope #:nodoc:
2
+ module ActsAsRevisable
3
+ class GemSpecOptions
4
+ HASH = {
5
+ :name => "rich-acts_as_revisable",
6
+ :version => WithoutScope::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 => "rich@withoutscope.com",
9
+ :homepage => "http://github.com/rich/acts_as_revisable",
10
+ :has_rdoc => true,
11
+ :authors => ["Rich Cavanaugh", "Stephen Caudill"],
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
@@ -0,0 +1,22 @@
1
+ module WithoutScope
2
+ module ActsAsRevisable
3
+ # This class provides for a flexible method of setting
4
+ # options and querying them. This is especially useful
5
+ # for giving users flexibility when using your plugin.
6
+ class Options
7
+ def initialize(*options, &block)
8
+ @options = options.extract_options!
9
+ instance_eval(&block) if block_given?
10
+ end
11
+
12
+ def method_missing(key, *args)
13
+ return (@options[key.to_s.gsub(/\?$/, '').to_sym].eql?(true)) if key.to_s.match(/\?$/)
14
+ if args.blank?
15
+ @options[key.to_sym]
16
+ else
17
+ @options[key.to_sym] = args.size == 1 ? args.first : args
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
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.
13
+ module WithoutScope::QuotedColumnConditions
14
+ def self.included(base)
15
+ base.send(:extend, ClassMethods)
16
+ end
17
+
18
+ module ClassMethods
19
+ def quote_bound_value(value)
20
+ if value.is_a?(Symbol) && column_names.member?(value.to_s)
21
+ # code borrowed from sanitize_sql_hash_for_conditions
22
+ attr = value.to_s
23
+ table_name = quoted_table_name
24
+
25
+ return "#{table_name}.#{connection.quote_column_name(attr)}"
26
+ end
27
+
28
+ super(value)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ module WithoutScope #:nodoc:
2
+ module ActsAsRevisable
3
+ module VERSION #:nodoc:
4
+ MAJOR = 1
5
+ MINOR = 0
6
+ TINY = 3
7
+
8
+ STRING = [MAJOR, MINOR, TINY].join('.')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'activesupport' unless defined? ActiveSupport
5
+ require 'activerecord' unless defined? ActiveRecord
6
+
7
+ require 'acts_as_revisable/version.rb'
8
+ require 'acts_as_revisable/base'
9
+
10
+ ActiveRecord::Base.send(:include, WithoutScope::ActsAsRevisable)
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'acts_as_revisable'
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe WithoutScope::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
@@ -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 WithoutScope::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
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe WithoutScope::ActsAsRevisable::Deletable do
4
+ after(:each) do
5
+ cleanup_db
6
+ end
7
+
8
+ before(:each) do
9
+ @person = Person.create(:name => "Rich", :notes => "a note")
10
+ @person.update_attribute(:name, "Sam")
11
+ end
12
+
13
+ it "should store a revision on destroy" do
14
+ lambda{ @person.destroy }.should change(OldPerson, :count).from(1).to(2)
15
+ end
16
+ end
data/spec/find_spec.rb ADDED
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe WithoutScope::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 find current and revisions with the :with_revisions option" do
23
+ Project.find(:all, :with_revisions => true).size.should == 2
24
+ end
25
+
26
+ it "should find revisions with conditions" do
27
+ Project.find(:all, :conditions => {:name => "Rich"}, :with_revisions => true).should == [@project1.find_revision(:previous)]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe WithoutScope::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
+ end
11
+
12
+ describe "with auto-generated revision class" do
13
+ it "should have a revision class" do
14
+ Foo.revision_class.should == FooRevision
15
+ end
16
+ end
17
+
18
+ describe "without revisions" do
19
+ it "should have a revision_number of zero" do
20
+ @project.revision_number.should be_zero
21
+ end
22
+
23
+ it "should be the current revision" do
24
+ @project.revisable_is_current.should be_true
25
+ end
26
+
27
+ it "should respond to current_revision? positively" do
28
+ @project.current_revision?.should be_true
29
+ end
30
+
31
+ it "should not have any revisions in the generic association" do
32
+ @project.revisions.should be_empty
33
+ end
34
+
35
+ it "should not have any revisions in the pretty named association" do
36
+ @project.sessions.should be_empty
37
+ end
38
+ end
39
+
40
+ describe "with revisions" do
41
+ before(:each) do
42
+ @project.update_attribute(:name, "Stephen")
43
+ end
44
+
45
+ it "should have a revision_number of one" do
46
+ @project.revision_number.should == 1
47
+ end
48
+
49
+ it "should have a single revision in the generic association" do
50
+ @project.revisions.size.should == 1
51
+ end
52
+
53
+ it "should have a single revision in the pretty named association" do
54
+ @project.sessions.size.should == 1
55
+ end
56
+
57
+ it "should return an instance of the revision class" do
58
+ @project.revisions.first.should be_an_instance_of(Session)
59
+ end
60
+
61
+ it "should have the original revision's data" do
62
+ @project.revisions.first.name.should == "Rich"
63
+ end
64
+ end
65
+
66
+ describe "with excluded columns modified" do
67
+ before(:each) do
68
+ @project.update_attribute(:unimportant, "a new value")
69
+ end
70
+
71
+ it "should maintain the revision_number at zero" do
72
+ @project.revision_number.should be_zero
73
+ end
74
+
75
+ it "should not have any revisions" do
76
+ @project.revisions.should be_empty
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,83 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ shared_examples_for "common Options usage" do
4
+ it "should return a set value" do
5
+ @options.one.should == 1
6
+ end
7
+
8
+ it "should return nil for an unset value" do
9
+ @options.two.should be_nil
10
+ end
11
+
12
+ it "should return false for unset query option" do
13
+ @options.should_not be_unset_value
14
+ end
15
+
16
+ it "should return true for a query option set to true" do
17
+ @options.should be_yes
18
+ end
19
+
20
+ it "should return false for a query option set to false" do
21
+ @options.should_not be_no
22
+ end
23
+
24
+ it "should return false for a query on a non-boolean value" do
25
+ @options.should_not be_one
26
+ end
27
+
28
+ it "should return an array when passed one" do
29
+ @options.arr.should be_a_kind_of(Array)
30
+ end
31
+
32
+ it "should not return an array when not passed one" do
33
+ @options.one.should_not be_a_kind_of(Array)
34
+ end
35
+
36
+ it "should have the right number of elements in an array" do
37
+ @options.arr.size.should == 3
38
+ end
39
+ end
40
+
41
+ describe WithoutScope::ActsAsRevisable::Options do
42
+ describe "with hash options" do
43
+ before(:each) do
44
+ @options = WithoutScope::ActsAsRevisable::Options.new :one => 1, :yes => true, :no => false, :arr => [1,2,3]
45
+ end
46
+
47
+ it_should_behave_like "common Options usage"
48
+ end
49
+
50
+ describe "with block options" do
51
+ before(:each) do
52
+ @options = WithoutScope::ActsAsRevisable::Options.new do
53
+ one 1
54
+ yes true
55
+ arr [1,2,3]
56
+ end
57
+ end
58
+
59
+ it_should_behave_like "common Options usage"
60
+ end
61
+
62
+ describe "with both block and hash options" do
63
+ before(:each) do
64
+ @options = WithoutScope::ActsAsRevisable::Options.new(:yes => true, :arr => [1,2,3]) do
65
+ one 1
66
+ end
67
+ end
68
+
69
+ it_should_behave_like "common Options usage"
70
+
71
+ describe "the block should override the hash" do
72
+ before(:each) do
73
+ @options = WithoutScope::ActsAsRevisable::Options.new(:yes => false, :one => 10, :arr => [1,2,3,4,5]) do
74
+ one 1
75
+ yes true
76
+ arr [1,2,3]
77
+ end
78
+ end
79
+
80
+ it_should_behave_like "common Options usage"
81
+ end
82
+ end
83
+ 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 == ActiveRecord::Base.send(:quote_bound_value, "what")
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe WithoutScope::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.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,93 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ if ENV['EDGE_RAILS_PATH']
10
+ edge_path = File.expand_path(ENV['EDGE_RAILS_PATH'])
11
+ require File.join(edge_path, 'activesupport', 'lib', 'active_support')
12
+ require File.join(edge_path, 'activerecord', 'lib', 'active_record')
13
+ end
14
+
15
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
16
+ require 'acts_as_revisable'
17
+
18
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
19
+
20
+ def setup_db
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
+
31
+ create_table :projects do |t|
32
+ t.string :name, :unimportant, :revisable_name, :revisable_type
33
+ t.text :notes
34
+ t.boolean :revisable_is_current
35
+ t.integer :revisable_original_id, :revisable_branched_from_id, :revisable_number
36
+ t.datetime :revisable_current_at, :revisable_revised_at, :revisable_deleted_at
37
+ t.timestamps
38
+ end
39
+
40
+ create_table :foos do |t|
41
+ t.string :name, :revisable_name, :revisable_type
42
+ t.text :notes
43
+ t.boolean :revisable_is_current
44
+ t.integer :revisable_original_id, :revisable_branched_from_id, :revisable_number, :project_id
45
+ t.datetime :revisable_current_at, :revisable_revised_at, :revisable_deleted_at
46
+ t.timestamps
47
+ end
48
+ end
49
+ end
50
+
51
+ setup_db
52
+
53
+ def cleanup_db
54
+ ActiveRecord::Base.connection.tables.each do |table|
55
+ ActiveRecord::Base.connection.execute("delete from #{table}")
56
+ end
57
+ end
58
+
59
+ class Person < ActiveRecord::Base
60
+ belongs_to :project
61
+
62
+ acts_as_revisable do
63
+ revision_class_name "OldPerson"
64
+ on_delete :revise
65
+ end
66
+ end
67
+
68
+ class OldPerson < ActiveRecord::Base
69
+ acts_as_revision do
70
+ revisable_class_name "Person"
71
+ clone_associations :all
72
+ end
73
+ end
74
+
75
+ class Project < ActiveRecord::Base
76
+ has_many :people
77
+
78
+ acts_as_revisable do
79
+ revision_class_name "Session"
80
+ except :unimportant
81
+ end
82
+ end
83
+
84
+ class Session < ActiveRecord::Base
85
+ acts_as_revision do
86
+ revisable_class_name "Project"
87
+ clone_associations :all
88
+ end
89
+ end
90
+
91
+ class Foo < ActiveRecord::Base
92
+ acts_as_revisable :generate_revision_class => true
93
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kb-acts_as_revisable
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Rich Cavanaugh
8
+ - Stephen Caudill
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-05-16 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description:
18
+ email: rich@withoutscope.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README.rdoc
25
+ - LICENSE
26
+ files:
27
+ - LICENSE
28
+ - README.rdoc
29
+ - Rakefile
30
+ - spec/associations_spec.rb
31
+ - spec/branch_spec.rb
32
+ - spec/deletable_spec.rb
33
+ - spec/find_spec.rb
34
+ - spec/general_spec.rb
35
+ - spec/options_spec.rb
36
+ - spec/quoted_columns_spec.rb
37
+ - spec/revert_spec.rb
38
+ - spec/spec.opts
39
+ - spec/spec_helper.rb
40
+ - lib/acts_as_revisable
41
+ - lib/acts_as_revisable/acts
42
+ - lib/acts_as_revisable/acts/common.rb
43
+ - lib/acts_as_revisable/acts/deletable.rb
44
+ - lib/acts_as_revisable/acts/revisable.rb
45
+ - lib/acts_as_revisable/acts/revision.rb
46
+ - lib/acts_as_revisable/base.rb
47
+ - lib/acts_as_revisable/gem_spec_options.rb
48
+ - lib/acts_as_revisable/options.rb
49
+ - lib/acts_as_revisable/quoted_columns.rb
50
+ - lib/acts_as_revisable/version.rb
51
+ - lib/acts_as_revisable.rb
52
+ - generators/revisable_migration
53
+ - generators/revisable_migration/revisable_migration_generator.rb
54
+ - generators/revisable_migration/templates
55
+ - generators/revisable_migration/templates/migration.rb
56
+ - rails/init.rb
57
+ has_rdoc: true
58
+ homepage: http://github.com/rich/acts_as_revisable
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --main
62
+ - README.rdoc
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.2.0
81
+ signing_key:
82
+ specification_version: 2
83
+ summary: acts_as_revisable enables revision tracking, querying, reverting and branching of ActiveRecord models. Inspired by acts_as_versioned.
84
+ test_files: []
85
+