• Stars
    star
    1,298
  • Rank 36,246 (Top 0.8 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 8 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Spreadsheet Architect is a library that allows you to create XLSX, ODS, or CSV spreadsheets super easily from ActiveRecord relations, plain Ruby objects, or tabular data.

Spreadsheet Architect

Gem Version CI Status RubyGems Downloads

Spreadsheet Architect is a library that allows you to create XLSX, ODS, or CSV spreadsheets super easily from ActiveRecord relations, plain Ruby objects, or tabular data.

Key Features:

  • Dead simple custom spreadsheets with custom data
  • Data Sources: Tabular Data from an Array, ActiveRecord relations, or array of plain Ruby object instances
  • Easily style and customize spreadsheets
  • Create multi sheet spreadsheets
  • Setting Class/Model or Project specific defaults
  • Simple to use ActionController renderers for Rails
  • Plain Ruby (without Rails) completely supported

Install

gem 'spreadsheet_architect'

General Usage

Tabular (Array) Data

The simplest and preferred usage is to simply create the data array yourself.

headers = ['Col 1','Col 2','Col 3']
data = [[1,2,3], [4,5,6], [7,8,9]]
SpreadsheetArchitect.to_xlsx(headers: headers, data: data)
SpreadsheetArchitect.to_ods(headers: headers, data: data)
SpreadsheetArchitect.to_csv(headers: headers, data: data)

Using this style will allow you to utilize any custom performance optimizations during your data generation process. This will come in handy when the spreadsheets get large and any loss in performance starts to matter.

Rails Relations or an Array of plain Ruby object instances

If you would like to add the methods to_xlsx, to_ods, to_csv, to_axlsx_package, to_rodf_spreadsheet to some class, you can simply include the SpreadsheetArchitect module to whichever classes you choose. For example:

class Post < ApplicationRecord
  include SpreadsheetArchitect
end

When using on an AR Relation or using the :instances option, SpreadsheetArchitect requires an instance method to be defined on the class to generate the data. By default it looks for the spreadsheet_columns method on the class. If you are using on an ActiveRecord model and that method is not defined, it would fallback to the models column_names method. If using the :data option this is completely ignored.

class Post
  include SpreadsheetArchitect

  def spreadsheet_columns
    ### Column format is: [Header, Cell Data / Method (if symbol) to Call on each Instance, (optional) Cell Type]
    [
      ['Title', :title],
      ['Content', content.strip],
      ['Author', (author.name if author)],
      ['Published?', (published ? 'Yes' : 'No')],
      :published_at, # uses the method name as header title Ex. 'Published At'
      ['# of Views', :number_of_views, :float],
      ['Rating', :rating],
      ['Category/Tags', "#{category.name} - #{tags.collect(&:name).join(', ')}"]
    ]
  end

end

Then use it on the class or ActiveRecord relations of the class

posts = Post.order(name: :asc).where(published: true)
posts.to_xlsx
posts.to_ods
posts.to_csv

# Plain Ruby Objects
posts_array = 10.times.map{|i| Post.new(number: i)}
Post.to_xlsx(instances: posts_array)
Post.to_ods(instances: posts_array)
Post.to_csv(instances: posts_array)

If you want to use a different method name then spreadsheet_columns you can pass a method name to the :spreadsheet_columns option.

Post.to_xlsx(instances: posts, spreadsheet_columns: :my_special_method)

Alternatively, you can pass a proc to the spreadsheet_columns option. For those purists that really dont want to define any extra spreadsheet_columns instance method on your model, this option can help you work with that methodology.

Post.to_xlsx(instances: posts, spreadsheet_columns: ->(instance){
  [
    ['Title', :title],
    ['Content', instance.content.strip],
    ['Author', (instance.author.name if instance.author)],
    ['Published?', (instance.published ? 'Yes' : 'No')],
    :published_at, # uses the method name as header title Ex. 'Published At'
    ['# of Views', :number_of_views, :float],
    ['Rating', :rating],
    ['Category/Tags', "#{instance.category.name} - #{instance.tags.collect(&:name).join(', ')}"],
    ['URL', :url, (val.start_with?("http") ? :hyperlink : :string)],
  ]
})

Sending & Saving Spreadsheets

Method 1: Save to a file manually

file_data = SpreadsheetArchitect.to_xlsx(headers: headers, data: data)

File.open('path/to/file.xlsx', 'w+b') do |f|
  f.write file_data
end

Method 2: Send Data via Rails Controller

class PostsController < ActionController::Base
  respond_to :html, :xlsx, :ods, :csv

  def index
    @posts = Post.order(published_at: :asc)

    render xlsx: @posts
  end

  # Using respond_with
  def index
    @posts = Post.order(published_at: :asc)

    respond_with @posts
  end

  # OR Using respond_with with custom options
  def index
    @posts = Post.order(published_at: :asc)

    if ['xlsx','ods','csv'].include?(request.format)
      respond_with @posts.to_xlsx(row_style: {bold: true}), filename: 'Posts'
    else
      respond_with @posts
    end
  end

  # OR Using responders
  def index
    @posts = Post.order(published_at: :asc)

    respond_to do |format|
      format.html
      format.xlsx { render xlsx: @posts }
      format.ods { render ods: @posts }
      format.csv{ render csv: @posts }
    end
  end

  # OR Using responders with custom options
  def index
    @posts = Post.order(published_at: :asc)

    respond_to do |format|
      format.html
      format.xlsx { render xlsx: @posts.to_xlsx(headers: false) }
      format.ods { render ods: Post.to_ods(instances: @posts) }
      format.csv{ render csv: @posts.to_csv(headers: false), filename: 'articles' }
    end
  end
end

Multi Sheet Spreadsheets

XLSX

axlsx_package = SpreadsheetArchitect.to_axlsx_package({headers: headers, data: data})
axlsx_package = SpreadsheetArchitect.to_axlsx_package({headers: headers, data: data}, axlsx_package)

File.open('path/to/multi_sheet_file.xlsx', 'w+b') do |f|
  f.write axlsx_package.to_stream.read
end

ODS

ods_spreadsheet = SpreadsheetArchitect.to_rodf_spreadsheet({headers: headers, data: data})
ods_spreadsheet = SpreadsheetArchitect.to_rodf_spreadsheet({headers: headers, data: data}, ods_spreadsheet)

File.open('path/to/multi_sheet_file.ods', 'w+b') do |f|
  f.write ods_spreadsheet.bytes
end

Methods

to_xlsx(options={})

Option Default Notes
data
2D Array
Cannot be used with the :instances option.

Tabular data for the non-header row cells.
instances
Array
Cannot be used with the :data option.

Array of class/model instances to be used as row data. Cannot be used with :data option
spreadsheet_columns
Proc/Symbol/String
Use this option to override or define the spreadsheet columns. Normally, if this option is not specified and are using the instances option/ActiveRecord relation, it uses the classes custom spreadsheet_columns method or any custom defaults defined.
If neither of those and is an ActiveRecord model, then it will falls back to the models self.column_names
Cannot be used with the :data option.

If a Proc value is passed it will be evaluated on the instance object.

If a Symbol or String value is passed then it will search the instance for a method name that matches and call it.
headers
Array / 2D Array
Data for the header row cells. If using on a class/relation, this defaults to the ones provided via spreadsheet_columns. Pass false to skip the header row.
sheet_name
String
Sheet1
header_style
Hash
{background_color: "AAAAAA", color: "FFFFFF", align: :center, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false} See all available style options here
row_style
Hash
{background_color: nil, color: "000000", align: :left, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false, format_code: nil} Styles for non-header rows. See all available style options here
column_styles
Array
See the kitchen sink example for usage
range_styles
Array
See the kitchen sink example for usage
conditional_row_styles
Array
See the kitchen sink example for usage. The if/unless proc will called with the following args: row_index, row_data
merges
Array
Merge cells. See the kitchen sink example for usage. Warning merges cannot overlap eachother, if you attempt to do so Excel will claim your spreadsheet is corrupt and refuse to open your spreadsheet.
borders
Array
See the kitchen sink example for usage
column_types
Array
Valid types for XLSX are :string, :integer, :float, :date, :time, :boolean, :hyperlink, nil = auto determine. You may also pass a Proc which evaluates to any of the valid types, for example ->(cell_val){ cell_val.start_with?('http') ? :hyperlink : :string }
column_widths
Array
Sometimes you may want explicit column widths. Use nil if you want a column to autofit again.
freeze_headers
Boolean
Make all header rows frozen/fixed so they do not scroll.
freeze
Hash
Make all specified row and/or column frozen/fixed so they do not scroll. See example usage
skip_defaults
Boolean
false Removes defaults and default styles. Particularily useful for heavily customized spreadsheets where the default styles get in the way.
escape_formulas
Boolean or Array
true Pass a single boolean to apply to all cells, or an array of booleans to control column-by-column. Advisable to be set true when involved with untrusted user input. See an example of the underlying functionality. NOTE: Header row cells are not escaped.
use_zero_based_row_index
Boolean
false Allows you to use zero-based row indexes when defining range_styles, merges, etc. Recommended to set this option for the whole project rather than per call. The original reason it was designed to be 1-based is because spreadsheet row numbers actually start with 1.

to_axlsx_spreadsheet(options={}, axlsx_package_to_join=nil)

Same options as to_xlsx

to_ods(options={})

Option Default Notes
data
2D Array
Cannot be used with the :instances option.

Tabular data for the non-header row cells.
instances
Array
Cannot be used with the :data option.

Array of class/model instances to be used as row data. Cannot be used with :data option
spreadsheet_columns
Proc/Symbol/String
Use this option to override or define the spreadsheet columns. Normally, if this option is not specified and are using the instances option/ActiveRecord relation, it uses the classes custom spreadsheet_columns method or any custom defaults defined.
If neither of those and is an ActiveRecord model, then it will falls back to the models self.column_names
Cannot be used with the :data option.

If a Proc value is passed it will be evaluated on the instance object.

If a Symbol or String value is passed then it will search the instance for a method name that matches and call it.
headers
Array / 2D Array
Data for the header row cells. If using on a class/relation, this defaults to the ones provided via spreadsheet_columns. Pass false to skip the header row.
sheet_name
String
Sheet1
header_style
Hash
{background_color: "AAAAAA", color: "FFFFFF", align: :center, font_size: 10, bold: true} Note: Currently ODS only supports these options
row_style
Hash
{background_color: nil, color: "000000", align: :left, font_size: 10, bold: false} Styles for non-header rows. Currently ODS only supports these options
column_types
Array
Valid types for ODS are :string, :float, :date, :time, :boolean, :hyperlink, nil = auto determine. Due to RODF Issue #19, :date/:time will be converted to :string. You may also pass a Proc which evaluates to any of the valid types, for example ->(cell_val){ cell_val.start_with?('http') ? :hyperlink : :string }
skip_defaults
Boolean
false Skip defaults and default styles. Particularly useful for heavily customized spreadsheets where the default styles get in the way.

to_rodf_spreadsheet(options={}, spreadsheet_to_join=nil)

Same options as to_ods

to_csv(options={})

Option Default Notes
data
2D Array
Cannot be used with the :instances option.

Tabular data for the non-header row cells.
instances
Array
Cannot be used with the :data option.

Array of class/model instances to be used as row data. Cannot be used with :data option
spreadsheet_columns
Proc/Symbol/String
Use this option to override or define the spreadsheet columns. Normally, if this option is not specified and are using the instances option/ActiveRecord relation, it uses the classes custom spreadsheet_columns method or any custom defaults defined.
If neither of those and is an ActiveRecord model, then it will falls back to the models self.column_names
Cannot be used with the :data option.

If a Proc value is passed it will be evaluated on the instance object.

If a Symbol or String value is passed then it will search the instance for a method name that matches and call it.
headers
Array / 2D Array
Data for the header row cells. If using on a class/relation, this defaults to the ones provided via spreadsheet_columns. Pass false to skip the header row.

Change class-wide default method options

class Post < ApplicationRecord
  include SpreadsheetArchitect

  def spreadsheet_columns
    [:name, :content]
  end

  SPREADSHEET_OPTIONS = {
    headers: [
      ['My Post Report'],
      self.column_names.map{|x| x.titleize}
    ],
     spreadsheet_columns: :spreadsheet_columns,
    header_style: {background_color: 'AAAAAA', color: 'FFFFFF', align: :center, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
    row_style: {background_color: nil, color: '000000', align: :left, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
    sheet_name: self.name,
    column_styles: [],
    range_styles: [],
    conditional_row_styles: [],
    merges: [],
    borders: [],
    column_types: [],
  }
end

Change project-wide default method options

# config/initializers/spreadsheet_architect.rb

SpreadsheetArchitect.default_options = {
  headers: true,
  spreadsheet_columns: :spreadsheet_columns,
  header_style: {background_color: 'AAAAAA', color: 'FFFFFF', align: :center, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
  row_style: {background_color: nil, color: '000000', align: :left, font_name: 'Arial', font_size: 10, bold: false, italic: false, underline: false},
  sheet_name: 'My Project Export',
  column_styles: [],
  range_styles: [],
  conditional_row_styles: [],
  merges: [],
  borders: [],
  column_types: [],
  use_zero_based_row_index: false,
}

Kitchen Sink Examples with Styling for XLSX and ODS

See test "kitchen sink" for XLSX and ODS

Axlsx Style Reference

I have compiled a list of all available style options for axlsx here: docs/axlsx_style_reference.md

Tips for Reducing Memory Usage

  • Use the :data option instead of active record relations
  • Utilize the light_record gem

Testing / Validating your Spreadsheets

A wise word of advice, when testing your spreadsheets I recommend to use Excel instead of LibreOffice. This is because I have seen through testing, that where LibreOffice seems to just let most incorrect things just slide on through, Excel will not even open the spreadsheet as apparently it is much more strict about the spreadsheet validations. This will help you better identify any incorrect styling or customization issues.

Contributing

We use the appraisal gem for testing multiple versions of axlsx. Please use the following steps to test using appraisal.

  1. bundle exec appraisal install
  2. bundle exec appraisal rake test

At this time the spreadsheets generated by the test suite are manually inspected. After running the tests, the test output can be viewed in tmp/

Credits

Created & Maintained by Weston Ganger - @westonganger

More Repositories

1

rails_i18n_manager

Web interface to manage i18n translations helping to facilitate the editors of your translations. Provides a low-tech and complete workflow for importing, translating, and exporting your I18n translation files. Designed to allow you to keep the translation files inside your projects git repository where they should be.
Ruby
205
star
2

paper_trail-association_tracking

Plugin for the PaperTrail gem to track and reify associations
Ruby
118
star
3

rearmed-js

A collection of helpful methods and monkey patches for Arrays, Objects, Numbers, and Strings in Javascript
JavaScript
104
star
4

active_snapshot

Simplified snapshots and restoration for ActiveRecord models and associations with a transparent white-box implementation
Ruby
96
star
5

rodf

ODF generation library for Ruby
Ruby
53
star
6

protected_attributes_continued

The community continued version of protected_attributes for Rails 5+
Ruby
45
star
7

rearmed-rb

A collection of helpful methods and monkey patches for Arrays, Hash, Enumerables, Strings, Objects & Dates in Ruby
Ruby
41
star
8

sexy_form.rb

Dead simple HTML form field builder for Ruby with built-in support for many popular UI libraries such as Bootstrap
Ruby
38
star
9

rearmed_rails

A collection of helpful methods and monkey patches for Rails
Ruby
33
star
10

form_builder.cr

Dead simple HTML form builder for Crystal with built-in support for many popular UI libraries such as Bootstrap
Crystal
31
star
11

bootstrap-directional-buttons

Directional / Arrow buttons for Bootstrap
HTML
22
star
12

capistrano-precompile-chooser

Capistrano plugin to precompile your Rails assets locally, remotely, or not at all provided with a very convenient default terminal prompt.
Ruby
13
star
13

active_sort_order

The "easy-peasy" dynamic sorting pattern for ActiveRecord that your Rails apps deserve
Ruby
13
star
14

input-autogrow

jQuery plugin for autogrowing inputs
JavaScript
8
star
15

chosen-bootstrap-theme

A Bootstrap theme for Chosen Select that actually looks like Bootstrap
CSS
8
star
16

js-try

JS-Try is a Javascript implementation of the try method from Rails for safe navigation
JavaScript
6
star
17

chosen-material-theme

A Material theme for Chosen Select
CSS
6
star
18

search_architect

Dead simple, powerful and fully customizable searching for your Rails or ActiveRecord models and associations.
Ruby
6
star
19

rails_uuid_to_integer_primary_keys

A Rails Migration to convert your UUID primary keys back to integer / bigint primary keys
Ruby
5
star
20

pairer

Pairer is Rails app/engine to Easily rotate and keep track of working pairs
Ruby
5
star
21

chosen-remote-source

Provides remote data source support for chosen-js selects
JavaScript
4
star
22

input-case-enforcer

Enforce uppercase, lowercase, or Capitalized inputs & textareas
JavaScript
4
star
23

paperclip_utils

Collection of Paperclip processors and a Helper class for easier dynamic processors and styles on your Paperclip uploads
Ruby
4
star
24

active_record_simple_execute

Sanitize and Execute your raw SQL queries in ActiveRecord and Rails with a much more intuitive and shortened syntax
Ruby
4
star
25

active_record_case_insensitive_finders

Adds case-insensitive finder methods to Rails and ActiveRecord
Ruby
3
star
26

chosen-readonly

Readonly support for Chosen selects
JavaScript
3
star
27

better_exception_notifier

An exception notifier for Rails and Rack apps
Ruby
3
star
28

rails_nestable_layouts

Rails Nestable Layouts - Dead simple nested layouts for Rails
Ruby
3
star
29

accepts_nested_attributes_for_public_id

A patch for Rails to support using a public ID column instead of ID for use with accepts_nested_attributes_for
Ruby
2
star
30

minitest_change_assertions

Provides assertions for your Minitest suite to determine if an object has been changed
Ruby
2
star
31

common_website_scripts_and_styles

JavaScript
2
star
32

rails_template

Efficient Rails Template
Ruby
2
star
33

rearmed-css

CSS Utility Classes
CSS
2
star
34

basic_ruby_and_rails_style_guide

2
star
35

simple_assets

Dead simple HTML-based assets helper for Ruby. The main idea here is to promote re-usability for projects.
Ruby
2
star
36

active_record_alias_join

Easily add custom SQL table aliases for your joins and includes
2
star
37

github_activity_scraper.rb

Scrape your entire GitHub activity information for all-time. Helps for aggregating how much open source sh!t you get done.
Ruby
2
star
38

devise_whos_here

Devise extension for logging current active users logged in using only the fast Rails cache and not your database
Ruby
2
star
39

github_actions_ci_example

Ruby
1
star
40

jekyll_template

CSS
1
star
41

rubocop_template_for_productive_teams

Rubocop template designed speed up your teams development process instead of dragging it down
Ruby
1
star
42

rails_upgrade

Ruby
1
star
43

ruby_view_template_converters

Complete solutions to convert ERB, SLIM, AND HAML with the least amount of manual effort
Ruby
1
star
44

westonganger

1
star
45

no_bullshit_middleman_template

Template for efficient static website generation using Middleman
HTML
1
star
46

prawn_invoice

Dead simple Prawn based PDF invoice generator with support for custom invoice templates
Ruby
1
star
47

rails_dummy_app

Rails Dummy App and test_helper.rb for testing gems
Ruby
1
star
48

wagon_starter

Starter Template for LocomotiveCMS Wagon Sites
JavaScript
1
star
49

essential_capybara_helpers

A set of essential capybara helpers for everyday use
1
star
50

fast_try

FastTry is a simple method wrapper to the safe navigation operator in Ruby
Ruby
1
star
51

lazy_serialize

Lazy Serialize is an alternative to ActiveRecord's serialize method which does not serialize each column until the first call to the attribute.
Ruby
1
star
52

prawn_resume

Dead simple Prawn based PDF resume generator with support for custom resume templates
Ruby
1
star
53

automatic_rails_route_testing

Template for easy exception testing for all routes within a Rails app
Ruby
1
star
54

select-sync

Javascript plugin for HTML `select` elements to synchronize by selected or disabled options
JavaScript
1
star
55

rails_scrabble_with_friends

Simple web-based scrabble for you and your friends with zero friction authentication
Ruby
1
star
56

facebook_marketplace_scraper

A locally run Rails app to automatically search facebook marketplace for deals and aggregate them all in a list.
Ruby
1
star
57

wagon_kitchen_sink

Example website for LocomotiveCMS / Wagon
JavaScript
1
star
58

rails_clientside_javascript_error_handler

Easily handle client-side Javascript exceptions within your Rails app
Ruby
1
star
59

rails_custom_form_builder

A good example/starter pack for a custom form builder for use with Rails form_for
Ruby
1
star