dump 1.0.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/.autotest +13 -0
- data/.gitignore +12 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +250 -0
- data/dump.gemspec +22 -0
- data/lib/dump.rb +3 -0
- data/lib/dump/capistrano.rb +1 -0
- data/lib/dump/railtie.rb +8 -0
- data/lib/dump_rake.rb +85 -0
- data/lib/dump_rake/archive_tar_minitar_fix.rb +8 -0
- data/lib/dump_rake/assets.rb +22 -0
- data/lib/dump_rake/continious_timeout.rb +38 -0
- data/lib/dump_rake/dump.rb +175 -0
- data/lib/dump_rake/dump_reader.rb +289 -0
- data/lib/dump_rake/dump_writer.rb +119 -0
- data/lib/dump_rake/env.rb +139 -0
- data/lib/dump_rake/env/filter.rb +26 -0
- data/lib/dump_rake/rails_root.rb +12 -0
- data/lib/dump_rake/table_manipulation.rb +131 -0
- data/lib/generators/assets_config/assets_config_generator.rb +16 -0
- data/lib/generators/assets_config/templates/assets +8 -0
- data/lib/tasks/assets.rake +17 -0
- data/lib/tasks/dump.rake +27 -0
- data/recipes/dump.rb +343 -0
- data/script/update_readme +21 -0
- data/spec/.gitignore +1 -0
- data/spec/.tmignore +1 -0
- data/spec/cycle_spec.rb +229 -0
- data/spec/db/database.example.yml +19 -0
- data/spec/db/schema.rb +7 -0
- data/spec/dummy-3.1.3/.gitignore +15 -0
- data/spec/dummy-3.1.3/.rspec +1 -0
- data/spec/dummy-3.1.3/Gemfile +23 -0
- data/spec/dummy-3.1.3/Gemfile.lock +159 -0
- data/spec/dummy-3.1.3/README +261 -0
- data/spec/dummy-3.1.3/Rakefile +7 -0
- data/spec/dummy-3.1.3/app/assets/images/rails.png +0 -0
- data/spec/dummy-3.1.3/app/assets/javascripts/application.js +9 -0
- data/spec/dummy-3.1.3/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy-3.1.3/app/controllers/application_controller.rb +3 -0
- data/spec/dummy-3.1.3/app/helpers/application_helper.rb +2 -0
- data/spec/dummy-3.1.3/app/mailers/.gitkeep +0 -0
- data/spec/dummy-3.1.3/app/models/.gitkeep +0 -0
- data/spec/dummy-3.1.3/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy-3.1.3/config.ru +4 -0
- data/spec/dummy-3.1.3/config/application.rb +54 -0
- data/spec/dummy-3.1.3/config/boot.rb +6 -0
- data/spec/dummy-3.1.3/config/database.yml +25 -0
- data/spec/dummy-3.1.3/config/environment.rb +5 -0
- data/spec/dummy-3.1.3/config/environments/development.rb +30 -0
- data/spec/dummy-3.1.3/config/environments/production.rb +60 -0
- data/spec/dummy-3.1.3/config/environments/test.rb +39 -0
- data/spec/dummy-3.1.3/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy-3.1.3/config/initializers/inflections.rb +10 -0
- data/spec/dummy-3.1.3/config/initializers/mime_types.rb +5 -0
- data/spec/dummy-3.1.3/config/initializers/secret_token.rb +7 -0
- data/spec/dummy-3.1.3/config/initializers/session_store.rb +8 -0
- data/spec/dummy-3.1.3/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy-3.1.3/config/locales/en.yml +5 -0
- data/spec/dummy-3.1.3/config/routes.rb +58 -0
- data/spec/dummy-3.1.3/db/seeds.rb +7 -0
- data/spec/dummy-3.1.3/doc/README_FOR_APP +2 -0
- data/spec/dummy-3.1.3/lib/assets/.gitkeep +0 -0
- data/spec/dummy-3.1.3/lib/tasks/.gitkeep +0 -0
- data/spec/dummy-3.1.3/log/.gitkeep +0 -0
- data/spec/dummy-3.1.3/public/404.html +26 -0
- data/spec/dummy-3.1.3/public/422.html +26 -0
- data/spec/dummy-3.1.3/public/500.html +26 -0
- data/spec/dummy-3.1.3/public/favicon.ico +0 -0
- data/spec/dummy-3.1.3/public/index.html +241 -0
- data/spec/dummy-3.1.3/public/robots.txt +5 -0
- data/spec/dummy-3.1.3/script/rails +6 -0
- data/spec/dummy-3.1.3/spec/spec_helper.rb +32 -0
- data/spec/dummy-3.1.3/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/dummy-3.1.3/vendor/plugins/.gitkeep +0 -0
- data/spec/lib/dump_rake/dump_reader_spec.rb +638 -0
- data/spec/lib/dump_rake/dump_spec.rb +291 -0
- data/spec/lib/dump_rake/dump_writer_spec.rb +328 -0
- data/spec/lib/dump_rake/env/filter_spec.rb +56 -0
- data/spec/lib/dump_rake/env_spec.rb +139 -0
- data/spec/lib/dump_rake/rails_root_spec.rb +45 -0
- data/spec/lib/dump_rake/table_manipulation_spec.rb +256 -0
- data/spec/lib/dump_rake_spec.rb +326 -0
- data/spec/recipes/dump_spec.rb +553 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/tasks/assets_spec.rb +92 -0
- data/spec/tasks/dump_spec.rb +107 -0
- metadata +272 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
class DumpRake
|
|
2
|
+
class DumpWriter < Dump
|
|
3
|
+
attr_reader :stream, :config
|
|
4
|
+
|
|
5
|
+
def self.create(path)
|
|
6
|
+
new(path).open do |dump|
|
|
7
|
+
ActiveRecord::Base.logger.silence do
|
|
8
|
+
dump.write_schema
|
|
9
|
+
|
|
10
|
+
dump.write_tables
|
|
11
|
+
dump.write_assets
|
|
12
|
+
|
|
13
|
+
dump.write_config
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def open
|
|
19
|
+
Pathname.new(path).dirname.mkpath
|
|
20
|
+
Zlib::GzipWriter.open(path) do |gzip|
|
|
21
|
+
gzip.mtime = Time.utc(2000)
|
|
22
|
+
lock do
|
|
23
|
+
Archive::Tar::Minitar.open(gzip, 'w') do |stream|
|
|
24
|
+
@stream = stream
|
|
25
|
+
@config = {:tables => {}}
|
|
26
|
+
yield(self)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def create_file(name)
|
|
33
|
+
Tempfile.open('dump') do |temp|
|
|
34
|
+
yield(temp)
|
|
35
|
+
temp.open
|
|
36
|
+
stream.tar.add_file_simple(name, :mode => 0100444, :size => temp.length) do |f|
|
|
37
|
+
f.write(temp.read(4096)) until temp.eof?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def write_schema
|
|
43
|
+
create_file('schema.rb') do |f|
|
|
44
|
+
DumpRake::Env.with_env('SCHEMA' => f.path) do
|
|
45
|
+
Rake::Task['db:schema:dump'].invoke
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def write_tables
|
|
51
|
+
verify_connection
|
|
52
|
+
tables_to_dump.with_progress('Tables') do |table|
|
|
53
|
+
write_table(table)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def write_table(table)
|
|
58
|
+
row_count = table_row_count(table)
|
|
59
|
+
config[:tables][table] = row_count
|
|
60
|
+
Progress.start(table, 1 + row_count) do
|
|
61
|
+
create_file("#{table}.dump") do |f|
|
|
62
|
+
columns = table_columns(table)
|
|
63
|
+
column_names = columns.map(&:name).sort
|
|
64
|
+
columns_by_name = columns.index_by(&:name)
|
|
65
|
+
|
|
66
|
+
Marshal.dump(column_names, f)
|
|
67
|
+
Progress.step
|
|
68
|
+
|
|
69
|
+
each_table_row(table, row_count) do |row|
|
|
70
|
+
values = column_names.map do |column|
|
|
71
|
+
columns_by_name[column].type_cast(row[column])
|
|
72
|
+
end
|
|
73
|
+
Marshal.dump(values, f)
|
|
74
|
+
Progress.step
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def write_assets
|
|
81
|
+
assets = assets_to_dump
|
|
82
|
+
if assets.present?
|
|
83
|
+
config[:assets] = {}
|
|
84
|
+
Dir.chdir(DumpRake::RailsRoot) do
|
|
85
|
+
assets = Dir[*assets].uniq
|
|
86
|
+
assets.with_progress('Assets') do |asset|
|
|
87
|
+
paths = Dir[File.join(asset, '**/*')]
|
|
88
|
+
files = paths.select{ |path| File.file?(path) }
|
|
89
|
+
config[:assets][asset] = {:total => paths.length, :files => files.length}
|
|
90
|
+
assets_root_link do |tmpdir, prefix|
|
|
91
|
+
paths.with_progress(asset) do |entry|
|
|
92
|
+
begin
|
|
93
|
+
Archive::Tar::Minitar.pack_file(File.join(prefix, entry), stream)
|
|
94
|
+
rescue => e
|
|
95
|
+
$stderr.puts "Skipped asset due to error #{e}"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def write_config
|
|
105
|
+
create_file('config') do |f|
|
|
106
|
+
Marshal.dump(config, f)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def assets_to_dump
|
|
111
|
+
begin
|
|
112
|
+
Rake::Task['assets'].invoke
|
|
113
|
+
DumpRake::Env[:assets].split(DumpRake::Assets::SPLITTER)
|
|
114
|
+
rescue
|
|
115
|
+
[]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'dump_rake/env/filter'
|
|
4
|
+
|
|
5
|
+
class DumpRake
|
|
6
|
+
module Env
|
|
7
|
+
DICTIONARY = {
|
|
8
|
+
:desc => %w[DESC DESCRIPTION],
|
|
9
|
+
:like => %w[LIKE VER VERSION],
|
|
10
|
+
:tags => %w[TAGS TAG],
|
|
11
|
+
:leave => %w[LEAVE],
|
|
12
|
+
:summary => %w[SUMMARY],
|
|
13
|
+
:assets => %w[ASSETS],
|
|
14
|
+
:tables => %w[TABLES],
|
|
15
|
+
:backup => %w[BACKUP AUTOBACKUP AUTO_BACKUP],
|
|
16
|
+
:transfer_via => %w[TRANSFER_VIA],
|
|
17
|
+
:migrate_down => %w[MIGRATE_DOWN],
|
|
18
|
+
:restore_schema => %w[RESTORE_SCHEMA],
|
|
19
|
+
:restore_tables => %w[RESTORE_TABLES],
|
|
20
|
+
:restore_assets => %w[RESTORE_ASSETS],
|
|
21
|
+
:show_size => %w[SHOW_SIZE], # internal
|
|
22
|
+
}.freeze unless defined? DICTIONARY
|
|
23
|
+
|
|
24
|
+
EXPLANATIONS = {
|
|
25
|
+
:desc => 'free form description of dump',
|
|
26
|
+
:like => 'filter dumps by full dump name',
|
|
27
|
+
:tags => 'comma separated list of tags',
|
|
28
|
+
:leave => 'number of dumps to leave',
|
|
29
|
+
:summary => 'output info about dump: "1", "true" or "yes" for basic info, "2" or "schema" to display schema as well',
|
|
30
|
+
:assets => 'comma or colon separated list of paths or globs to dump',
|
|
31
|
+
:tables => 'comma separated list of tables to dump or if prefixed by "-" — to skip; by default only sessions table is skipped; schema_info and schema_migrations are always included if they are present',
|
|
32
|
+
:backup => 'no autobackup if you pass "0", "no" or "false"',
|
|
33
|
+
:transfer_via => 'transfer method (rsync, sftp or scp)',
|
|
34
|
+
:migrate_down => 'don\'t run down for migrations not present in dump if you pass "0", "no" or "false"; pass "reset" to recreate (drop and create) db',
|
|
35
|
+
:restore_schema => 'don\'t read/change schema if you pass "0", "no" or "false" (useful to just restore data for table; note that schema info tables are also not restored)',
|
|
36
|
+
:restore_tables => 'works as TABLES, but for restoring',
|
|
37
|
+
:restore_assets => 'works as ASSETS, but for restoring',
|
|
38
|
+
}.freeze unless defined? EXPLANATIONS
|
|
39
|
+
|
|
40
|
+
class << self
|
|
41
|
+
def with_env(hash)
|
|
42
|
+
old = {}
|
|
43
|
+
hash.each do |key, value|
|
|
44
|
+
key = DICTIONARY[key].first if DICTIONARY[key]
|
|
45
|
+
old[key] = ENV[key]
|
|
46
|
+
ENV[key] = value
|
|
47
|
+
end
|
|
48
|
+
begin
|
|
49
|
+
yield
|
|
50
|
+
ensure
|
|
51
|
+
old.each do |key, value|
|
|
52
|
+
ENV[key] = value
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def with_clean_env(hash = {}, &block)
|
|
58
|
+
empty_env = {}
|
|
59
|
+
DICTIONARY.keys.each{ |key| empty_env[key] = nil }
|
|
60
|
+
with_env(empty_env.merge(hash), &block)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def [](key)
|
|
64
|
+
if DICTIONARY[key]
|
|
65
|
+
ENV.values_at(*DICTIONARY[key]).compact.first
|
|
66
|
+
else
|
|
67
|
+
ENV[key]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def filter(key, splitter = nil)
|
|
72
|
+
@filters ||= Hash.new{ |hash, key| hash[key] = Filter.new(*key) }
|
|
73
|
+
@filters[[self[key], splitter]]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def yes?(key)
|
|
77
|
+
%w[1 y t].include?(first_char(key))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def no?(key)
|
|
81
|
+
%w[0 n f].include?(first_char(key))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def downcase(key)
|
|
85
|
+
self[key].to_s.downcase.strip
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def variable_names_for_command(command)
|
|
89
|
+
m = {
|
|
90
|
+
:select => [:like, :tags],
|
|
91
|
+
:assets => [:assets],
|
|
92
|
+
:restore_options => [:migrate_down, :restore_schema, :restore_tables, :restore_assets],
|
|
93
|
+
:transfer_options => [:transfer_via]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
m[:versions] = m[:select] | [:summary]
|
|
97
|
+
m[:create] = [:desc, :tags, :tables] | m[:assets]
|
|
98
|
+
m[:restore] = m[:select] | m[:restore_options]
|
|
99
|
+
m[:cleanup] = m[:select] | [:leave]
|
|
100
|
+
|
|
101
|
+
m[:transfer] = m[:select] | m[:transfer_options]
|
|
102
|
+
|
|
103
|
+
m[:mirror] = [:backup] | m[:create] | m[:transfer_options] | m[:restore_options]
|
|
104
|
+
m[:backup] = m[:create] | [:transfer_via]
|
|
105
|
+
m[:backup_restore] = m[:transfer] | m[:restore_options]
|
|
106
|
+
|
|
107
|
+
m[command] || []
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def for_command(command, strings = false)
|
|
111
|
+
variables = variable_names_for_command(command)
|
|
112
|
+
variables.inject({}) do |env, variable|
|
|
113
|
+
value = self[variable]
|
|
114
|
+
env[strings ? DICTIONARY[variable].first : variable] = value if value
|
|
115
|
+
env
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def stringify!(hash)
|
|
120
|
+
hash.keys.each do |key|
|
|
121
|
+
hash[DICTIONARY[key] ? DICTIONARY[key].first : key.to_s] = hash.delete(key)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def explain_variables_for_command(command)
|
|
126
|
+
".\n" <<
|
|
127
|
+
variable_names_for_command(command).map do |variable_name|
|
|
128
|
+
" #{DICTIONARY[variable_name].join(', ')} — #{EXPLANATIONS[variable_name]}\n"
|
|
129
|
+
end.join('')
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
def first_char(key)
|
|
135
|
+
downcase(key)[0, 1]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
class DumpRake
|
|
4
|
+
module Env
|
|
5
|
+
class Filter
|
|
6
|
+
attr_reader :invert, :values, :transparent
|
|
7
|
+
def initialize(s, splitter = nil)
|
|
8
|
+
if s
|
|
9
|
+
s = s.dup
|
|
10
|
+
@invert = !!s.sub!(/^-/, '')
|
|
11
|
+
@values = s.split(splitter || ',').map(&:strip).map(&:downcase).uniq.select(&:present?)
|
|
12
|
+
else
|
|
13
|
+
@transparent = true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def pass?(value)
|
|
18
|
+
transparent || (invert ^ values.include?(value.to_s.downcase))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def custom_pass?(&block)
|
|
22
|
+
transparent || (invert ^ values.any?(&block))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
class DumpRake
|
|
2
|
+
module TableManipulation
|
|
3
|
+
protected
|
|
4
|
+
|
|
5
|
+
def schema_tables
|
|
6
|
+
%w[schema_info schema_migrations]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def verify_connection
|
|
10
|
+
ActiveRecord::Base.connection.verify!(0)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def quote_table_name(table)
|
|
15
|
+
ActiveRecord::Base.connection.quote_table_name(table)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def quote_column_name(column)
|
|
19
|
+
ActiveRecord::Base.connection.quote_column_name(column)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def quote_value(value)
|
|
23
|
+
ActiveRecord::Base.connection.quote(value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def clear_table(table_sql)
|
|
28
|
+
ActiveRecord::Base.connection.delete("DELETE FROM #{table_sql}", 'Clearing table')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def insert_into_table(table_sql, columns_sql, values_sql)
|
|
32
|
+
values_sql = values_sql.join(',') if values_sql.is_a?(Array)
|
|
33
|
+
ActiveRecord::Base.connection.insert("INSERT INTO #{table_sql} #{columns_sql} VALUES #{values_sql}", 'Loading dump')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def fix_sequence!(table)
|
|
37
|
+
if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
|
|
38
|
+
ActiveRecord::Base.connection.reset_pk_sequence!(table)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def join_for_sql(quoted)
|
|
43
|
+
"(#{quoted.join(',')})"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def columns_insert_sql(columns)
|
|
47
|
+
join_for_sql(columns.map{ |column| quote_column_name(column) })
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def values_insert_sql(values)
|
|
51
|
+
join_for_sql(values.map{ |value| quote_value(value) })
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def avaliable_tables
|
|
56
|
+
ActiveRecord::Base.connection.tables
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def tables_to_dump
|
|
60
|
+
if DumpRake::Env[:tables]
|
|
61
|
+
avaliable_tables.select do |table|
|
|
62
|
+
schema_tables.include?(table) || DumpRake::Env.filter(:tables).pass?(table)
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
avaliable_tables - %w[sessions]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def table_row_count(table)
|
|
70
|
+
ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM #{quote_table_name(table)}").to_i
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
CHUNK_SIZE_MIN = 100 unless const_defined?(:CHUNK_SIZE_MIN)
|
|
74
|
+
CHUNK_SIZE_MAX = 3_000 unless const_defined?(:CHUNK_SIZE_MAX)
|
|
75
|
+
def table_chunk_size(table)
|
|
76
|
+
expected_row_size = table_columns(table).map do |column|
|
|
77
|
+
case column.type
|
|
78
|
+
when :text
|
|
79
|
+
Math.sqrt(column.limit || 2_147_483_647)
|
|
80
|
+
when :string
|
|
81
|
+
Math.sqrt(column.limit || 255)
|
|
82
|
+
else
|
|
83
|
+
column.limit || 10
|
|
84
|
+
end
|
|
85
|
+
end.sum
|
|
86
|
+
[[(10_000_000 / expected_row_size).round, CHUNK_SIZE_MIN].max, CHUNK_SIZE_MAX].min
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def table_columns(table)
|
|
90
|
+
ActiveRecord::Base.connection.columns(table)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def table_has_primary_column?(table)
|
|
94
|
+
# bad test for primary column, but primary even for primary column is nil
|
|
95
|
+
table_columns(table).any?{ |column| column.name == table_primary_key(table) && column.type == :integer }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def table_primary_key(table)
|
|
99
|
+
'id'
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def each_table_row(table, row_count, &block)
|
|
103
|
+
if table_has_primary_column?(table) && row_count > (chunk_size = table_chunk_size(table))
|
|
104
|
+
# adapted from ActiveRecord::Batches
|
|
105
|
+
primary_key = table_primary_key(table)
|
|
106
|
+
quoted_primary_key = "#{quote_table_name(table)}.#{quote_column_name(primary_key)}"
|
|
107
|
+
select_where_primary_key =
|
|
108
|
+
"SELECT * FROM #{quote_table_name(table)}" +
|
|
109
|
+
" WHERE #{quoted_primary_key} %s" +
|
|
110
|
+
" ORDER BY #{quoted_primary_key} ASC" +
|
|
111
|
+
" LIMIT #{chunk_size}"
|
|
112
|
+
rows = select_all_by_sql(select_where_primary_key % '>= 0')
|
|
113
|
+
until rows.blank?
|
|
114
|
+
rows.each(&block)
|
|
115
|
+
break if rows.length < chunk_size
|
|
116
|
+
rows = select_all_by_sql(select_where_primary_key % "> #{rows.last[primary_key].to_i}")
|
|
117
|
+
end
|
|
118
|
+
else
|
|
119
|
+
table_rows(table).each(&block)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def table_rows(table)
|
|
124
|
+
select_all_by_sql("SELECT * FROM #{quote_table_name(table)}")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def select_all_by_sql(sql)
|
|
128
|
+
ActiveRecord::Base.connection.select_all(sql)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
case
|
|
2
|
+
when defined?(Rails::Generator::Base)
|
|
3
|
+
class AssetsConfigGenerator < Rails::Generator::Base
|
|
4
|
+
def manifest
|
|
5
|
+
record do |m|
|
|
6
|
+
m.file 'assets', 'config/assets'
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
when defined?(Rails::Generators::Base)
|
|
11
|
+
class AssetsConfigGenerator < Rails::Generators::Base
|
|
12
|
+
def create_assets_config
|
|
13
|
+
create_file 'config/assets', File.read(File.join(File.dirname(__FILE__), 'templates/assets'))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|