Object-Oriented File Importing and Parsing

Harlow Ward

The following is an example of file importing and parsing in Ruby using object-oriented techniques such as duck typing and dependency injection.

Inject a CSVParser dependency into a the Importer

# app/controllers/contact_imports_controller.rb:
class ContactImportsController < ApplicationController
  def new
    @importer = Importer.new
  end

  def create
    @importer = Importer.new(
      parser: CSVParser.new(import_file),
      import_type: import_type
    )
    @importer.import

    respond_with(@importer, location: contacts_path)
  end

  private

  def import_file
    params[:importer][:file]
  end

  def import_type
    params[:importer][:import_type]
  end
end

A given parser must respond to rows and an instance of the given import_type must respond to create

# app/models/importer.rb
class Importer
  include ActiveModel::Validations
  include ActiveModel::Conversion

  VALID_IMPORT_TYPES = ['contact']

  validates :import_type, inclusion: { in: VALID_IMPORT_TYPES }

  attr_reader :parser, :import_type

  def initialize(attributes={})
    @parser = attributes[:parser]
    @import_type = attributes[:import_type]
  end

  def import
    if valid?
      rows.each do |row|
        import_factory.create(row)
      end
    end
  end

  private

  def rows
    parser.rows
  end

  def import_factory
    import_type.to_s.constantize
  end
end

A Contact responds to create:

# app/models/contact.rb:
class Contact < ActiveRecord::Base
end

A CSVParser responds to rows:

# app/models/csv_parser.rb:
class CSVParser
  initialize(csv_file)
    @csv_file = csv_file
  end

  def rows
    # code that parses the CSV file and returns an array of hashes
  end
end

To import XML files instead of CSV files, replace the CSV parser with another object that responds to rows.

# app/models/xml_parser.rb
class XMLParser
  def initialize(xml_file)
    @xml_file = xml_file
  end

  def rows
    # code that parses the XML file and returns an array of hashes
  end
end

Takeaways

  • Create small objects with single responsibilities
  • Keep public interfaces consistant
  • Send messages between collaborating objects without querying type

What’s next

Detect emerging problems in your codebase with Ruby Science. We’ll deliver solutions for fixing them, and demonstrate techniques for building a Ruby on Rails application that will be fun to work on for years to come.

Grab a free sample of Ruby Science today!