CsvRails

CsvRails provides a simple way of download csv in Rails 3.

Supported versions

Ruby 1.9 (1.8 not supported) Rails 3 ActiveRecord or Mongoid

Install

To use CsvRails simply add the line

gem 'csv_rails'

to your Gemfile and then run

bundle install

Example

class UsersController < ApplicationController
  def index
    @users = User.all

    respond_to do |format|
      format.html { @users = @users.all }
      format.json { render json: @users }
      format.csv{ render csv: @users, fields: [:id, :name, :age], encoding: 'SJIS', without_header: true }
    end
  end

Usage

Download

If you want formatted attribute, CsvRails call “#attribute_as_csv”. For example, you wish formatted created_at then you write like this.

class User < ActiveRecord::Base
  def created_at_as_csv
    created_at.strftime("%F %H:%M")
  end
end

CsvRails define a singleton method Array.to_csv, and the method accept fields option. The fields option can not only database fields also method and method chain.

class User < ActiveRecord::Base
  has_many :memberships
  has_many :groups, through: :memberships

  def ok
    "OK"
  end
end

class UsersController < ApplicationController
  def index
    @users = User.all

    respond_to do |format|
      format.csv{ render csv: @users, fields: [:ok, :"groups.first.name"], encoding: 'SJIS' }
    end
  end

If you do not use :header option, header is using :fields and I18n transfer.

# config/locales/ja.yml
ja:
  activerecord:
    attributes:
      group: &groupmodel
        name: グループ名
      user:
        id: ID
        name: 名前
        age: 年齢
        ok: OK
      # rails3
        groups:
          first:
            <<: *groupmodel
      # rails 3.2.3 - 3.2.5
      user/groups:
        first:
          <<: *groupmodel
      # rails 3.2.6 or higher
      groups/first:
        <<: *groupmodel

# app/controllers/user_controller.rb
def index
  @users = User.where("id < 1").all
  respond_to do |format|
    format.csv{ render csv: @users, fields: [:ok, :"groups.first.name"], encoding: 'SJIS' } #=> "OK,グループ名"
  end
end

And you can use tsv. Both tsv and csv can accept row_sep option.

# app/controllers/user_controller.rb
def index
  @users = User.all
  respond_to do |format|
    format.csv{ render csv: @users, :row_sep => "\r\n" }
    format.tsv{ render tsv: @users, :row_sep => "\r\n" }
  end
end

You also use i18n_scope option

# config/locales/ja.yml
ja:
  csv:
    name: なまえ

User.where("id < 1").all.to_csv(:i18n_scope => :csv) #=> "なまえ\n"

Upload

CSVRails is also have upload concern.

You should include CSVRails::Import into your model.

example

# app/model/user.rb
class User < ActiveRecord::Base
  attr_accessor :file
  include CsvRails::Import
....

And render file_field into form

example

# app/views/users/index.html.erb

<%= form_for(@user, multipart: true) do |f| %>
  File: <%= f.file_field :file %>
  <%= f.submit 'Upload' %>
<% end %>

Next implement action

# app/controllers/users_controller.rb

def create
  if params[:format] == 'csv'
    users = User.csv_import(params[:file])
    if users.find{|u| u.errors.any? }
      render :index
    else
      redirect_to users_path, notice: 'Upload success'
    end
  end
end

csv_import is be able to use block.

User.csv_import(params[:file]) do |user, params, row_number|
  next false if row_number == 2 # 'next false' in the block, the line is skipped.
end

csv_import use transaction, if invalid line is existed then all rows is not imported.

First line used for fields, but you can manually choose it using options

User.csv_import(params[:file], fields: [:age, :name])

It line first line has ‘id’, csv_import call where(id: id).first_or_initialize, but you can use other field name

User.csv_import(params[:file], find_key: :name)
#=> It call User.where(name: name).first_or_initialize internal.

Copyright © 2012-2013 yalab, released under the MIT license