Permalink
Browse files

Add console methods.

  • Loading branch information...
rroblak committed Nov 18, 2013
1 parent 5373693 commit f381350e057f31f06efd0ad7331153ef5387d7a1
View
@@ -5,6 +5,7 @@ gem 'activerecord'
group :development, :test do
gem 'byebug'
+ gem 'factory_girl'
end
group :development do
View
@@ -1,7 +1,10 @@
+require 'seed_dump/console_methods/enumeration'
+require 'seed_dump/console_methods'
require 'seed_dump/dump_methods'
class SeedDump
- include SeedDump::DumpMethods
+ extend ConsoleMethods
+ include DumpMethods
require 'seed_dump/railtie' if defined?(Rails)
end
@@ -0,0 +1,92 @@
+class SeedDump
+ module ConsoleMethods
+ include Enumeration
+
+ def dump(records, options = {})
+ return nil if records.count == 0
+
+ io = open_io(options)
+
+ write_records_to_io(records, io, options)
+
+ ensure
+ io.close if io.present?
+ end
+
+ private
+
+ def dump_record(record)
+ attribute_strings = []
+
+ # We select only string attribute names to avoid conflict
+ # with the composite_primary_keys gem (it returns composite
+ # primary key attribute names as hashes).
+ record.attributes.select {|key| key.is_a?(String) }.each do |attribute, value|
+ attribute_strings << dump_attribute_new(attribute, value) unless [:id, :created_at, :updated_at].include?(attribute.to_sym)
+ end
+
+ "{#{attribute_strings.join(", ")}}"
+ end
+
+ def dump_attribute_new(attribute, value)
+ "#{attribute}: #{value_to_s(value)}"
+ end
+
+ def value_to_s(value)
+ value = case value
+ when BigDecimal
+ value.to_s
+ when Date, Time, DateTime
+ value.to_s(:db)
+ else
+ value
+ end
+
+ value.inspect
+ end
+
+ def open_io(options)
+ if options[:file].present?
+ File.open(options[:file], 'w+')
+ else
+ StringIO.new('', 'w+')
+ end
+ end
+
+ def write_records_to_io(records, io, options)
+ io.write("#{model_for(records)}.create!([")
+
+ enumeration_method = if records.is_a?(ActiveRecord::Relation) || records.is_a?(Class)
+ :active_record_enumeration
+ else
+ :enumerable_enumeration
+ end
+
+ send(enumeration_method, records, io, options) do |record_strings, batch_number, last_batch_number|
+ io.write("#{record_strings.join(",\n" + (' ' * 16))}")
+
+ io.write(",\n#{' ' * 16}") if batch_number != last_batch_number
+ end
+
+ io.write("])\n")
+
+ if options[:file].present?
+ nil
+ else
+ io.rewind
+ io.read
+ end
+ end
+
+ def model_for(records)
+ if records.is_a?(Class)
+ records
+ elsif records.respond_to?(:model)
+ records.model
+ else
+ records[0].class
+ end
+ end
+
+ end
+end
@@ -0,0 +1,63 @@
+class SeedDump
+ module ConsoleMethods
+ module Enumeration
+ def active_record_enumeration(records, io, options)
+ # If the records don't already have an order,
+ # order them by primary key ascending.
+ if !records.respond_to?(:arel) || records.arel.orders.blank?
+ records.order("#{records.quoted_table_name}.#{records.quoted_primary_key} ASC")
+ end
+
+ batch_size, last_batch_number = batch_params_from(records, options)
+
+ # Loop through each batch
+ (1..last_batch_number).each do |batch_number|
+
+ record_strings = []
+
+ # Loop through the records of the current batch
+ records.offset((batch_number - 1) * batch_size).limit(batch_size).each do |record|
+ record_strings << dump_record(record)
+ end
+
+ yield record_strings, batch_number, last_batch_number
+ end
+ end
+
+ def enumerable_enumeration(records, io, options)
+ batch_size, last_batch_number = batch_params_from(records, options)
+
+ record_strings = []
+
+ batch_number = 1
+
+ records.each_with_index do |record, i|
+ record_strings << dump_record(record)
+
+ if (record_strings.length == batch_size) || (i == records.length - 1)
+ yield record_strings, batch_number, last_batch_number
+
+ record_strings = []
+ batch_number += 1
+ end
+ end
+ end
+
+ def batch_params_from(records, options)
+ batch_size = batch_size_from(options)
+
+ last_batch_number = (records.count.to_f / batch_size).ceil
+
+ [batch_size, last_batch_number]
+ end
+
+ def batch_size_from(options)
+ if options[:batch_size].present?
+ options[:batch_size].to_i
+ else
+ 1000
+ end
+ end
+ end
+ end
+end
@@ -82,7 +82,6 @@ def dump_model(model)
else
"\n#{model}.#{@opts['create_method']}([\n" << rows.join(",\n") << "\n])\n"
end
-
end
def dump_models
View
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe SeedDump do
+
+ describe '.dump' do
+ before do
+ Rails.application.eager_load!
+
+ create_db
+
+ 3.times { FactoryGirl.create(:sample) }
+
+ @expected_output = "Sample.create!([{string: \"string\", text: \"text\", integer: 42, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false},\n {string: \"string\", text: \"text\", integer: 42, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false},\n {string: \"string\", text: \"text\", integer: 42, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false}])\n"
+ end
+
+ context 'without file option' do
+ it 'should return the dump of the models passed in' do
+ SeedDump.dump(Sample).should eq(@expected_output)
+ end
+ end
+
+ context 'with file option' do
+ it 'should dump the models to the specified file' do
+ filename = Dir::Tmpname.make_tmpname(File.join(Dir.tmpdir, 'foo'), nil)
+
+ SeedDump.dump(Sample, file: filename)
+
+ File.open(filename) do |file|
+ file.read.should eq(@expected_output)
+ end
+ end
+ end
+
+ context 'with an order parameter' do
+ it 'should dump the models in the specified order' do
+ Sample.delete_all
+ samples = 3.times {|i| FactoryGirl.create(:sample, integer: i) }
+
+ SeedDump.dump(Sample.order('integer DESC')).should eq("Sample.create!([{string: \"string\", text: \"text\", integer: 2, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false},\n {string: \"string\", text: \"text\", integer: 1, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false},\n {string: \"string\", text: \"text\", integer: 0, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false}])\n")
+ end
+ end
+
+ context 'without an order parameter' do
+ it 'should dump the models sorted by primary key ascending' do
+ Sample.delete_all
+ samples = 3.times {|i| FactoryGirl.create(:sample, integer: i) }
+
+ SeedDump.dump(Sample).should eq("Sample.create!([{string: \"string\", text: \"text\", integer: 0, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false},\n {string: \"string\", text: \"text\", integer: 1, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false},\n {string: \"string\", text: \"text\", integer: 2, float: 3.14, decimal: \"2.72\", datetime: \"1776-07-04 19:14:00\", time: \"2000-01-01 03:15:00\", date: \"1863-11-19\", binary: \"binary\", boolean: false}])\n")
+ end
+ end
+
+ context 'with a batch_size parameter' do
+ it 'should not raise an exception' do
+
+ SeedDump.dump(Sample, batch_size: 100)
+ end
+ end
+
+ context 'with an array' do
+ it 'should return the dump of the models passed in' do
+ SeedDump.dump(Sample.all.to_a, batch_size: 2).should eq(@expected_output)
+ end
+
+ it 'should return nil if the array is empty' do
+ SeedDump.dump([]).should be(nil)
+ end
+ end
+ end
+end
View
@@ -0,0 +1,14 @@
+FactoryGirl.define do
+ factory :sample do
+ string 'string'
+ text 'text'
+ integer 42
+ float 3.14
+ decimal 2.72
+ datetime DateTime.parse('July 4, 1776 7:14pm UTC')
+ time Time.parse('3:15am UTC')
+ date Date.parse('November 19, 1863')
+ binary 'binary'
+ boolean false
+ end
+end
View
@@ -46,7 +46,6 @@ def create_db
t.float 'float'
t.decimal 'decimal'
t.datetime 'datetime'
- t.datetime 'timestamp'
t.time 'time'
t.date 'date'
t.binary 'binary'
View
@@ -26,7 +26,7 @@
load_sample_data
- @sd.dump_models.should match(/\nSample\.create!\(\[\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :timestamp => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n\]\)\n\n\n/)
+ @sd.dump_models.should eq("\nSample.create!([\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n])\n\n\n")
end
end
@@ -39,7 +39,7 @@
@sd.setup @env
- @sd.dump_models.should match(/\nSample\.create\(\[\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :timestamp => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n\]\)\n\n\n/)
+ @sd.dump_models.should eq("\nSample.create([\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n])\n\n\n")
end
end
@@ -53,7 +53,7 @@
@sd.setup @env
- @sd.dump_models.should match(/\nSample\.create!\(\[\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :timestamp => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n\]\)\n\n\n/)
+ @sd.dump_models.should eq("\nSample.create!([\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n])\n\n\n")
end
end
@@ -68,7 +68,7 @@
@sd.dump_models.should_not include('updated_at')
end
- it "should not exclude any attributes if it's specified as empty" do
+ it "should not exclude any attributes if it's specified as an empty string" do
load_sample_data
@env['EXCLUDE'] = ''
View
@@ -23,7 +23,7 @@
@sd.setup @env
- @sd.dump_models.should match(/\nSample\.create!\(\[\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :timestamp => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n\]\)\n\n\n/)
+ @sd.dump_models.should eq("\nSample.create!([\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n])\n\n\n")
end
it "should return the contents of the dump" do
@@ -33,7 +33,7 @@
@sd.setup @env
- @sd.dump_models.should match(/\nSample\.create!\(\[\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :timestamp => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n\]\)\n\n\n/)
+ @sd.dump_models.should match(/\nSample\.create!\(\[\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n\]\)\n\n\n/)
end
it 'should run ok without ActiveRecord::SchemaMigration being set (needed for Rails Engines)' do
@@ -55,7 +55,7 @@
load_sample_data
- @sd.dump_models.should match(/\nSample\.create!\(\[\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :timestamp => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n\]\)\n\n\n/)
+ @sd.dump_models.should match(/\nSample\.create!\(\[\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n\]\)\n\n\n/)
end
it "should skip any models that don't have have any rows" do
@@ -77,7 +77,7 @@
Sample.any_instance.stub(:attributes).and_return(attributes)
- @sd.dump_models.should eq("\nSample.create!([\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :timestamp => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n])\n\n\n")
+ @sd.dump_models.should eq("\nSample.create!([\n { :string => nil, :text => nil, :integer => nil, :float => nil, :decimal => nil, :datetime => nil, :time => nil, :date => nil, :binary => nil, :boolean => nil }\n])\n\n\n")
end
end
end
View
@@ -1,13 +1,19 @@
require 'seed_dump'
-require 'active_support/core_ext/string'
-require 'active_support/descendants_tracker'
+
+require 'active_support'
require 'active_record'
+
require 'byebug'
+
require 'database_cleaner'
+require 'factory_girl'
+
require './spec/helpers'
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
+FactoryGirl.find_definitions
+
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true

0 comments on commit f381350

Please sign in to comment.