mover 0.1.0
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/MIT-LICENSE +18 -0
- data/README.markdown +95 -0
- data/Rakefile +2 -0
- data/init.rb +1 -0
- data/lib/mover/migrator.rb +43 -0
- data/lib/mover/record.rb +114 -0
- data/lib/mover/table.rb +82 -0
- data/lib/mover.rb +69 -0
- data/log/development.log +6 -0
- data/rails/init.rb +2 -0
- data/require.rb +49 -0
- data/spec/Rakefile +12 -0
- data/spec/config/database.yml.example +6 -0
- data/spec/db/migrate/001_create_articles.rb +34 -0
- data/spec/db/migrate/002_add_permalink.rb +9 -0
- data/spec/db/migrate/003_remove_magic_columns.rb +9 -0
- data/spec/fixtures/article.rb +4 -0
- data/spec/fixtures/comment.rb +4 -0
- data/spec/log/test.log +17759 -0
- data/spec/mover/migrator_spec.rb +33 -0
- data/spec/mover/record_spec.rb +155 -0
- data/spec/mover/table_spec.rb +79 -0
- data/spec/spec_helper.rb +48 -0
- metadata +86 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2009 Winton Welsh
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
Mover
|
2
|
+
=====
|
3
|
+
|
4
|
+
Move ActiveRecord records across tables like it ain't no thang.
|
5
|
+
|
6
|
+
Requirements
|
7
|
+
------------
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
sudo gem install mover
|
11
|
+
</pre>
|
12
|
+
|
13
|
+
<a name="create_the_movable_table"></a>
|
14
|
+
|
15
|
+
Create the movable table
|
16
|
+
------------------------
|
17
|
+
|
18
|
+
Migration:
|
19
|
+
|
20
|
+
<pre>
|
21
|
+
class CreateArchivedArticles < ActiveRecord::Migration
|
22
|
+
def self.up
|
23
|
+
Article.create_movable_table(
|
24
|
+
:archived,
|
25
|
+
:columns => %w(id title body created_at),
|
26
|
+
:indexes => %w(id created_at)
|
27
|
+
)
|
28
|
+
add_column :archived_articles, :move_id, :string
|
29
|
+
add_column :archived_articles, :moved_at, :datetime
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.down
|
33
|
+
Article.drop_movable_table(:archived)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
</pre>
|
37
|
+
|
38
|
+
The first parameter names your movable table. In this example, the table is named <code>archived_articles</code>.
|
39
|
+
|
40
|
+
Options:
|
41
|
+
|
42
|
+
* <code>:columns</code> - Only use certain columns from the original table. Defaults to all.
|
43
|
+
* <code>:indexes</code> - Only create certain indexes. Defaults to all.
|
44
|
+
|
45
|
+
We also added two columns, <code>move\_id</code> and <code>moved\_at</code>. These are <a href="#magic_columns">magic columns</a>.
|
46
|
+
|
47
|
+
<a name="define_the_model"></a>
|
48
|
+
|
49
|
+
Define the model
|
50
|
+
----------------
|
51
|
+
|
52
|
+
<pre>
|
53
|
+
class Article < ActiveRecord::Base
|
54
|
+
is_movable :archived
|
55
|
+
end
|
56
|
+
</pre>
|
57
|
+
|
58
|
+
The <code>is_movable</code> method takes any number of parameters for multiple movable tables.
|
59
|
+
|
60
|
+
Moving records
|
61
|
+
--------------
|
62
|
+
|
63
|
+
<pre>
|
64
|
+
Article.last.move_to(:archived)
|
65
|
+
Article.move_to(:archived, [ "created_at > ?", Date.today ])
|
66
|
+
</pre>
|
67
|
+
|
68
|
+
Associations move if they are movable and if all movable tables have a <code>move_id</code> column (see <a href="#magic_columns">magic columns</a>).
|
69
|
+
|
70
|
+
Restoring records
|
71
|
+
-----------------
|
72
|
+
|
73
|
+
<pre>
|
74
|
+
Article.move_from(:archived, [ "created_at > ?", Date.today ])
|
75
|
+
ArchivedArticle.last.move_from
|
76
|
+
</pre>
|
77
|
+
|
78
|
+
You can access the movable table by prepending its name to the original class name. In this example, you would use <code>ArchivedArticle</code>.
|
79
|
+
|
80
|
+
<a name="magic_columns"></a>
|
81
|
+
|
82
|
+
Magic columns
|
83
|
+
-------------
|
84
|
+
|
85
|
+
### move_id
|
86
|
+
|
87
|
+
By default, restoring a record will only restore itself and not its movable relationships.
|
88
|
+
|
89
|
+
To restore the relationships automatically, add the <code>move_id</code> column to all movable tables involved.
|
90
|
+
|
91
|
+
### moved_at
|
92
|
+
|
93
|
+
If you need to know when the record was moved, add the <code>moved\_at</code> column to your movable table.
|
94
|
+
|
95
|
+
See the <a href="#create_the_movable_table">create the movable table</a> section for an example of how to add the magic columns.
|
data/Rakefile
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/rails/init"
|
@@ -0,0 +1,43 @@
|
|
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
|
+
supported = [
|
8
|
+
:add_column, :add_timestamps, :change_column,
|
9
|
+
:change_column_default, :change_table,
|
10
|
+
:drop_table, :remove_column, :remove_columns,
|
11
|
+
:remove_timestamps, :rename_column, :rename_table
|
12
|
+
]
|
13
|
+
|
14
|
+
# Don't change these columns
|
15
|
+
%w(moved_at move_id).each do |column|
|
16
|
+
return if args.include?(column) || args.include?(column.intern)
|
17
|
+
end
|
18
|
+
|
19
|
+
if !args.empty? && supported.include?(method)
|
20
|
+
connection = ActiveRecord::Base.connection
|
21
|
+
table_name = ActiveRecord::Migrator.proper_table_name(args[0])
|
22
|
+
# Find model
|
23
|
+
klass = Object.subclasses_of(ActiveRecord::Base).detect do |klass|
|
24
|
+
if klass.respond_to?(:movable_types)
|
25
|
+
klass.table_name.to_s == table_name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# Run migration on movable table
|
29
|
+
if klass
|
30
|
+
klass.movable_types.each do |type|
|
31
|
+
args[0] = [ type, table_name ].join('_')
|
32
|
+
if method == :rename_table
|
33
|
+
args[1] = [ type, args[1].to_s ].join('_')
|
34
|
+
end
|
35
|
+
if connection.table_exists?(args[0])
|
36
|
+
connection.send(method, *args, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/mover/record.rb
ADDED
@@ -0,0 +1,114 @@
|
|
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(type.to_s.classify + self.table_name.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
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Mover
|
2
|
+
module Base
|
3
|
+
module Table
|
4
|
+
|
5
|
+
def create_movable_table(type, options={})
|
6
|
+
movable_table = [ type, table_name ].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 #{[ type, table_name ].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/lib/mover.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../require")
|
2
|
+
Require.lib!
|
3
|
+
|
4
|
+
module Mover
|
5
|
+
module Base
|
6
|
+
def self.included(base)
|
7
|
+
unless base.included_modules.include?(Included)
|
8
|
+
base.extend ClassMethods
|
9
|
+
base.send :include, Included
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def is_movable(*types)
|
15
|
+
@movable_types = types
|
16
|
+
|
17
|
+
self.class_eval do
|
18
|
+
attr_accessor :movable_id
|
19
|
+
class <<self
|
20
|
+
attr_reader :movable_types
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
types.each do |type|
|
25
|
+
eval <<-RUBY
|
26
|
+
class ::#{type.to_s.classify}#{self.table_name.classify} < ActiveRecord::Base
|
27
|
+
include Mover::Base::Record::InstanceMethods
|
28
|
+
|
29
|
+
self.table_name = "#{type}_#{self.table_name}"
|
30
|
+
|
31
|
+
def self.movable_type
|
32
|
+
#{type.inspect}
|
33
|
+
end
|
34
|
+
|
35
|
+
def moved_from_class
|
36
|
+
#{self.table_name.classify}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
end
|
41
|
+
|
42
|
+
extend Table
|
43
|
+
extend Record::ClassMethods
|
44
|
+
include Record::InstanceMethods
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module Migration
|
50
|
+
def self.included(base)
|
51
|
+
unless base.included_modules.include?(Included)
|
52
|
+
base.extend Migrator
|
53
|
+
base.send :include, Included
|
54
|
+
base.class_eval do
|
55
|
+
class <<self
|
56
|
+
alias_method :method_missing_without_mover, :method_missing
|
57
|
+
alias_method :method_missing, :method_missing_with_mover
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module Included
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
ActiveRecord::Base.send(:include, Mover::Base)
|
69
|
+
ActiveRecord::Migration.send(:include, Mover::Migration)
|
data/log/development.log
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSET SQL_AUTO_IS_NULL=0[0m
|
2
|
+
[4;35;1mUser Load (59.6ms)[0m [0mSELECT * FROM `users` ORDER BY users.id DESC LIMIT 1[0m
|
3
|
+
[4;36;1mUser Columns (21.6ms)[0m [0;1mSHOW FIELDS FROM `users`[0m
|
4
|
+
[4;36;1mSQL (0.3ms)[0m [0;1mSET SQL_AUTO_IS_NULL=0[0m
|
5
|
+
[4;35;1mSQL (0.3ms)[0m [0mSHOW TABLES[0m
|
6
|
+
[4;36;1mUser Columns (13.0ms)[0m [0;1mSHOW FIELDS FROM `users`[0m
|
data/rails/init.rb
ADDED
data/require.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'require'
|
3
|
+
require 'require'
|
4
|
+
|
5
|
+
Require do
|
6
|
+
gem(:active_wrapper, '=0.2.3') { require 'active_wrapper' }
|
7
|
+
gem :require, '=0.2.6'
|
8
|
+
gem(:rake, '=0.8.7') { require 'rake' }
|
9
|
+
gem :rspec, '=1.3.0'
|
10
|
+
|
11
|
+
gemspec do
|
12
|
+
author 'Winton Welsh'
|
13
|
+
dependencies do
|
14
|
+
gem :require
|
15
|
+
end
|
16
|
+
email 'mail@wintoni.us'
|
17
|
+
name 'mover'
|
18
|
+
homepage "http://github.com/winton/#{name}"
|
19
|
+
summary "Move ActiveRecord records across tables like it ain't no thang"
|
20
|
+
version '0.1.0'
|
21
|
+
end
|
22
|
+
|
23
|
+
bin { require 'lib/mover' }
|
24
|
+
|
25
|
+
lib do
|
26
|
+
require 'digest/md5'
|
27
|
+
require 'lib/mover/migrator'
|
28
|
+
require 'lib/mover/record'
|
29
|
+
require 'lib/mover/table'
|
30
|
+
end
|
31
|
+
|
32
|
+
rakefile do
|
33
|
+
gem(:active_wrapper)
|
34
|
+
gem(:rake) { require 'rake/gempackagetask' }
|
35
|
+
gem(:rspec) { require 'spec/rake/spectask' }
|
36
|
+
require 'require/tasks'
|
37
|
+
end
|
38
|
+
|
39
|
+
rails_init { require 'lib/mover' }
|
40
|
+
|
41
|
+
spec_helper do
|
42
|
+
gem(:active_wrapper)
|
43
|
+
require 'require/spec_helper'
|
44
|
+
require 'rails/init'
|
45
|
+
require 'pp'
|
46
|
+
require 'spec/fixtures/article'
|
47
|
+
require 'spec/fixtures/comment'
|
48
|
+
end
|
49
|
+
end
|
data/spec/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
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(:archived)
|
11
|
+
add_column :archived_articles, :move_id, :string
|
12
|
+
add_column :archived_articles, :moved_at, :datetime
|
13
|
+
|
14
|
+
Article.create_movable_table(:drafted)
|
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(:archived)
|
24
|
+
add_column :archived_comments, :move_id, :string
|
25
|
+
add_column :archived_comments, :moved_at, :datetime
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.down
|
29
|
+
drop_table :articles
|
30
|
+
drop_table :comments
|
31
|
+
Article.drop_movable_table(:archived)
|
32
|
+
Comment.drop_movable_table(:archived)
|
33
|
+
end
|
34
|
+
end
|