Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update extract_fixtures task with configurable output directory #28

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
73 changes: 63 additions & 10 deletions lib/tasks/extract_fixtures.rake
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,73 @@
desc 'Create YAML test fixtures from data in an existing database.
Defaults to development database. Set RAILS_ENV to override.'

module Psych
module Visitors
class YAMLTree
# Override default time format
# https://github.com/ruby/ruby/blob/v3_3_6/ext/psych/lib/psych/visitors/yaml_tree.rb#L484-L490
def format_time time, utc = time.utc?
if utc
time.strftime("%Y-%m-%d %H:%M:%S")
else
time.strftime("%Y-%m-%d %H:%M:%S %:z")
end
end
end
end
end

task :extract_fixtures => :environment do
sql = "SELECT * FROM %s"
skip_tables = ["schema_info"]
dir = ENV['DIR'] || './tmp/fixtures'
time_offset = ENV['TIME_OFFSET'] || ''
tables = ENV['TABLES']&.split(',') || []
skip_tables = ENV['SKIP_TABLES']&.split(',') || []
table_filters = ENV['TABLE_FILTERS']&.split(';')&.map {|tf| tf.split(":", 2)}&.to_h || {}
omit_default_or_nil = ActiveRecord::Type::Boolean.new.cast(ENV.fetch('OMIT_DEFAULT_OR_NIL', 'false'))

FileUtils.mkdir_p(dir)
if time_offset.present? && !time_offset.match?(/^([+-](0[0-9]|1[0-4]):[0-5][0-9])$/)
abort("Invalid TIME_OFFSET format. Use +HH:MM or -HH:MM (e.g. +09:00)")
end
skip_tables += ["schema_migrations", "ar_internal_metadata"]

ActiveRecord::Base.establish_connection
(ActiveRecord::Base.connection.tables - skip_tables).each do |table_name|
tables = tables.present? ? tables : ActiveRecord::Base.connection.tables
(tables - skip_tables).each do |table_name|
columns = ActiveRecord::Base.connection.columns(table_name)
column_names = columns.map(&:name)
has_id_column = column_names.include?('id')
order_columns = has_id_column ? 'id' : column_names.join(', ')
where_clause = table_filters.has_key?(table_name) ? "WHERE #{table_filters[table_name]}" : ''
sql = "SELECT * FROM #{table_name} #{where_clause} ORDER BY #{order_columns}"
data = ActiveRecord::Base.connection.select_all(sql)
if data.empty?
next
end
i = "000"
File.open("#{Rails.root}/#{table_name}.yml", 'w' ) do |file|
data = ActiveRecord::Base.connection.select_all(sql % table_name)
File.open(File.join(dir, "#{table_name}.yml"), 'w') do |file|
file.write data.inject({}) { |hash, record|
# cast extracted values
ActiveRecord::Base.connection.columns(table_name).each { |col|
record[col.name] = col.type_cast(record[col.name]) if record[col.name]
}
hash["#{table_name}_#{i.succ!}"] = record
# omit default or nil values or cast extracted values with formatting time
columns.each do |col|
if omit_default_or_nil && (
(!col.default.nil? && !record[col.name].nil? && record[col.name].to_s == col.default) ||
(col.default.nil? && record[col.name].nil?)
)
record.delete(col.name)
next
elsif record[col.name]
record[col.name] = ActiveRecord::Type.lookup(col.type).deserialize(record[col.name])
if col.type == :datetime && record[col.name].is_a?(Time)
if time_offset.present?
record[col.name] = record[col.name].localtime(time_offset)
else
record[col.name] = record[col.name].getutc
end
end
end
end
key_id = has_id_column ? sprintf('%03d', record['id']) : i.succ!
hash["#{table_name}_#{key_id}"] = record
hash
}.to_yaml
end
Expand Down
Loading