mover 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mover do
4
+
5
+ describe :move_to do
6
+
7
+ before(:each) do
8
+ [ 1, 0, 1 ].each { |v| $db.migrate(v) }
9
+ @articles = create_records(Article)
10
+ @comments = create_records(Comment)
11
+ @articles[0].move_to(ArticleArchive)
12
+ Article.move_to(ArticleArchive, [ 'id = ?', 2 ])
13
+ end
14
+
15
+ describe 'move records' do
16
+
17
+ it "should move both articles and their associations" do
18
+ Article.count.should == 3
19
+ Comment.count.should == 3
20
+ ArticleArchive.count.should == 2
21
+ CommentArchive.count.should == 2
22
+ Article.find_by_id(1).nil?.should == true
23
+ Comment.find_by_id(2).nil?.should == true
24
+ ArticleArchive.find_by_id(1).nil?.should == false
25
+ CommentArchive.find_by_id(2).nil?.should == false
26
+ comments = ArticleArchive.find_by_id(1).comments
27
+ comments.length.should == 1
28
+ comments.first.id.should == 1
29
+ comments = ArticleArchive.find_by_id(2).comments
30
+ comments.length.should == 1
31
+ comments.first.id.should == 2
32
+ end
33
+
34
+ it "should assign moved_at" do
35
+ ArticleArchive.find_by_id(1).moved_at.utc.to_s.should == Time.now.utc.to_s
36
+ end
37
+ end
38
+
39
+ describe 'move records back' do
40
+
41
+ before(:each) do
42
+ ArticleArchive.find(1).move_to(Article)
43
+ ArticleArchive.move_to(Article, [ 'id = ?', 2 ])
44
+ end
45
+
46
+ it "should move both articles and their associations" do
47
+ Article.count.should == 5
48
+ Comment.count.should == 5
49
+ ArticleArchive.count.should == 0
50
+ CommentArchive.count.should == 0
51
+ Article.find_by_id(1).nil?.should == false
52
+ Comment.find_by_id(2).nil?.should == false
53
+ ArticleArchive.find_by_id(1).nil?.should == true
54
+ CommentArchive.find_by_id(2).nil?.should == true
55
+ comments = Article.find_by_id(1).comments
56
+ comments.length.should == 1
57
+ comments.first.id.should == 1
58
+ comments = Article.find_by_id(2).comments
59
+ comments.length.should == 1
60
+ comments.first.id.should == 2
61
+ end
62
+ end
63
+ end
64
+
65
+ describe :reserve_id do
66
+ it "should return an id" do
67
+ Article.reserve_id.class.should == Fixnum
68
+ end
69
+
70
+ it "should delete the record" do
71
+ id = Article.reserve_id
72
+ Article.find_by_id(id).should == nil
73
+ end
74
+ end
75
+ end
data/spec/spec_helper.rb CHANGED
@@ -24,7 +24,7 @@ def connection
24
24
  ActiveRecord::Base.connection
25
25
  end
26
26
 
27
- def create_records(klass=Article, values={})
27
+ def create_records(klass, values={})
28
28
  klass.delete_all
29
29
  (1..5).collect do |x|
30
30
  klass.column_names.each do |column|
@@ -37,12 +37,4 @@ def create_records(klass=Article, values={})
37
37
  values[:id] = x
38
38
  klass.create(values)
39
39
  end
40
- end
41
-
42
- def migrate_with_state(version)
43
- @old_article_columns = columns("articles")
44
- @old_archive_columns = columns("articles_archive")
45
- $db.migrate(version)
46
- @new_article_columns = columns("articles")
47
- @new_archive_columns = columns("articles_archive")
48
40
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Winton Welsh
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-04-01 00:00:00 -07:00
12
+ date: 2010-04-02 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -32,26 +32,20 @@ extra_rdoc_files:
32
32
  - README.markdown
33
33
  files:
34
34
  - init.rb
35
- - lib/mover/migrator.rb
36
- - lib/mover/record.rb
37
- - lib/mover/table.rb
38
35
  - lib/mover.rb
39
- - log/development.log
40
36
  - MIT-LICENSE
41
37
  - rails/init.rb
42
38
  - Rakefile
43
39
  - README.markdown
