Created
January 31, 2019 23:07
-
-
Save scottserok/b1cb43166ebee062bae5a79f9abde00a to your computer and use it in GitHub Desktop.
Simple class to easily build CSV outputs for your POROs or ActiveRecord models.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# CsvBuilder configures a model to generate a resource. Inspired by ActiveAdmin. | |
# | |
# Usage example: | |
# | |
# csv_builder = CSVBuilder.new User, col_sep: ';' | |
# csv_builder.column :id | |
# csv_builder.column :email | |
# csv_builder.column("Name") { |resource| resource.display_name } | |
# csv_builder.headers %w[ID Email Name] | |
# csv_builder.scope { |scope| scope.where('created_at > ?', 7.days.ago) } # unavailable for POROs | |
# csv_builder.generate | |
# | |
class CsvBuilder | |
attr_reader :columns, :options | |
def initialize(klass, options = {}) | |
fail ArgumentError, 'Must provide a Class' unless klass.is_a?(Class) | |
@klass = klass | |
@options = options.reverse_merge(row_sep: "\n") | |
@columns = [] | |
@headers = [] | |
end | |
def column(str, &block) | |
columns << Column.new(str, block) | |
end | |
def header(str) | |
@headers << str | |
end | |
def headers(arr) | |
@headers = arr | |
end | |
def scope(&block) | |
if @klass.ancestors.include?(ActiveRecord::Base) # naive approach | |
@scope = block if block.is_a?(Proc) | |
else | |
raise StandardError.new(@klass.to_s + ' is not a decendant of ActiveRecord::Base') | |
end | |
end | |
def generate(io = '') | |
io << CSV.generate_line(generate_header_row, @options) | |
resources.each do |resource| | |
io << CSV.generate_line(generate_data_row(resource), @options) | |
end | |
io | |
end | |
class Column | |
attr_reader :name, :data | |
def initialize(str, block = nil) | |
@name = str | |
@data = block || str.to_sym | |
end | |
end | |
private | |
def generate_header_row | |
if @headers.empty? | |
columns.map do |column| | |
if column.name.is_a?(Symbol) | |
@klass.human_attribute_name(column.name) | |
else | |
column.name | |
end | |
end | |
else | |
@headers | |
end | |
end | |
def generate_data_row(resource) | |
columns.map do |column| | |
if column.data.is_a?(Proc) | |
column.data.call resource | |
else | |
resource.public_send(column.data) | |
end | |
end | |
end | |
def resources | |
if @scope | |
@scope.call(@klass).find_each.lazy | |
else | |
@klass.all.find_each.lazy | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment