0.0
No commit activity in last 3 years
No release in over 3 years
Extends default CSV behavior to ActiveRecord models
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

 Project Readme

Overview

Extend default CSV behavior to Active Record models making it easier to offer CSV downloads. Default behavior is to iterate over all belongs_to, has_one and has_many associations and add methods for names and counts.

Installation and Usage

gem 'acts_as_csv'
$ bundle update

And then for an Order model you can do the following:

rails c
=> Order.first.to_csv
# ['1', 'Some Name']
=> Order.csv_columns
# ['id', 'name']

So a build_csv method would look something like this:

def self.build_csv(collection)
  CSV.generate do |csv|
    csv << csv_columns
    collection.each { |record| csv << record.to_csv }
  end
end

Default Associations Output Behavior

Singluar (belongs_to and has_one)

For belongs_to and has_one associations, the to_csv instance method and csv_column s class method will check if the target model has a name attribute and will output if it exists. Otherwise it will default to outputting the _id attribute of the target model.

Multipe (has_many)

For has_many associations, the default behavior is to add a count attribute. This can be overridden by adding a method in the class of the model you want to ovverride. Example below:

#order.rb

class Order < ActiveRecord::Base

	has_many :line_items

    def has_many_associations
      self.reflect_on_all_associations(:has_many).collect {|assoc| "#{assoc.name}_total_amount"}
    end

    def line_items_total_amount
    	line_items.sum(:amount)
    end

end

Additional configuration

You can add additional columns to be outputted in the default CSV export by adding the optional_csv_attributes method to the target model:

class Order < ActiveRecord::Base

	# needs to return an array of strings representing method names that exist for the model
	def self.optional_csv_attributes
		['state']
	end

	def state
		return 'the state'
	end

end

Some columns should be filtered from output and you can create model level filters with the filter_names method:

class Order < ActiveRecord::Base
	
	# again it needs to return an array of strings 
	def self.filter_names
		['ssn', 'credit_card', 'password']
	end

end

Angular.js, ResourceController and fully-functional downloads

Here's the ResourceController setup with an amended index method to catch the .csv format request:

class OrdersController < ApplicationController

  def index
    ...
    respond_to do |format|
      format.json { render :index }
      format.csv { send_data build_csv, filename: "Orders-#{Date.today}.csv" }
    end
  end

  private

    def build_csv
      CSV.generate do |csv|
        csv << Order.csv_columns
        @orders.each { |order| csv << order.to_csv }
      end
    end

end

Working with Angular

You can't download files from a server using XHR requests which is what the ajax request from Angular's $http service makes. There's tons of janky workarounds on the web but it's easiest just to do <a target='_sef' href='/some/path?{{ransackParams(q)}}'> and then let the Rails server and browser handle the file downloading without Angular in the way.

We want to offer the flexibility to download any model using Ransack and only delivers the rows that meet the Ransack criteria. We have historically avoiding sending a get request with Ransack to the API from Angular because of the pain in Url encoding a complex JS object. What we overlooked was the built-in functionality of the jQuery library. So its as easy as this.

HTML

<a target='_self' href='/some/path.csv?{{ransackParams(q)}}'>

Angular controller

    $scope.ransackParams = function(q) {
        return jQuery.param( {q: q} );
    }