pax_global_header00006660000000000000000000000064133077373010014516gustar00rootroot0000000000000052 comment=f2a9bbb64a7c7e232a92e37e6e3cc113d9531f29 yaml_db-0.7.0/000077500000000000000000000000001330773730100131315ustar00rootroot00000000000000yaml_db-0.7.0/.gitignore000066400000000000000000000000431330773730100151160ustar00rootroot00000000000000*.gem /.bundle/ /pkg/ Gemfile.lock yaml_db-0.7.0/.rspec000066400000000000000000000000361330773730100142450ustar00rootroot00000000000000--color --require spec_helper yaml_db-0.7.0/.travis.yml000066400000000000000000000026201330773730100152420ustar00rootroot00000000000000sudo: false dist: trusty language: ruby cache: bundler rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 - 2.1 - 2.2 - 2.3 env: - RAILS_VERSION='~> 3.0.0' - RAILS_VERSION='~> 3.1.0' - RAILS_VERSION='~> 3.2.0' - RAILS_VERSION='~> 4.0.0' - RAILS_VERSION='~> 4.1.0' - RAILS_VERSION='~> 4.2.0' matrix: fast_finish: true exclude: - env: RAILS_VERSION='~> 4.0.0' rvm: 1.8.7 - env: RAILS_VERSION='~> 4.0.0' rvm: 1.9.2 - env: RAILS_VERSION='~> 4.1.0' rvm: 1.8.7 - env: RAILS_VERSION='~> 4.1.0' rvm: 1.9.2 - env: RAILS_VERSION='~> 4.2.0' rvm: 1.8.7 - env: RAILS_VERSION='~> 4.2.0' rvm: 1.9.2 include: - env: RAILS_VERSION='~> 4.2.0' rvm: 2.4 - env: RAILS_VERSION='~> 4.2.0' rvm: 2.5 - env: RAILS_VERSION='~> 5.0.0' rvm: 2.2 - env: RAILS_VERSION='~> 5.0.0' rvm: 2.3 - env: RAILS_VERSION='~> 5.0.0' rvm: 2.4 - env: RAILS_VERSION='~> 5.0.0' rvm: 2.5 - env: RAILS_VERSION='~> 5.1.0' rvm: 2.2 - env: RAILS_VERSION='~> 5.1.0' rvm: 2.3 - env: RAILS_VERSION='~> 5.1.0' rvm: 2.4 - env: RAILS_VERSION='~> 5.1.0' rvm: 2.5 - env: RAILS_VERSION='~> 5.2.0' rvm: 2.2 - env: RAILS_VERSION='~> 5.2.0' rvm: 2.3 - env: RAILS_VERSION='~> 5.2.0' rvm: 2.4 - env: RAILS_VERSION='~> 5.2.0' rvm: 2.5 before_install: - gem update bundler yaml_db-0.7.0/CHANGELOG.md000066400000000000000000000011451330773730100147430ustar00rootroot00000000000000## yaml_db 0.6.0 * Add Rails 5.1 support * Fix a bug that caused errors when dumping HABTM tables ## yaml_db 0.5.0 * Order HABTM tables using both IDs (@mauriciopasquier) ## yaml_db 0.4.2 * Use Windows-compatible directory names * Sort tables in the dump so that two dumps can reliably be diffed ## yaml_db 0.4.1 Version skipped due to developer error ## yaml_db 0.4.0 * Put everything under the YamlDb module namespace * Add Rails 5.0 support ## yaml_db 0.3.0 * Add Rails 4.x support * Restore Rails 3.0 compatibility * Test against all supported Rails versions ## yaml_db 0.2.3 * Support Rails 3.2 yaml_db-0.7.0/Gemfile000066400000000000000000000001121330773730100144160ustar00rootroot00000000000000source 'https://rubygems.org' gemspec gem 'rails', ENV['RAILS_VERSION'] yaml_db-0.7.0/README.md000066400000000000000000000045131330773730100144130ustar00rootroot00000000000000# YamlDb YamlDb is a database-independent format for dumping and restoring data. It complements the database-independent schema format found in db/schema.rb. The data is saved into db/data.yml. This can be used as a replacement for mysqldump or pg_dump, but only for the databases typically used by Rails apps. Users, permissions, schemas, triggers, and other advanced database features are not supported - by design. Any database that has an ActiveRecord adapter should work. This gem supports Rails versions 3.0 through 5.2. [![Build Status](https://travis-ci.org/yamldb/yaml_db.svg?branch=master)](https://travis-ci.org/yamldb/yaml_db) ## Installation Simply add to your Gemfile: gem 'yaml_db' All rake tasks will then be available to you. ## Usage rake db:data:dump -> Dump contents of Rails database to db/data.yml rake db:data:load -> Load contents of db/data.yml into the database Further, there are tasks db:dump and db:load which do the entire database (the equivalent of running db:schema:dump followed by db:data:load). Also, there are other tasks recently added that allow the export of the database contents to/from multiple files (each one named after the table being dumped or loaded). rake db:data:dump_dir -> Dump contents of database to curr_dir_name/tablename.extension (defaults to yaml) rake db:data:load_dir -> Load contents of db/#{dir} into database (where dir is ENV['dir'] || 'base') In addition, we have plugins whereby you can export your database to/from various formats. We only deal with yaml and csv right now, but you can easily write tools for your own formats (such as Excel or XML). To use another format, just load setting the "class" parameter to the class you are using. This defaults to "YamlDb::Helper" which is a refactoring of the old yaml_db code. We'll shorten this to use class nicknames in a little bit. ## Examples One common use would be to switch your data from one database backend to another. For example, let's say you wanted to switch from SQLite to MySQL. You might execute the following steps: 1. rake db:dump 2. Edit config/database.yml and change your adapter to mysql, set up database params 3. mysqladmin create [database name] 4. rake db:load ## Credits Created by Orion Henry and Adam Wiggins. Major updates by Ricardo Chimal Jr. and Nate Kidwell. yaml_db-0.7.0/Rakefile000066400000000000000000000002051330773730100145730ustar00rootroot00000000000000require 'rake' require "rspec/core/rake_task" require "bundler/gem_tasks" RSpec::Core::RakeTask.new(:spec) task :default => :spec yaml_db-0.7.0/lib/000077500000000000000000000000001330773730100136775ustar00rootroot00000000000000yaml_db-0.7.0/lib/tasks/000077500000000000000000000000001330773730100150245ustar00rootroot00000000000000yaml_db-0.7.0/lib/tasks/yaml_db_tasks.rake000066400000000000000000000016431330773730100205100ustar00rootroot00000000000000namespace :db do desc "Dump schema and data to db/schema.rb and db/data.yml" task(:dump => [ "db:schema:dump", "db:data:dump" ]) desc "Load schema and data from db/schema.rb and db/data.yml" task(:load => [ "db:schema:load", "db:data:load" ]) namespace :data do desc "Dump contents of database to db/data.extension (defaults to yaml)" task :dump => :environment do YamlDb::RakeTasks.data_dump_task end desc "Dump contents of database to curr_dir_name/tablename.extension (defaults to yaml)" task :dump_dir => :environment do YamlDb::RakeTasks.data_dump_dir_task end desc "Load contents of db/data.extension (defaults to yaml) into database" task :load => :environment do YamlDb::RakeTasks.data_load_task end desc "Load contents of db/data_dir into database" task :load_dir => :environment do YamlDb::RakeTasks.data_load_dir_task end end end yaml_db-0.7.0/lib/yaml_db.rb000066400000000000000000000030371330773730100156360ustar00rootroot00000000000000require 'rubygems' require 'yaml' require 'active_record' require 'rails/railtie' require 'yaml_db/rake_tasks' require 'yaml_db/version' require 'yaml_db/serialization_helper' module YamlDb module Helper def self.loader Load end def self.dumper Dump end def self.extension "yml" end end module Utils def self.chunk_records(records) yaml = [ records ].to_yaml yaml.sub!(/---\s\n|---\n/, '') yaml.sub!('- - -', ' - -') yaml end end class Dump < SerializationHelper::Dump def self.dump_table_columns(io, table) io.write("\n") io.write({ table => { 'columns' => table_column_names(table) } }.to_yaml) end def self.dump_table_records(io, table) table_record_header(io) column_names = table_column_names(table) each_table_page(table) do |records| rows = SerializationHelper::Utils.unhash_records(records.to_a, column_names) io.write(Utils.chunk_records(rows)) end end def self.table_record_header(io) io.write(" records: \n") end end class Load < SerializationHelper::Load def self.load_documents(io, truncate = true) YAML.load_stream(io) do |ydoc| ydoc.keys.each do |table_name| next if ydoc[table_name].nil? load_table(table_name, ydoc[table_name], truncate) end end end end class Railtie < Rails::Railtie rake_tasks do load File.expand_path('../tasks/yaml_db_tasks.rake', __FILE__) end end end yaml_db-0.7.0/lib/yaml_db/000077500000000000000000000000001330773730100153065ustar00rootroot00000000000000yaml_db-0.7.0/lib/yaml_db/csv_db.rb000066400000000000000000000034441330773730100171000ustar00rootroot00000000000000module YamlDb module CsvDb module Helper def self.loader Load end def self.dumper Dump end def self.extension "csv" end end class Load < SerializationHelper::Load def self.load_documents(io, truncate = true) tables = {} curr_table = nil io.each do |line| if /BEGIN_CSV_TABLE_DECLARATION(.+)END_CSV_TABLE_DECLARATION/ =~ line curr_table = $1 tables[curr_table] = {} else if tables[curr_table]["columns"] tables[curr_table]["records"] << FasterCSV.parse(line)[0] else tables[curr_table]["columns"] = FasterCSV.parse(line)[0] tables[curr_table]["records"] = [] end end end tables.each_pair do |table_name, contents| load_table(table_name, contents, truncate) end end end class Dump < SerializationHelper::Dump def self.before_table(io,table) io.write "BEGIN_CSV_TABLE_DECLARATION#{table}END_CSV_TABLE_DECLARATION\n" end def self.dump(io) tables.each do |table| before_table(io, table) dump_table(io, table) after_table(io, table) end end def self.after_table(io,table) io.write "" end def self.dump_table_columns(io, table) io.write(table_column_names(table).to_csv) end def self.dump_table_records(io, table) column_names = table_column_names(table) each_table_page(table) do |records| rows = SerializationHelper::Utils.unhash_records(records, column_names) records.each do |record| io.write(record.to_csv) end end end end end end yaml_db-0.7.0/lib/yaml_db/rake_tasks.rb000066400000000000000000000017161330773730100177670ustar00rootroot00000000000000module YamlDb module RakeTasks def self.data_dump_task SerializationHelper::Base.new(helper).dump(db_dump_data_file(helper.extension)) end def self.data_dump_dir_task dir = ENV['dir'] || default_dir_name SerializationHelper::Base.new(helper).dump_to_dir(dump_dir("/#{dir}")) end def self.data_load_task SerializationHelper::Base.new(helper).load(db_dump_data_file(helper.extension)) end def self.data_load_dir_task dir = ENV['dir'] || 'base' SerializationHelper::Base.new(helper).load_from_dir(dump_dir("/#{dir}")) end private def self.default_dir_name Time.now.strftime('%FT%H%M%S') end def self.db_dump_data_file(extension = 'yml') "#{dump_dir}/data.#{extension}" end def self.dump_dir(dir = '') "#{Rails.root}/db#{dir}" end def self.helper format_class = ENV['class'] || 'YamlDb::Helper' format_class.constantize end end end yaml_db-0.7.0/lib/yaml_db/serialization_helper.rb000066400000000000000000000140711330773730100220520ustar00rootroot00000000000000require 'active_support/core_ext/kernel/reporting' module YamlDb module SerializationHelper class Base attr_reader :extension def initialize(helper) @dumper = helper.dumper @loader = helper.loader @extension = helper.extension end def dump(filename) disable_logger File.open(filename, "w") do |file| @dumper.dump(file) end reenable_logger end def dump_to_dir(dirname) Dir.mkdir(dirname) tables = @dumper.tables tables.each do |table| File.open("#{dirname}/#{table}.#{@extension}", "w") do |io| @dumper.before_table(io, table) @dumper.dump_table io, table @dumper.after_table(io, table) end end end def load(filename, truncate = true) disable_logger @loader.load(File.new(filename, "r"), truncate) reenable_logger end def load_from_dir(dirname, truncate = true) Dir.entries(dirname).each do |filename| if filename =~ /^[.]/ next end @loader.load(File.new("#{dirname}/#{filename}", "r"), truncate) end end def disable_logger @@old_logger = ActiveRecord::Base.logger ActiveRecord::Base.logger = nil end def reenable_logger ActiveRecord::Base.logger = @@old_logger end end class Load def self.load(io, truncate = true) ActiveRecord::Base.connection.transaction do load_documents(io, truncate) end end def self.truncate_table(table) begin ActiveRecord::Base.connection.execute("TRUNCATE #{Utils.quote_table(table)}") rescue Exception ActiveRecord::Base.connection.execute("DELETE FROM #{Utils.quote_table(table)}") end end def self.load_table(table, data, truncate = true) column_names = data['columns'] if truncate truncate_table(table) end load_records(table, column_names, data['records']) reset_pk_sequence!(table) end def self.load_records(table, column_names, records) if column_names.nil? return end quoted_column_names = column_names.map { |column| ActiveRecord::Base.connection.quote_column_name(column) }.join(',') quoted_table_name = Utils.quote_table(table) records.each do |record| quoted_values = record.map{|c| ActiveRecord::Base.connection.quote(c)}.join(',') ActiveRecord::Base.connection.execute("INSERT INTO #{quoted_table_name} (#{quoted_column_names}) VALUES (#{quoted_values})") end end def self.reset_pk_sequence!(table_name) if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!) ActiveRecord::Base.connection.reset_pk_sequence!(table_name) end end end module Utils def self.unhash(hash, keys) keys.map { |key| hash[key] } end def self.unhash_records(records, keys) records.each_with_index do |record, index| records[index] = unhash(record, keys) end records end def self.convert_booleans(records, columns) records.each do |record| columns.each do |column| next if is_boolean(record[column]) record[column] = convert_boolean(record[column]) end end records end def self.convert_boolean(value) ['t', '1', true, 1].include?(value) end def self.boolean_columns(table) columns = ActiveRecord::Base.connection.columns(table).reject { |c| silence_warnings { c.type != :boolean } } columns.map { |c| c.name } end def self.is_boolean(value) value.kind_of?(TrueClass) or value.kind_of?(FalseClass) end def self.quote_table(table) ActiveRecord::Base.connection.quote_table_name(table) end def self.quote_column(column) ActiveRecord::Base.connection.quote_column_name(column) end end class Dump def self.before_table(io, table) end def self.dump(io) tables.each do |table| before_table(io, table) dump_table(io, table) after_table(io, table) end end def self.after_table(io, table) end def self.tables ActiveRecord::Base.connection.tables.reject { |table| ['schema_info', 'schema_migrations'].include?(table) }.sort end def self.dump_table(io, table) return if table_record_count(table).zero? dump_table_columns(io, table) dump_table_records(io, table) end def self.table_column_names(table) ActiveRecord::Base.connection.columns(table).map { |c| c.name } end def self.each_table_page(table, records_per_page=1000) total_count = table_record_count(table) pages = (total_count.to_f / records_per_page).ceil - 1 keys = sort_keys(table) boolean_columns = Utils.boolean_columns(table) (0..pages).to_a.each do |page| query = Arel::Table.new(table).order(*keys).skip(records_per_page*page).take(records_per_page).project(Arel.sql('*')) records = ActiveRecord::Base.connection.select_all(query.to_sql) records = Utils.convert_booleans(records, boolean_columns) yield records end end def self.table_record_count(table) ActiveRecord::Base.connection.select_one("SELECT COUNT(*) FROM #{Utils.quote_table(table)}").values.first.to_i end # Return the first column as sort key unless the table looks like a # standard has_and_belongs_to_many join table, in which case add the second "ID column" def self.sort_keys(table) first_column, second_column = table_column_names(table) if [first_column, second_column].all? { |name| name =~ /_id$/ } [Utils.quote_column(first_column), Utils.quote_column(second_column)] else [Utils.quote_column(first_column)] end end end end end yaml_db-0.7.0/lib/yaml_db/version.rb000066400000000000000000000000461330773730100173200ustar00rootroot00000000000000module YamlDb VERSION = "0.7.0" end yaml_db-0.7.0/spec/000077500000000000000000000000001330773730100140635ustar00rootroot00000000000000yaml_db-0.7.0/spec/spec_helper.rb000066400000000000000000000055011330773730100167020ustar00rootroot00000000000000require 'yaml_db' # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| config.expect_with :rspec do |expectations| # This option will default to `true` in RSpec 4. It makes the `description` # and `failure_message` of custom matchers include text for helper methods # defined using `chain`, e.g.: # be_bigger_than(2).and_smaller_than(4).description # # => "be bigger than 2 and smaller than 4" # ...rather than: # # => "be bigger than 2" #expectations.include_chain_clauses_in_custom_matcher_descriptions = true end config.mock_with :rspec do |mocks| # Prevents you from mocking or stubbing a method that does not exist on # a real object. This is generally recommended, and will default to # `true` in RSpec 4. #mocks.verify_partial_doubles = true end # These two settings work together to allow you to limit a spec run # to individual examples or groups you care about by tagging them with # `:focus` metadata. When nothing is tagged with `:focus`, all examples # get run. config.filter_run :focus config.run_all_when_everything_filtered = true # Limits the available syntax to the non-monkey patched syntax that is recommended. # For more details, see: # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching config.disable_monkey_patching! # This setting enables warnings. It's recommended, but in some cases may # be too noisy due to issues in dependencies. config.warnings = true # Many RSpec users commonly either run the entire suite or an individual # file, and it's useful to allow more verbose output when running an # individual spec file. if config.files_to_run.one? # Use the documentation formatter for detailed output, # unless a formatter has already been configured # (e.g. via a command-line flag). config.default_formatter = 'doc' end # Print the 10 slowest examples and example groups at the # end of the spec run, to help surface which specs are running # particularly slow. #config.profile_examples = 10 # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = :random # Seed global randomization in this process using the `--seed` CLI option. # Setting this allows you to use `--seed` to deterministically reproduce # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed end yaml_db-0.7.0/spec/tasks/000077500000000000000000000000001330773730100152105ustar00rootroot00000000000000yaml_db-0.7.0/spec/tasks/yaml_db_tasks_spec.rb000066400000000000000000000032621330773730100213660ustar00rootroot00000000000000require 'rake' RSpec.describe 'Rake tasks' do before do Rake::Application.new.rake_require('tasks/yaml_db_tasks') Rake::Task.define_task(:environment) end subject { Rake::Task[self.class.description] } describe 'db:dump' do it 'depends on db:schema:dump and db:data:dump' do expect(subject.prerequisites).to eq(['db:schema:dump', 'db:data:dump']) end end describe 'db:load' do it 'depends on db:schema:load and db:data:load' do expect(subject.prerequisites).to eq(['db:schema:load', 'db:data:load']) end end describe 'db:data:dump' do it 'loads the environment' do expect(subject.prerequisites).to eq(['environment']) end it 'invokes the correct task' do expect(YamlDb::RakeTasks).to receive(:data_dump_task).once.with(no_args) subject.invoke end end describe 'db:data:dump_dir' do it 'loads the environment' do expect(subject.prerequisites).to eq(['environment']) end it 'invokes the correct task' do expect(YamlDb::RakeTasks).to receive(:data_dump_dir_task).once.with(no_args) subject.invoke end end describe 'db:data:load' do it 'loads the environment' do expect(subject.prerequisites).to eq(['environment']) end it 'invokes the correct task' do expect(YamlDb::RakeTasks).to receive(:data_load_task).once.with(no_args) subject.invoke end end describe 'db:data:load_dir' do it 'loads the environment' do expect(subject.prerequisites).to eq(['environment']) end it 'invokes the correct task' do expect(YamlDb::RakeTasks).to receive(:data_load_dir_task).once.with(no_args) subject.invoke end end end yaml_db-0.7.0/spec/yaml_db/000077500000000000000000000000001330773730100154725ustar00rootroot00000000000000yaml_db-0.7.0/spec/yaml_db/dump_spec.rb000066400000000000000000000037201330773730100200000ustar00rootroot00000000000000module YamlDb RSpec.describe Dump do before do allow(ActiveRecord::Base).to receive(:connection).and_return(double('connection').as_null_object) allow(ActiveRecord::Base.connection).to receive(:tables).and_return([ 'mytable', 'schema_info', 'schema_migrations' ]) allow(ActiveRecord::Base.connection).to receive(:columns).with('mytable').and_return([ double('a',:name => 'a', :type => :string), double('b', :name => 'b', :type => :string) ]) allow(ActiveRecord::Base.connection).to receive(:select_one).and_return({"count"=>"2"}) allow(ActiveRecord::Base.connection).to receive(:select_all).and_return([ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ]) allow(Utils).to receive(:quote_table).with('mytable').and_return('mytable') end before(:each) do allow(File).to receive(:open).with('dump.yml', 'w').and_yield(StringIO.new) @io = StringIO.new end it "returns a formatted string" do Dump.table_record_header(@io) @io.rewind expect(@io.read).to eq(" records: \n") end it "returns a yaml string that contains a table header and column names" do allow(Dump).to receive(:table_column_names).with('mytable').and_return([ 'a', 'b' ]) Dump.dump_table_columns(@io, 'mytable') @io.rewind if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3') expect(@io.read).to eq(< 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 }] ) Dump.dump_table_records(@io, 'mytable') @io.rewind expect(@io.read).to eq(< 'sqlite3', :database => ':memory:' ) # define a dummy User model class User < ActiveRecord::Base end # create the users table ActiveRecord::Schema.define do self.verbose = false create_table :users, :force => true do |t| t.string :username end end # add some users User.create([ {:username => 'alice'}, {:username => 'bob'} ]) RSpec.describe "with real ActiveRecord," do it "contains two users" do expect(User.count).to eq(2) end it "dumps the user records" do @io = StringIO.new YamlDb::Dump.dump_table_records(@io, 'users') @io.rewind expect(@io.read).to eq(< { 'columns' => [ 'a', 'b' ], 'records' => [[1, 2], [3, 4]] } } ) expect(Load).to receive(:load_table).with('mytable', { 'columns' => [ 'a', 'b' ], 'records' => [[1, 2], [3, 4]] },true) Load.load(@io) end it "calls load structure when the document in the file contains no records" do expect(YAML).to receive(:load_stream).with(@io).and_yield({ 'mytable' => nil }) expect(Load).not_to receive(:load_table) Load.load(@io) end end end yaml_db-0.7.0/spec/yaml_db/rake_tasks_spec.rb000066400000000000000000000070131330773730100211610ustar00rootroot00000000000000module YamlDb RSpec.describe RakeTasks do before do @serializer = instance_double(SerializationHelper::Base) allow(SerializationHelper::Base).to receive(:new).and_return(@serializer) allow(Rails).to receive(:root).and_return('/root') allow(Time).to receive(:now).and_return(Time.parse('2007-08-09 12:34:56')) stub_const('UserSpecifiedHelper', Class.new) allow(UserSpecifiedHelper).to receive(:extension).and_return('ext') end describe '.data_dump_task' do it 'dumps to a file' do expect(SerializationHelper::Base).to receive(:new).once.with(Helper) expect(@serializer).to receive(:dump).once.with('/root/db/data.yml') RakeTasks.data_dump_task end it 'dumps to a file using a user-specified format class' do stub_const('ENV', 'class' => 'UserSpecifiedHelper') expect(SerializationHelper::Base).to receive(:new).once.with(UserSpecifiedHelper) expect(@serializer).to receive(:dump).once.with('/root/db/data.ext') RakeTasks.data_dump_task end end describe '.data_dump_dir_task' do it 'dumps to a directory' do expect(SerializationHelper::Base).to receive(:new).once.with(Helper) expect(@serializer).to receive(:dump_to_dir).once.with('/root/db/2007-08-09T123456') RakeTasks.data_dump_dir_task end it 'dumps to a directory using a user-specified format class' do stub_const('ENV', 'class' => 'UserSpecifiedHelper') expect(SerializationHelper::Base).to receive(:new).once.with(UserSpecifiedHelper) expect(@serializer).to receive(:dump_to_dir).once.with('/root/db/2007-08-09T123456') RakeTasks.data_dump_dir_task end it 'dumps to a user-specified directory' do stub_const('ENV', 'dir' => 'user_dir') expect(SerializationHelper::Base).to receive(:new).once.with(Helper) expect(@serializer).to receive(:dump_to_dir).once.with('/root/db/user_dir') RakeTasks.data_dump_dir_task end end describe '.data_load_task' do it 'loads a file' do expect(SerializationHelper::Base).to receive(:new).once.with(Helper) expect(@serializer).to receive(:load).once.with('/root/db/data.yml') RakeTasks.data_load_task end it 'loads a file using a user-specified format class' do stub_const('ENV', 'class' => 'UserSpecifiedHelper') expect(SerializationHelper::Base).to receive(:new).once.with(UserSpecifiedHelper) expect(@serializer).to receive(:load).once.with('/root/db/data.ext') RakeTasks.data_load_task end end describe '.data_load_dir_task' do it 'loads a directory' do expect(SerializationHelper::Base).to receive(:new).once.with(Helper) expect(@serializer).to receive(:load_from_dir).once.with('/root/db/base') RakeTasks.data_load_dir_task end it 'loads a directory using a user-specified format class' do stub_const('ENV', 'class' => 'UserSpecifiedHelper') expect(SerializationHelper::Base).to receive(:new).once.with(UserSpecifiedHelper) expect(@serializer).to receive(:load_from_dir).once.with('/root/db/base') RakeTasks.data_load_dir_task end it 'loads a user-specified directory' do stub_const('ENV', 'dir' => 'user_dir') expect(SerializationHelper::Base).to receive(:new).once.with(Helper) expect(@serializer).to receive(:load_from_dir).once.with('/root/db/user_dir') RakeTasks.data_load_dir_task end end end end yaml_db-0.7.0/spec/yaml_db/serialization_helper_base_spec.rb000066400000000000000000000035631330773730100242460ustar00rootroot00000000000000module YamlDb module SerializationHelper RSpec.describe Base do def prestub_active_record end before do @io = StringIO.new allow(ActiveRecord::Base).to receive(:connection).and_return(double('connection')) allow(ActiveRecord::Base.connection).to receive(:tables).and_return([ 'mytable', 'schema_info', 'schema_migrations' ]) end def stub_helper! @helper = double("MyHelper") @dumper = double("MyDumper"); @loader = double("MyLoader"); allow(@helper).to receive(:dumper).and_return(@dumper) allow(@helper).to receive(:loader).and_return(@loader) allow(@helper).to receive(:extension).and_return("yml") allow(@dumper).to receive(:tables).and_return([ActiveRecord::Base.connection.tables[0]]) allow(@dumper).to receive(:before_table).and_return(nil) allow(@dumper).to receive(:after_table).and_return(nil) end context "for multi-file dumps" do before do expect(File).to receive(:open).once.with("dir_name/mytable.yml", "w").and_yield(@io) expect(Dir).to receive(:mkdir).once.with("dir_name") stub_helper! expect(@dumper).to receive(:dump_table).once.with(@io, "mytable") end it "creates the number of files that there are tables" do Base.new(@helper).dump_to_dir "dir_name" end end context "for multi-file loads" do before do stub_helper! expect(@loader).to receive(:load).once.with(@io, true) expect(File).to receive(:new).once.with("dir_name/mytable.yml", "r").and_return(@io) allow(Dir).to receive(:entries).and_return(["mytable.yml"]) end it "inserts into the number of tables that there are files" do Base.new(@helper).load_from_dir "dir_name" end end end end end yaml_db-0.7.0/spec/yaml_db/serialization_helper_dump_spec.rb000066400000000000000000000074571330773730100243070ustar00rootroot00000000000000module YamlDb module SerializationHelper RSpec.describe Dump do before do allow(ActiveRecord::Base).to receive(:connection).and_return(double('connection').as_null_object) allow(ActiveRecord::Base.connection).to receive(:tables).and_return([ 'mytable', 'schema_info', 'schema_migrations' ]) allow(ActiveRecord::Base.connection).to receive(:columns).with('mytable').and_return([ double('a', :name => 'a', :type => :string), double('b', :name => 'b', :type => :string) ]) allow(ActiveRecord::Base.connection).to receive(:select_one).and_return({"count"=>"2"}) allow(ActiveRecord::Base.connection).to receive(:select_all).and_return([ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ]) allow(Utils).to receive(:quote_table).with('mytable').and_return('mytable') end before(:each) do allow(File).to receive(:open).with('dump.yml', 'w').and_yield(StringIO.new) @io = StringIO.new end it "returns a list of column names" do expect(Dump.table_column_names('mytable')).to eq([ 'a', 'b' ]) end it "returns the total number of records in a table" do expect(Dump.table_record_count('mytable')).to eq(2) end describe ".each_table_page" do before do allow(Dump).to receive(:sort_keys) end it "returns all records from the database and returns them when there is only 1 page" do Dump.each_table_page('mytable') do |records| expect(records).to eq([ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ]) end end it "paginates records from the database and returns them" do allow(ActiveRecord::Base.connection).to receive(:select_all).and_return([ { 'a' => 1, 'b' => 2 } ], [ { 'a' => 3, 'b' => 4 } ]) records = [ ] Dump.each_table_page('mytable', 1) do |page| expect(page.size).to eq(1) records.concat(page) end expect(records).to eq([ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ]) end end it "dumps a table's contents to yaml" do expect(Dump).to receive(:dump_table_columns) expect(Dump).to receive(:dump_table_records) Dump.dump_table(@io, 'mytable') end it "does not dump a table's contents when the record count is zero" do allow(Dump).to receive(:table_record_count).with('mytable').and_return(0) expect(Dump).not_to receive(:dump_table_columns) expect(Dump).not_to receive(:dump_table_records) Dump.dump_table(@io, 'mytable') end describe ".tables" do it "returns a list of tables without the rails schema table" do expect(Dump.tables).to eq(['mytable']) end it "returns the list of tables in a consistent (sorted) order" do allow(ActiveRecord::Base.connection).to receive(:tables).and_return(%w(z y x)) expect(Dump.tables).to eq(%w(x y z)) end end describe ".sort_keys" do before do allow(Utils).to receive(:quote_column) { |column| column } end it "returns the first column as sort key" do expect(Dump.sort_keys('mytable')).to eq(['a']) end it "returns the combined ids as sort key if the table looks like a HABTM" do allow(ActiveRecord::Base.connection).to receive(:columns).with('mytable').and_return([ double('a_id', :name => 'a_id', :type => :string), double('b_id', :name => 'b_id', :type => :string) ]) expect(Dump.sort_keys('mytable')).to eq(['a_id', 'b_id']) end it "quotes the column name" do allow(Utils).to receive(:quote_column).with('a').and_return('`a`') expect(Dump.sort_keys('mytable')).to eq(['`a`']) end end end end end yaml_db-0.7.0/spec/yaml_db/serialization_helper_load_spec.rb000066400000000000000000000076361330773730100242600ustar00rootroot00000000000000module YamlDb module SerializationHelper RSpec.describe Load do before do allow(Utils).to receive(:quote_table).with('mytable').and_return('mytable') allow(ActiveRecord::Base).to receive(:connection).and_return(double('connection').as_null_object) allow(ActiveRecord::Base.connection).to receive(:transaction).and_yield @io = StringIO.new end it "truncates the table" do allow(ActiveRecord::Base.connection).to receive(:execute).with("TRUNCATE mytable").and_return(true) expect(ActiveRecord::Base.connection).not_to receive(:execute).with("DELETE FROM mytable") Load.truncate_table('mytable') end it "deletes the table if truncate throws an exception" do expect(ActiveRecord::Base.connection).to receive(:execute).with("TRUNCATE mytable").and_raise() expect(ActiveRecord::Base.connection).to receive(:execute).with("DELETE FROM mytable").and_return(true) Load.truncate_table('mytable') end it "calls reset pk sequence if the connection adapter is postgres" do expect(ActiveRecord::Base.connection).to receive(:respond_to?).with(:reset_pk_sequence!).and_return(true) expect(ActiveRecord::Base.connection).to receive(:reset_pk_sequence!).with('mytable') Load.reset_pk_sequence!('mytable') end it "does not call reset pk sequence for other adapters" do expect(ActiveRecord::Base.connection).to receive(:respond_to?).with(:reset_pk_sequence!).and_return(false) expect(ActiveRecord::Base.connection).not_to receive(:reset_pk_sequence!) Load.reset_pk_sequence!('mytable') end it "inserts records into a table" do allow(ActiveRecord::Base.connection).to receive(:quote_column_name).with('a').and_return('a') allow(ActiveRecord::Base.connection).to receive(:quote_column_name).with('b').and_return('b') allow(ActiveRecord::Base.connection).to receive(:quote).with(1).and_return("'1'") allow(ActiveRecord::Base.connection).to receive(:quote).with(2).and_return("'2'") allow(ActiveRecord::Base.connection).to receive(:quote).with(3).and_return("'3'") allow(ActiveRecord::Base.connection).to receive(:quote).with(4).and_return("'4'") expect(ActiveRecord::Base.connection).to receive(:execute).with("INSERT INTO mytable (a,b) VALUES ('1','2')") expect(ActiveRecord::Base.connection).to receive(:execute).with("INSERT INTO mytable (a,b) VALUES ('3','4')") Load.load_records('mytable', ['a', 'b'], [[1, 2], [3, 4]]) end it "quotes column names that correspond to sql keywords" do allow(ActiveRecord::Base.connection).to receive(:quote_column_name).with('a').and_return('a') allow(ActiveRecord::Base.connection).to receive(:quote_column_name).with('count').and_return('"count"') allow(ActiveRecord::Base.connection).to receive(:quote).with(1).and_return("'1'") allow(ActiveRecord::Base.connection).to receive(:quote).with(2).and_return("'2'") allow(ActiveRecord::Base.connection).to receive(:quote).with(3).and_return("'3'") allow(ActiveRecord::Base.connection).to receive(:quote).with(4).and_return("'4'") expect(ActiveRecord::Base.connection).to receive(:execute).with("INSERT INTO mytable (a,\"count\") VALUES ('1','2')") expect(ActiveRecord::Base.connection).to receive(:execute).with("INSERT INTO mytable (a,\"count\") VALUES ('3','4')") Load.load_records('mytable', ['a', 'count'], [[1, 2], [3, 4]]) end it "truncates the table and then loads the records into the table" do expect(Load).to receive(:truncate_table).with('mytable') expect(Load).to receive(:load_records).with('mytable', ['a', 'b'], [[1, 2], [3, 4]]) expect(Load).to receive(:reset_pk_sequence!).with('mytable') Load.load_table('mytable', { 'columns' => [ 'a', 'b' ], 'records' => [[1, 2], [3, 4]] }) end end end end yaml_db-0.7.0/spec/yaml_db/serialization_helper_utils_spec.rb000066400000000000000000000043631330773730100244730ustar00rootroot00000000000000module YamlDb module SerializationHelper RSpec.describe Utils do before do allow(ActiveRecord::Base).to receive(:connection).and_return(double('connection').as_null_object) end it "returns an array of hash values using an array of ordered keys" do expect(Utils.unhash({ 'a' => 1, 'b' => 2 }, [ 'b', 'a' ])).to eq([ 2, 1 ]) end it "unhashes each hash to an array using an array of ordered keys" do expect(Utils.unhash_records([ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ], [ 'b', 'a' ])).to eq([ [ 2, 1 ], [ 4, 3 ] ]) end it "returns true if it is a boolean type" do expect(Utils.is_boolean(true)).to be true expect(Utils.is_boolean('true')).to be false end it "returns an array of boolean columns" do allow(ActiveRecord::Base.connection).to receive(:columns).with('mytable').and_return([ double('a',:name => 'a',:type => :string), double('b', :name => 'b',:type => :boolean) ]) expect(Utils.boolean_columns('mytable')).to eq(['b']) end it "quotes the table name" do expect(ActiveRecord::Base.connection).to receive(:quote_table_name).with('values').and_return('`values`') expect(Utils.quote_table('values')).to eq('`values`') end it "converts ruby booleans to true and false" do expect(Utils.convert_boolean(true)).to be true expect(Utils.convert_boolean(false)).to be false end it "converts ruby strings t and f to true and false" do expect(Utils.convert_boolean('t')).to be true expect(Utils.convert_boolean('f')).to be false end it "converts ruby strings 1 and 0 to true and false" do expect(Utils.convert_boolean('1')).to be true expect(Utils.convert_boolean('0')).to be false end it "converts ruby integers 1 and 0 to true and false" do expect(Utils.convert_boolean(1)).to be true expect(Utils.convert_boolean(0)).to be false end describe ".quote_column" do it "quotes the column name" do allow(ActiveRecord::Base.connection).to receive(:quote_column_name).with('id').and_return('`id`') expect(Utils.quote_column('id')).to eq('`id`') end end end end end yaml_db-0.7.0/spec/yaml_db/utils_spec.rb000066400000000000000000000006231330773730100201720ustar00rootroot00000000000000module YamlDb RSpec.describe Utils do it "turns an array with one record into a yaml chunk" do expect(Utils.chunk_records([ %w(a b) ])).to eq(<= 3.0" s.add_runtime_dependency "rake", ">= 0.8.7" s.add_development_dependency "bundler", "~> 1.14" s.add_development_dependency "rspec", "~> 3.0" s.add_development_dependency "sqlite3", "~> 1.3" end