44
40
  - require.rb
45
41
  - spec/config/database.yml.example
46
- - spec/db/migrate/001_create_articles.rb
47
- - spec/db/migrate/002_add_permalink.rb
48
- - spec/db/migrate/003_remove_magic_columns.rb
42
+ - spec/db/migrate/001_create_fixtures.rb
49
43
  - spec/fixtures/article.rb
44
+ - spec/fixtures/article_archive.rb
50
45
  - spec/fixtures/comment.rb
46
+ - spec/fixtures/comment_archive.rb
51
47
  - spec/log/test.log
52
- - spec/mover/migrator_spec.rb
53
- - spec/mover/record_spec.rb
54
- - spec/mover/table_spec.rb
48
+ - spec/mover_spec.rb
55
49
  - spec/Rakefile
56
50
  - spec/spec_helper.rb
57
51
  has_rdoc: true
@@ -1,44 +0,0 @@
1
- module Mover
2
- module Migrator
3
-
4
- def method_missing_with_mover(method, *arguments, &block)
5
- args = Marshal.load(Marshal.dump(arguments))
6
- method_missing_without_mover(method, *arguments, &block)
7
-
8
- supported = [
9
- :add_column, :add_timestamps, :change_column,
10
- :change_column_default, :change_table,
11
- :drop_table, :remove_column, :remove_columns,
12
- :remove_timestamps, :rename_column, :rename_table
13
- ]
14
-
15
- # Don't change these columns
16
- %w(moved_at move_id).each do |column|
17
- return if args.include?(column) || args.include?(column.intern)
18
- end
19
-
20
- if !args.empty? && supported.include?(method)
21
- connection = ActiveRecord::Base.connection
22
- table_name = ActiveRecord::Migrator.proper_table_name(args[0])
23
- # Find model
24
- klass = Object.subclasses_of(ActiveRecord::Base).detect do |klass|
25
- if klass.respond_to?(:movable_types)
26
- klass.table_name.to_s == table_name
27
- end
28
- end
29
- # Run migration on movable table
30
- if klass
31
- klass.movable_types.each do |type|
32
- args[0] = [ table_name, type ].join('_')
33
- if method == :rename_table
34
- args[1] = [ args[1].to_s, type ].join('_')
35
- end
36
- if connection.table_exists?(args[0])
37
- connection.send(method, *args, &block)
38
- end
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end
data/lib/mover/record.rb DELETED
@@ -1,114 +0,0 @@
1
- module Mover
2
- module Base
3
- module Record
4
- module ClassMethods
5
-
6
- def move_from(type, conditions)
7
- klass = movable_class(type)
8
- if klass
9
- if klass.column_names.include?('move_id')
10
- klass.find_each(:conditions => conditions) do |record|
11
- record.move_from
12
- end
13
- else
14
- execute_move(klass, self, conditions)
15
- end
16
- end
17
- end
18
-
19
- def move_to(type, conditions)
20
- if movable_class(type).column_names.include?('move_id')
21
- self.find_each(:conditions => conditions) do |record|
22
- record.move_to(type)
23
- end
24
- else
25
- execute_move(self, movable_class(type), conditions)
26
- end
27
- end
28
-
29
- private
30
-
31
- def execute_move(from_class, to_class, conditions, &block)
32
- add_conditions! where = '', conditions
33
- insert = from_class.column_names & to_class.column_names
34
- insert.collect! { |col| connection.quote_column_name(col) }
35
- select = insert.clone
36
- yield(insert, select) if block_given?
37
- if to_class.column_names.include?('moved_at')
38
- insert << connection.quote_column_name('moved_at')
39
- select << connection.quote(Time.now)
40
- end
41
- connection.execute(<<-SQL)
42
- INSERT INTO #{to_class.table_name} (#{insert.join(', ')})
43
- SELECT #{select.join(', ')}
44
- FROM #{from_class.table_name}
45
- #{where}
46
- SQL
47
- connection.execute("DELETE FROM #{from_class.table_name} #{where}")
48
- end
49
-
50
- def movable_class(type)
51
- eval(self.name + type.to_s.classify)
52
- rescue
53
- raise "#{self.table_name.classify} needs an `is_movable :#{type}` definition"
54
- end
55
- end
56
-
57
- module InstanceMethods
58
-
59
- def move_from
60
- return unless self.respond_to?(:moved_from_class)
61
- # Move associations
62
- moved_from_class.reflect_on_all_associations.each do |association|
63
- if move_association?(association)
64
- klass = association.klass.send(:movable_class, self.class.movable_type)
65
- klass.find_each(:conditions => [ 'move_id = ?', self.move_id ]) do |record|
66
- record.move_from
67
- end
68
- end
69
- end
70
- # Move record
71
- conditions = "#{self.class.primary_key} = #{id}"
72
- moved_from_class.send(:execute_move, self.class, moved_from_class, conditions)
73
- end
74
-
75
- def move_to(type)
76
- return if self.respond_to?(:moved_from_class)
77
- klass = self.class.send :movable_class, type
78
- if klass
79
- # Create movable_id
80
- if !self.movable_id && klass.column_names.include?('move_id')
81
- self.movable_id = Digest::MD5.hexdigest("#{self.class.name}#{self.id}")
82
- end
83
- # Move associations
84
- self.class.reflect_on_all_associations.each do |association|
85
- if move_association?(association)
86
- self.send(association.name).each do |record|
87
- record.movable_id = self.movable_id
88
- record.move_to(type)
89
- end
90
- end
91
- end
92
- # Move record
93
- me = self
94
- conditions = "#{self.class.primary_key} = #{id}"
95
- self.class.send(:execute_move, self.class, klass, conditions) do |insert, select|
96
- if me.movable_id
97
- insert << connection.quote_column_name('move_id')
98
- select << connection.quote(self.movable_id)
99
- end
100
- end
101
- self.movable_id = nil
102
- end
103
- end
104
-
105
- private
106
-
107
- def move_association?(association)
108
- association.klass.respond_to?(:movable_types) &&
109
- association.macro.to_s =~ /^has/
110
- end
111
- end
112
- end
113
- end
114
- end
data/lib/mover/table.rb DELETED
@@ -1,82 +0,0 @@
1
- module Mover
2
- module Base
3
- module Table
4
-
5
- def create_movable_table(type, options={})
6
- movable_table = [ table_name, type ].join('_')
7
- columns =
8
- if options[:columns]
9
- options[:columns].collect { |c| "`#{c}`" }.join(', ')
10
- else
11
- '*'
12
- end
13
- engine = options[:engine]
14
- engine ||=
15
- if connection.class.to_s.include?('Mysql')
16
- "ENGINE=InnoDB"
17
- end
18
- if table_exists? and !connection.table_exists?(movable_table)
19
- # Create table
20
- connection.execute(<<-SQL)
21
- CREATE TABLE #{movable_table} #{engine}
22
- AS SELECT #{columns}
23
- FROM #{table_name}
24
- WHERE false;
25
- SQL
26
- # Create indexes
27
- options[:indexes] ||= indexed_columns(table_name)
28
- options[:indexes].each do |column|
29
- connection.add_index(movable_table, column)
30
- end
31
- end
32
- end
33
-
34
- def drop_movable_table(*types)
35
- types.each do |type|
36
- connection.execute("DROP TABLE IF EXISTS #{[ table_name, type ].join('_')}")
37
- end
38
- end
39
-
40
- private
41
-
42
- def indexed_columns(table_name)
43
- # MySQL
44
- if connection.class.to_s.include?('Mysql')
45
- index_query = "SHOW INDEX FROM #{table_name}"
46
- indexes = connection.select_all(index_query).collect do |r|
47
- r["Column_name"]
48
- end
49
- # PostgreSQL
50
- # http://stackoverflow.com/questions/2204058/show-which-columns-an-index-is-on-in-postgresql/2213199
51
- elsif connection.class.to_s.include?('PostgreSQL')
52
- index_query = <<-SQL
53
- select
54
- t.relname as table_name,
55
- i.relname as index_name,
56
- a.attname as column_name
57
- from
58
- pg_class t,
59
- pg_class i,
60
- pg_index ix,
61
- pg_attribute a
62
- where
63
- t.oid = ix.indrelid
64
- and i.oid = ix.indexrelid
65
- and a.attrelid = t.oid
66
- and a.attnum = ANY(ix.indkey)
67
- and t.relkind = 'r'
68
- and t.relname = '#{table_name}'
69
- order by
70
- t.relname,
71
- i.relname
72
- SQL
73
- indexes = connection.select_all(index_query).collect do |r|
74
- r["column_name"]
75
- end
76
- else
77
- raise 'Mover does not support this database adapter'
78
- end
79
- end
80
- end
81
- end
82
- end
data/log/development.log DELETED
@@ -1,6 +0,0 @@
1
- SQL (0.2ms) SET SQL_AUTO_IS_NULL=0
2
- User Load (59.6ms) SELECT * FROM `users` ORDER BY users.id DESC LIMIT 1
3
- User Columns (21.6ms) SHOW FIELDS FROM `users`
4
- SQL (0.3ms) SET SQL_AUTO_IS_NULL=0
5
- SQL (0.3ms) SHOW TABLES
6
- User Columns (13.0ms) SHOW FIELDS FROM `users`
@@ -1,34 +0,0 @@
1
- class CreateArticles < ActiveRecord::Migration
2
- def self.up
3
- create_table :articles do |t|
4
- t.string :title
5
- t.string :body
6
- t.boolean :read
7
- end
8
- add_index :articles, :title
9
-
10
- Article.create_movable_table(:archive)
11
- add_column :articles_archive, :move_id, :string
12
- add_column :articles_archive, :moved_at, :datetime
13
-
14
- Article.create_movable_table(:draft)
15
-
16
- create_table :comments do |t|
17
- t.string :title
18
- t.string :body
19
- t.boolean :read
20
- t.integer :article_id
21
- end
22
-
23
- Comment.create_movable_table(:archive)
24
- add_column :comments_archive, :move_id, :string
25
- add_column :comments_archive, :moved_at, :datetime
26
- end
27
-
28
- def self.down
29
- drop_table :articles
30
- drop_table :comments
31
- Article.drop_movable_table(:archive)
32
- Comment.drop_movable_table(:archive)
33
- end
34
- end
@@ -1,9 +0,0 @@
1
- class AddPermalink < ActiveRecord::Migration
2
- def self.up
3
- add_column :articles, :permalink, :string
4
- end
5
-
6
- def self.down
7
- remove_column :articles, :permalink
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- class RemoveMagicColumns < ActiveRecord::Migration
2
- def self.up
3
- remove_column :articles, :move_id
4
- remove_column :articles, :moved_at
5
- end
6
-
7
- def self.down
8
- end
9
- end
@@ -1,34 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
-
3
- describe Mover::Migrator do
4
-
5
- before(:each) do
6
- $db.migrate(1)
7
- $db.migrate(0)
8
- $db.migrate(1)
9
- end
10
-
11
- describe :method_missing_with_mover do
12
-
13
- it 'should migrate both tables up' do
14
- migrate_with_state(2)
15
- (@new_article_columns - @old_article_columns).should == [ 'permalink' ]
16
- (@new_archive_columns - @old_archive_columns).should == [ 'permalink' ]
17
- end
18
-
19
- it 'should migrate both tables down' do
20
- $db.migrate(2)
21
- migrate_with_state(1)
22
- (@old_article_columns - @new_article_columns).should == [ 'permalink' ]
23
- (@old_archive_columns - @new_archive_columns).should == [ 'permalink' ]
24
- end
25
-
26
- it "should not touch the archive's move_id or moved_at column" do
27
- connection.add_column(:articles, :move_id, :integer)
28
- connection.add_column(:articles, :moved_at, :datetime)
29
- migrate_with_state(3)
30
- (@old_article_columns - @new_article_columns).should == [ 'move_id', 'moved_at' ]
31
- (@old_archive_columns - @new_archive_columns).should == []
32
- end
33
- end
34
- end
@@ -1,156 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
-
3
- describe Mover::Base::Record do
4
-
5
- before(:all) do
6
- $db.migrate(1)
7
- $db.migrate(0)
8
- $db.migrate(1)
9
- end
10
-
11
- describe :InstanceMethods do
12
- describe :move_to do
13
-
14
- before(:all) do
15
- @articles = create_records
16
- @comments = create_records(Comment)
17
- @articles[0..1].each do |a|
18
- a.move_to(:archive)
19
- end
20
- end
21
-
22
- it "should move some records to the archive table" do
23
- Article.count.should == 3
24
- ArticleArchive.count.should == 2
25
- end
26
-
27
- it "should preserve record attributes" do
28
- 2.times do |x|
29
- original = @articles[x]
30
- copy = ArticleArchive.find(original.id)
31
- record_match?(original, copy)
32
- end
33
- end
34
-
35
- it "should move associated records" do
36
- Comment.count.should == 3
37
- CommentArchive.count.should == 2
38
- end
39
-
40
- it "should preserve associated record attributes" do
41
- 2.times do |x|
42
- original = @comments[x]
43
- copy = CommentArchive.find(original.id)
44
- record_match?(original, copy)
45
- end
46
- end
47
-
48
- it "should populate move_id" do
49
- (1..2).each do |x|
50
- article = ArticleArchive.find(x)
51
- comment = CommentArchive.find(x)
52
- comment.move_id.nil?.should == false
53
- comment.move_id.length.should == 32
54
- comment.move_id.should == article.move_id
55
- end
56
- end
57
-
58
- it "should populate moved_at" do
59
- (1..2).each do |x|
60
- article = ArticleArchive.find(x)
61
- comment = CommentArchive.find(x)
62
- comment.moved_at.nil?.should == false
63
- comment.moved_at.should == article.moved_at
64
- end
65
- end
66
- end
67
-
68
- describe :move_from do
69
-
70
- before(:all) do
71
- articles = create_records
72
- create_records(Comment)
73
- articles[0..1].each do |a|
74
- a.move_to(:archive)
75
- end
76
- @articles = ArticleArchive.find(1, 2)
77
- @comments = CommentArchive.find(1, 2)
78
- @articles.each do |article|
79
- article.move_from
80
- end
81
- end
82
-
83
- it "should move records back to the original table" do
84
- Article.count.should == 5
85
- ArticleArchive.count.should == 0
86
- end
87
-
88
- it "should preserve record attributes" do
89
- 2.times do |x|
90
- original = @articles[x]
91
- copy = Article.find(original.id)
92
- record_match?(original, copy)
93
- end
94
- end
95
-
96
- it "should move associated records" do
97
- Comment.count.should == 5
98
- CommentArchive.count.should == 0
99
- end
100
-
101
- it "should preserve associated record attributes" do
102
- 2.times do |x|
103
- original = @comments[x]
104
- copy = Comment.find(original.id)
105
- record_match?(original, copy)
106
- end
107
- end
108
- end
109
- end
110
-
111
- describe :ClassMethods do
112
- describe :move_to do
113
-
114
- before(:all) do
115
- create_records
116
- create_records(Comment)
117
- Article.move_to(:archive, [ 'id = ? OR id = ?', 1, 2 ])
118
- Article.move_to(:draft, [ 'id = ? OR id = ?', 3, 4 ])
119
- end
120
-
121
- it "should move the records" do
122
- Article.count.should == 1
123
- ArticleArchive.count.should == 2
124
- ArticleDraft.count.should == 2
125
- end
126
-
127
- it "should move associated records" do
128
- Comment.count.should == 3
129
- CommentArchive.count.should == 2
130
- end
131
- end
132
-
133
- describe :move_from do
134
-
135
- before(:all) do
136
- create_records
137
- create_records(Comment)
138
- Article.move_to(:archive, [ 'id = ? OR id = ?', 1, 2 ])
139
- Article.move_to(:draft, [ 'id = ? OR id = ?', 3, 4 ])
140
- Article.move_from(:archive, [ 'id = ? OR id = ?', 1, 2 ])
141
- Article.move_from(:draft, [ 'id = ? OR id = ?', 3, 4 ])
142
- end
143
-
144
- it "should move the records" do
145
- Article.count.should == 5
146
- ArticleArchive.count.should == 0
147
- ArticleDraft.count.should == 0
148
- end
149
-
150
- it "should move associated records" do
151
- Comment.count.should == 5
152
- CommentArchive.count.should == 0
153
- end
154
- end
155
- end
156
- end