git_friendly_dumper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +48 -0
- data/Rakefile +43 -0
- data/features/dump/change_dump_path.feature +26 -0
- data/features/dump/data_dump.feature +27 -0
- data/features/dump/default_dump.feature +39 -0
- data/features/dump/dump_and_raise_error.feature +58 -0
- data/features/dump/dump_deleted_records_and_clobber.feature +98 -0
- data/features/dump/dump_specific_tables.feature +39 -0
- data/features/load/clobber_load.feature +49 -0
- data/features/load/db_dump_path_load.feature +63 -0
- data/features/load/default_load.feature +103 -0
- data/features/load/load_and_raise_error.feature +97 -0
- data/features/load/load_fixtures.feature +159 -0
- data/features/load/load_git_changes.feature +17 -0
- data/features/load/load_specific_tables.feature +119 -0
- data/features/step_definitions/app_steps.rb +25 -0
- data/features/step_definitions/database_steps.rb +172 -0
- data/features/support/announce.rb +3 -0
- data/features/support/env.rb +2 -0
- data/features/support/logger.rb +7 -0
- data/lib/git_friendly_dumper.rb +279 -0
- data/lib/git_friendly_dumper/version.rb +3 -0
- data/lib/tasks/git_friendly_dumper_tasks.rake +51 -0
- data/spec/git_friendly_dumper_spec.rb +319 -0
- data/spec/resources/migrate/20070101000000_create_firsts.rb +11 -0
- data/spec/resources/migrate/20070101010000_create_seconds.rb +11 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/connect.rb +6 -0
- data/spec/support/logger.rb +7 -0
- metadata +196 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
Given /I am in an empty app/ do
|
2
|
+
steps %q{
|
3
|
+
Given a directory named "app"
|
4
|
+
Given I cd to "app"
|
5
|
+
}
|
6
|
+
in_current_dir { `ln -s ../../../lib lib` }
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /a Rakefile exists which has an environment task and loads git_friendly_dumper tasks/ do
|
10
|
+
steps %q{
|
11
|
+
Given a file named "Rakefile" with:
|
12
|
+
"""
|
13
|
+
$LOAD_PATH.unshift("lib")
|
14
|
+
require 'rake'
|
15
|
+
require 'active_record'
|
16
|
+
|
17
|
+
load "lib/tasks/git_friendly_dumper_tasks.rake"
|
18
|
+
|
19
|
+
task :environment do
|
20
|
+
require 'active_record'
|
21
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "test.sqlite3")
|
22
|
+
end
|
23
|
+
"""
|
24
|
+
}
|
25
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
Given /^there is a connection$/ do
|
2
|
+
ActiveRecord::Base.connection.should_not be_nil
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^an empty database$/ do
|
6
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "#{current_dir}/test.sqlite3")
|
7
|
+
db_table_names.each do |table|
|
8
|
+
ActiveRecord::Base.connection.drop_table table
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I refresh the database tables cache$/ do
|
13
|
+
# prevents exception SQLite3::SchemaChangedException: no such table: users: SELECT * FROM "users" (ActiveRecord::StatementInvalid)
|
14
|
+
# TODO: is there a public API for this?
|
15
|
+
ActiveRecord::Base.connection.tables
|
16
|
+
end
|
17
|
+
|
18
|
+
When(/^I execute the schema "([^"]*)"$/) do |schema_path|
|
19
|
+
schema_definition = File.read(File.join(current_dir, schema_path))
|
20
|
+
ActiveRecord::Migration.suppress_messages do
|
21
|
+
ActiveRecord::Schema.define do
|
22
|
+
eval schema_definition
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Then(/^a "([^"]*)" table should exist with structure:$/) do |table_name, table|
|
28
|
+
# table is a Cucumber::Ast::Table
|
29
|
+
# TODO: make this db independent, currenlty for sqlite3 only
|
30
|
+
table.diff! ActiveRecord::Base.connection.send :table_structure, table_name
|
31
|
+
end
|
32
|
+
|
33
|
+
Then /^list the table names$/ do
|
34
|
+
announce db_table_names.to_sentence
|
35
|
+
end
|
36
|
+
|
37
|
+
Then(/^the database should have tables:$/) do |table|
|
38
|
+
table.diff! db_table_names.map {|c| [c]}, :surplus_row => true, :surplus_col => true
|
39
|
+
end
|
40
|
+
|
41
|
+
Then(/^the database should not have table "([^"]*)"$/) do |table_name|
|
42
|
+
db_table_names.should_not include(table_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^show me the tables$/ do
|
46
|
+
db_table_names.each do |table_name|
|
47
|
+
pp table_name
|
48
|
+
pp(table_contents(table_name))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Then /^show me the "([^"]*)" table$/ do |table_name|
|
53
|
+
announce table_contents(table_name).to_yaml
|
54
|
+
end
|
55
|
+
|
56
|
+
Given /^the database has a "([^"]*)" table( \(with timestamps\))?:$/ do |table_name, timestamps, table|
|
57
|
+
create_table(table_name, timestamps)
|
58
|
+
|
59
|
+
columns = []
|
60
|
+
table.headers.each do |column_def|
|
61
|
+
raise "column_def should look like 'column_name (type)'" unless match = column_def.match(/(\w+) \((\w+)\)/)
|
62
|
+
add_column_to_table(table_name, match[1], match[2])
|
63
|
+
columns << match[1]
|
64
|
+
end
|
65
|
+
|
66
|
+
table.rows.each do |row|
|
67
|
+
attrs = {}
|
68
|
+
columns.each_with_index do |column_name, index|
|
69
|
+
attrs[column_name] = row[index]
|
70
|
+
end
|
71
|
+
insert_record_into_table(table_name, attrs)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
Then /^the "([^"]*)" table should match exactly:$/ do |table_name, table|
|
76
|
+
table.diff! table_to_strings(table_contents(table_name)), :surplus_col => true
|
77
|
+
end
|
78
|
+
|
79
|
+
Then /^the "([^"]*)" table should match exactly \(ignoring (ids)?(?: and )?(timestamps)?\):$/ do |table_name, ids, timestamps, table|
|
80
|
+
table.diff! table_to_strings(table_contents(table_name, :ids => !ids, :timestamps => !timestamps)), :surplus_col => true
|
81
|
+
end
|
82
|
+
|
83
|
+
When /^I destroy record (\d+) from the "([^"]*)" table$/ do |id, table_name|
|
84
|
+
class_for_table(table_name).destroy(id)
|
85
|
+
end
|
86
|
+
|
87
|
+
Then /^the data in the dumped "([^"]*)" yaml files should match the database contents$/ do |table_name|
|
88
|
+
records = class_for_table(table_name).all
|
89
|
+
fixtures = fixtures_for_table(table_name)
|
90
|
+
records.count.should == fixtures.length
|
91
|
+
records.each {|record| match_fixture_file_against_record(record)}
|
92
|
+
end
|
93
|
+
|
94
|
+
module FixtureHelpers
|
95
|
+
def fixture_path_for(record)
|
96
|
+
fixture_id_path = ("%08d" % record.id).scan(/..../).join('/')
|
97
|
+
File.join current_dir, "db/dump", record.class.table_name, "#{fixture_id_path}.yml"
|
98
|
+
end
|
99
|
+
|
100
|
+
def replace_record_with_fixture!(record)
|
101
|
+
require 'active_record/fixtures'
|
102
|
+
fixture_path =fixture_path_for(record)
|
103
|
+
fixture = ActiveRecord::Fixture.new(YAML.load(File.read(fixture_path)), record.class)
|
104
|
+
record.destroy
|
105
|
+
ActiveRecord::Base.connection.insert_fixture fixture, record.class.table_name
|
106
|
+
record.class.find(record.id)
|
107
|
+
end
|
108
|
+
|
109
|
+
def match_fixture_file_against_record(record)
|
110
|
+
record.attributes.dup.should == replace_record_with_fixture!(record).attributes
|
111
|
+
announce "#{table_name.singularize} #{record.id} data matches its fixture data #{fixture_path_for(record)}" if @announce
|
112
|
+
end
|
113
|
+
|
114
|
+
def fixtures_for_table(table_name)
|
115
|
+
fixtures = Dir.glob File.join(current_dir, "db/dump", table_name, '**', '*.yml')
|
116
|
+
announce "Fixtures for #{table_name}:\n#{fixtures.join("\n")}\n" if @announce
|
117
|
+
fixtures
|
118
|
+
end
|
119
|
+
end
|
120
|
+
World(FixtureHelpers)
|
121
|
+
|
122
|
+
module DatabaseHelpers
|
123
|
+
def create_table(name, timestamps = false)
|
124
|
+
ActiveRecord::Base.connection.create_table name do |t|
|
125
|
+
t.timestamps if timestamps
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_column_to_table(table_name, column_name, type)
|
130
|
+
ActiveRecord::Base.connection.change_table table_name do |t|
|
131
|
+
t.send type, column_name
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def class_for_table(table_name)
|
136
|
+
@class_for_table ||= {}
|
137
|
+
@class_for_table[table_name] ||= begin
|
138
|
+
Class.new(ActiveRecord::Base).tap {|klass| klass.table_name = table_name }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def insert_record_into_table(table_name, attrs)
|
143
|
+
class_for_table(table_name).create! attrs
|
144
|
+
end
|
145
|
+
|
146
|
+
#returns ['table', 'names'] with sqlite adapter
|
147
|
+
def db_table_names
|
148
|
+
ActiveRecord::Base.connection.tables
|
149
|
+
end
|
150
|
+
|
151
|
+
# table_contents 'users' # gives back everything
|
152
|
+
# table_contents 'users', :timestamps => false # without timestamps
|
153
|
+
# table_contents 'users', :ids => false # without ids
|
154
|
+
def table_contents(table_name, opts={:timestamps => true, :ids => true})
|
155
|
+
contents = class_for_table(table_name).all.map(&:attributes)
|
156
|
+
contents.tap do |contents|
|
157
|
+
contents.map{|c| c.delete('id')} unless opts[:ids]
|
158
|
+
contents.map{|c| c.delete('updated_at'); c.delete('created_at')} unless opts[:timestamps]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
#because cucumber table#diff! expects types to match and step transforms are silly
|
163
|
+
def table_to_strings(table)
|
164
|
+
table.each do |row|
|
165
|
+
row.each_pair do |key, value|
|
166
|
+
row[key] = value.is_a?(Time) ? value.utc.to_s(:db) : value.to_s
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
World(DatabaseHelpers)
|
@@ -0,0 +1,279 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'active_record/fixtures'
|
4
|
+
|
5
|
+
begin; require 'progressbar'; rescue MissingSourceFile; end
|
6
|
+
|
7
|
+
# Database independent and git friendly replacement for mysqldump for rails projects
|
8
|
+
class GitFriendlyDumper
|
9
|
+
include FileUtils
|
10
|
+
|
11
|
+
attr_accessor :root, :path, :connection, :tables, :force, :include_schema, :show_progress, :clobber_fixtures, :limit, :raise_error, :fixtures
|
12
|
+
alias_method :include_schema?, :include_schema
|
13
|
+
alias_method :clobber_fixtures?, :clobber_fixtures
|
14
|
+
alias_method :show_progress?, :show_progress
|
15
|
+
alias_method :force?, :force
|
16
|
+
alias_method :raise_error?, :raise_error
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def dump(options = {})
|
20
|
+
new(options).dump
|
21
|
+
end
|
22
|
+
|
23
|
+
def load(options = {})
|
24
|
+
new(options).load
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(options = {})
|
29
|
+
options.assert_valid_keys(:root, :path, :connection, :connection_name, :tables, :force, :include_schema, :show_progress, :clobber_fixtures, :limit, :raise_error, :fixtures, :fixtures_file)
|
30
|
+
|
31
|
+
self.root = options[:root] || (defined?(Rails) && Rails.root) || pwd
|
32
|
+
|
33
|
+
if options[:fixtures_file]
|
34
|
+
raise ArgumentError, "GitFriendlyDumper cannot specify both :fixtures and :fixtures_file" if options[:fixtures].present?
|
35
|
+
options[:fixtures] = File.read(options[:fixtures_file]).split("\n").map(&:squish).reject(&:blank?)
|
36
|
+
end
|
37
|
+
|
38
|
+
if options[:fixtures] && (options[:include_schema] || options[:clobber_fixtures])
|
39
|
+
raise ArgumentError, "GitFriendlyDumper if :fixtures option given, neither :include_schema nor :clobber_fixtures can be given"
|
40
|
+
end
|
41
|
+
|
42
|
+
if options[:show_progress] && !defined?(ProgressBar)
|
43
|
+
raise RuntimeError, "GitFriendlyDumper requires the progressbar gem for progress option.\n sudo gem install progressbar"
|
44
|
+
end
|
45
|
+
|
46
|
+
self.path = File.expand_path(options[:path] || 'db/dump')
|
47
|
+
self.tables = options[:tables]
|
48
|
+
self.fixtures = options[:fixtures]
|
49
|
+
self.limit = options.key?(:limit) ? options[:limit].to_i : 2500
|
50
|
+
self.raise_error = options.key?(:raise_error) ? options[:raise_error] : true
|
51
|
+
self.force = options.key?(:force) ? options[:force] : false
|
52
|
+
self.include_schema = options.key?(:include_schema) ? options[:include_schema] : false
|
53
|
+
self.show_progress = options.key?(:show_progress) ? options[:show_progress] : false
|
54
|
+
self.clobber_fixtures = options.key?(:clobber_fixtures) ? options[:clobber_fixtures] : (options[:tables].blank? ? true : false)
|
55
|
+
self.connection = options[:connection] || begin
|
56
|
+
if options[:connection_name]
|
57
|
+
ActiveRecord::Base.establish_connection(options[:connection_name])
|
58
|
+
end
|
59
|
+
ActiveRecord::Base.connection
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def dump
|
64
|
+
if fixtures
|
65
|
+
raise ArgumentError, "Cannot dump when :fixtures option is given"
|
66
|
+
end
|
67
|
+
self.tables ||= db_tables
|
68
|
+
tables.delete('schema_migrations') unless include_schema?
|
69
|
+
if force? || (tables & fixtures_tables).empty? || confirm?(:dump)
|
70
|
+
puts "Dumping data#{' and structure' if include_schema?} from #{current_database_name} to #{path.sub("#{root}/",'')}\n"
|
71
|
+
clobber_all_fixtures if clobber_fixtures?
|
72
|
+
connection.transaction do
|
73
|
+
tables.each {|table| dump_table(table) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def load
|
79
|
+
fixtures ? load_fixtures : load_tables
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def current_database_name
|
84
|
+
@current_database_name ||= (connection.respond_to?(:current_database) && connection.current_database) || 'database'
|
85
|
+
end
|
86
|
+
|
87
|
+
def confirm?(type)
|
88
|
+
dump_path = path.sub("#{root}/", '')
|
89
|
+
if clobber_fixtures? && type == :dump
|
90
|
+
puts "\nWARNING: all fixtures in #{dump_path}"
|
91
|
+
else
|
92
|
+
puts "\nWARNING: the following #{type == :dump ? 'fixtures' : 'tables'} in #{type == :dump ? dump_path : current_database_name}:"
|
93
|
+
puts " " + tables.join("\n ")
|
94
|
+
end
|
95
|
+
if fixtures
|
96
|
+
puts "will have records replaced by the specified #{fixtures.length} fixtures (deleting if fixture file is missing)"
|
97
|
+
else
|
98
|
+
puts "will be replaced with #{type == :dump ? 'records' : 'fixtures'}#{' and table schemas' if include_schema?} from #{type == :dump ? current_database_name : dump_path}."
|
99
|
+
end
|
100
|
+
puts "Do you wish to proceed? (type 'yes' to proceed)"
|
101
|
+
proceed = ($stdin.gets.downcase.strip == 'yes')
|
102
|
+
puts "#{type.to_s.capitalize} cancelled at user's request." unless proceed
|
103
|
+
proceed
|
104
|
+
end
|
105
|
+
|
106
|
+
def fixtures_tables
|
107
|
+
@fixture_tables ||= Dir[File.join(path, '*')].select{|f| File.directory?(f)}.map{|f| File.basename(f)}
|
108
|
+
end
|
109
|
+
|
110
|
+
def db_tables
|
111
|
+
@db_tables ||= connection.tables
|
112
|
+
end
|
113
|
+
|
114
|
+
def load_tables
|
115
|
+
self.tables ||= fixtures_tables
|
116
|
+
tables.delete('schema_migrations') unless include_schema?
|
117
|
+
if force? || (tables & db_tables).empty? || confirm?(:load)
|
118
|
+
puts "Loading data#{' and structure' if include_schema?} into #{current_database_name} from #{path.sub("#{root}/",'')}\n"
|
119
|
+
connection.transaction do
|
120
|
+
tables.each {|table| load_table(table) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def load_fixtures
|
126
|
+
fixtures_tables = []
|
127
|
+
fixtures.map! do |fixture|
|
128
|
+
raise ArgumentError, "Fixture filename error: #{fixture} should be a relative filename e.g. users/0000/0001.yml" unless fixture =~ /^\w+\/\d+\/\d+\.yml$/
|
129
|
+
table = fixture.split('/').first
|
130
|
+
if (!tables || tables.include?(table))
|
131
|
+
unless fixtures_tables.include?(table)
|
132
|
+
begin
|
133
|
+
"::#{table.classify}".constantize
|
134
|
+
rescue NameError
|
135
|
+
eval "class ::#{table.classify} < ActiveRecord::Base; end"
|
136
|
+
end
|
137
|
+
fixtures_tables << table
|
138
|
+
end
|
139
|
+
fixture
|
140
|
+
end
|
141
|
+
end
|
142
|
+
fixtures.compact!
|
143
|
+
|
144
|
+
self.tables = fixtures_tables
|
145
|
+
|
146
|
+
if force? || (tables & db_tables).empty? || confirm?(:load)
|
147
|
+
puts "Loading fixtures into #{current_database_name} from #{path.sub("#{root}/",'')}\n"
|
148
|
+
show_progress? && (progress_bar = ProgressBar.new("fixtures", fixtures.length))
|
149
|
+
connection.transaction do
|
150
|
+
fixtures.each do |fixture|
|
151
|
+
match_data = fixture.match(/(\w+)\/(.+)\.yml/)
|
152
|
+
table, id, file = match_data[1], match_data[2].sub('/','').to_i, File.join(path, fixture)
|
153
|
+
|
154
|
+
raise "Couldn't determine id from #{fixture} (id was #{id})" if id < 1
|
155
|
+
connection.delete("DELETE FROM #{table} WHERE id=#{id};")
|
156
|
+
load_fixture(table.classify.constantize, table, file) if File.exist?(file)
|
157
|
+
show_progress? && progress_bar.inc
|
158
|
+
end
|
159
|
+
end
|
160
|
+
show_progress && progress_bar.finish
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def dump_table(table)
|
165
|
+
clobber_fixtures_for_table(table)
|
166
|
+
count = connection.select_value("SELECT COUNT(*) FROM %s" % table).to_i
|
167
|
+
show_progress? && (progress_bar = ProgressBar.new(table, count))
|
168
|
+
|
169
|
+
offset = 0
|
170
|
+
while (records = select_records(table, offset)).any?
|
171
|
+
dump_records(table, records, show_progress? && progress_bar)
|
172
|
+
offset += limit
|
173
|
+
end
|
174
|
+
|
175
|
+
show_progress? && progress_bar.finish
|
176
|
+
dump_table_schema(table) if include_schema?
|
177
|
+
rescue ActiveRecord::ActiveRecordError => e
|
178
|
+
puts "dumping #{table} failed: #{e.message}"
|
179
|
+
puts "Partial dump files have been left behind and you should clean up before continuing (e.g. git status, git checkout, git clean)."
|
180
|
+
raise e if raise_error?
|
181
|
+
end
|
182
|
+
|
183
|
+
def select_records(table, offset)
|
184
|
+
connection.select_all("SELECT * FROM %s LIMIT #{limit} OFFSET #{offset}" % table)
|
185
|
+
end
|
186
|
+
|
187
|
+
def dump_records(table, records, progress_bar)
|
188
|
+
records.each_with_index do |record, index|
|
189
|
+
id = record['id'] ? record['id'].to_i : index + 1
|
190
|
+
fixture_file = File.join(path, table, *id_path(id)) + ".yml"
|
191
|
+
`mkdir -p #{File.dirname(fixture_file)}`
|
192
|
+
File.open(fixture_file, "w") do |record_file|
|
193
|
+
record_file.write record.to_yaml
|
194
|
+
end
|
195
|
+
show_progress? && progress_bar.inc
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def load_table(table)
|
200
|
+
# create a placeholder AR class for the table without loading anything from the app.
|
201
|
+
klass = eval "class #{table.classify} < ActiveRecord::Base; end"
|
202
|
+
include_schema? ? load_table_schema(table) : clobber_records(table)
|
203
|
+
files = Dir[File.join(path, table, '**', '*.yml')]
|
204
|
+
show_progress? && (progress_bar = ProgressBar.new(table, files.length))
|
205
|
+
files.each do |file|
|
206
|
+
load_fixture(klass, table, file)
|
207
|
+
show_progress? && progress_bar.inc
|
208
|
+
end
|
209
|
+
show_progress? && progress_bar.finish
|
210
|
+
rescue ActiveRecord::ActiveRecordError => e
|
211
|
+
puts "loading #{table} failed - check log for details"
|
212
|
+
raise e if raise_error?
|
213
|
+
end
|
214
|
+
|
215
|
+
def load_fixture(klass, table, file)
|
216
|
+
fixture = ActiveRecord::Fixture.new(YAML.load(File.read(file)), klass)
|
217
|
+
begin
|
218
|
+
connection.insert_fixture fixture, table
|
219
|
+
rescue ActiveRecord::ActiveRecordError => e
|
220
|
+
puts "loading fixture #{file} failed - check log for details"
|
221
|
+
raise e if raise_error?
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def dump_table_schema(table)
|
226
|
+
File.open(File.join(path, table, 'schema.rb'), "w") do |schema_file|
|
227
|
+
if table == 'schema_migrations'
|
228
|
+
schema_file.write schema_migrations_schema
|
229
|
+
else
|
230
|
+
schema_dumper.send :table, table, schema_file
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def schema_migrations_schema
|
236
|
+
<<-end_eval
|
237
|
+
create_table "schema_migrations", :force => true, :id => false do |t|
|
238
|
+
t.string "version", :null => false
|
239
|
+
end
|
240
|
+
add_index :schema_migrations, :version, :unique => true, :name => 'unique_schema_migrations'
|
241
|
+
end_eval
|
242
|
+
end
|
243
|
+
|
244
|
+
def schema_dumper
|
245
|
+
@schema_dumper ||= ActiveRecord::SchemaDumper.send :new, @connection
|
246
|
+
end
|
247
|
+
|
248
|
+
def load_table_schema(table)
|
249
|
+
schema_definition = File.read(File.join(path, table, 'schema.rb'))
|
250
|
+
ActiveRecord::Migration.suppress_messages do
|
251
|
+
ActiveRecord::Schema.define do
|
252
|
+
eval schema_definition
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def clobber_fixtures_for_table(table)
|
258
|
+
`rm -rf #{File.join(path, table)}`
|
259
|
+
`mkdir -p #{File.join(path, table)}`
|
260
|
+
end
|
261
|
+
|
262
|
+
def clobber_all_fixtures
|
263
|
+
fixtures_tables.each {|table| clobber_fixtures_for_table(table)}
|
264
|
+
end
|
265
|
+
|
266
|
+
def clobber_records(table)
|
267
|
+
connection.delete "DELETE FROM #{table}"
|
268
|
+
end
|
269
|
+
|
270
|
+
# Partitions the given id into an array of path components.
|
271
|
+
#
|
272
|
+
# For example, given an id of 1
|
273
|
+
# <tt>["0000", "0001"]</tt>
|
274
|
+
#
|
275
|
+
# Currently only integer ids are supported
|
276
|
+
def id_path(id)
|
277
|
+
("%08d" % id).scan(/..../)
|
278
|
+
end
|
279
|
+
end
|