lib/seed-fu/writer.rb in seed-fu-1.2.3 vs lib/seed-fu/writer.rb in seed-fu-2.0.0

- old
+ new

@@ -1,3 +1,132 @@ -require File.join( File.dirname(__FILE__), 'writer', 'abstract' ) -require File.join( File.dirname(__FILE__), 'writer', 'seed' ) -require File.join( File.dirname(__FILE__), 'writer', 'seed_many' ) +module SeedFu + # {Writer} is used to programmatically generated seed files. For example, you might want to write + # a script which converts data in a CSV file to a valid Seed Fu seed file, which can then be + # imported. + # + # @example Basic usage + # SeedFu::Writer.write('path/to/file.rb', :class_name => 'Person', :constraints => [:first_name, :last_name]) do |writer| + # writer.write(:first_name => 'Jon', :last_name => 'Smith', :age => 21) + # writer.write(:first_name => 'Emily', :last_name => 'McDonald', :age => 24) + # end + # + # # Writes the following to the file: + # # + # # Person.seed(:first_name, :last_name, + # # {:first_name=>"Jon", :last_name=>"Smith", :age=>21}, + # # {:first_name=>"Emily", :last_name=>"McDonald", :age=>24} + # # ) + class Writer + cattr_accessor :default_options + @@default_options = { + :chunk_size => 100, + :constraints => [:id], + :seed_type => :seed + } + + # @param [Hash] options + # @option options [String] :class_name *Required* The name of the Active Record model to + # generate seeds for + # @option options [Fixnum] :chunk_size (100) The number of seeds to write before generating a + # `# BREAK EVAL` line. (Chunking reduces memory usage when loading seeds.) + # @option options [:seed, :seed_once] :seed_type (:seed) The method to use when generating + # seeds. See {ActiveRecordExtension} for details. + # @option options [Array<Symbol>] :constraints ([:id]) The constraining attributes for the seeds + def initialize(options = {}) + @options = self.class.default_options.merge(options) + raise ArgumentError, "missing option :class_name" unless @options[:class_name] + end + + # Creates a new instance of {Writer} with the `options`, and then calls {#write} with the + # `io_or_filename` and `block` + def self.write(io_or_filename, options = {}, &block) + new(options).write(io_or_filename, &block) + end + + # Writes the necessary headers and footers, and yields to a block within which the actual + # seed data should be writting using the `#<<` method. + # + # @param [IO] io_or_filename The IO to which writes will be made. (If an `IO` is given, it is + # your responsibility to close it after writing.) + # @param [String] io_or_filename The filename of a file to make writes to. (Will be opened and + # closed automatically.) + # @yield [self] make calls to `#<<` within the block + def write(io_or_filename, &block) + raise ArgumentError, "missing block" unless block_given? + + if io_or_filename.respond_to?(:write) + write_to_io(io_or_filename, &block) + else + File.open(io_or_filename, 'w') do |file| + write_to_io(file, &block) + end + end + end + + # Add a seed. Must be called within a block passed to {#write}. + # @param [Hash] seed The attributes for the seed + def <<(seed) + raise "You must add seeds inside a SeedFu::Writer#write block" unless @io + + buffer = '' + + if chunk_this_seed? + buffer << seed_footer + buffer << "# BREAK EVAL\n" + buffer << seed_header + end + + buffer << ",\n" + buffer << ' ' + seed.inspect + + @io.write(buffer) + + @count += 1 + end + alias_method :add, :<< + + private + + def write_to_io(io) + @io, @count = io, 0 + @io.write(file_header) + @io.write(seed_header) + yield(self) + @io.write(seed_footer) + @io.write(file_footer) + ensure + @io, @count = nil, nil + end + + def file_header + <<-END +# DO NOT MODIFY THIS FILE, it was auto-generated. +# +# Date: #{Time.now} +# Seeding #{@options[:class_name]} +# Written with the command: +# +# #{$0} #{$*.join} +# + END + end + + def file_footer + <<-END +# End auto-generated file. + END + end + + def seed_header + constraints = @options[:constraints] && @options[:constraints].map(&:inspect).join(', ') + "#{@options[:class_name]}.#{@options[:seed_type]}(#{constraints}" + end + + def seed_footer + "\n)\n" + end + + def chunk_this_seed? + @count != 0 && (@count % @options[:chunk_size]) == 0 + end + end +end