git_friendly_dumper 0.0.1
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/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
|