Skip to content
Generate simple reports from Enumerable collections, from simple Arrays to ActiveRecord Collections.
Ruby
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib
test
.drone.yml
.gitignore
.ruby-version
.travis.yml
Gemfile
Jenkinsfile
LICENSE.txt
README.md
Rakefile
simple_report.gemspec

README.md

simple_report

Build Status Code Climate Test Coverage

simple_report is a gem for building simple reports from Enumerable collections. It was extracted from a larger application and only supports Excel exports for now, but we plan to add support for HTML reports as well (see TODO below). Other formats could be added if requested.

simple_report is meant to provide a simple way to get a collection of data out of your application and out to the user in a reasonable report, and do so without any hassle. It's not intended to collect, analyze, or transform data for you.

Installation

Add this line to your application's Gemfile:

gem 'simple_report'

And then execute:

$ bundle

Or install it yourself as:

$ gem install simple_report

Usage

Like a typical spreadsheet, each report may have multiple Sheets. Each sheet has multiple fields/columns. The data sources passed in can be any Enumerable collection, whether that's a simple Array or an ActiveRecord query does not matter.

Let's build our first report:

class CustomerReport < SimpleReport::Base
  def build_report
    data = Customer.all

    add_sheet('Customers', data) do |sheet|
      sheet.add_field('Username', :username)
      sheet.add_field('Full Name', width: 30) { |x| x.first_name + " " x.last_name }
      sheet.add_field('Billing Rate', :billing_rate, format: :money)
      sheet.add_field('SLA %', :sla, format: :percent)
      sheet.add_field('Placeholder', value: 'TBD')
    end
  end
end

report = CustomerReport.new
File.write('report.xlsx', report.to_xlsx)

This is pretty straightforward, and covers the entire API. We'll go over it in detail below.

class CustomerReport < SimpleReport::Base
  def build_report

Each report must subclass SimpleReport::Base and define the report in the build_report method. For this example, we're report on an an ActiveRecord collection of all Customerruby records.

    data = Customer.all

Our report data will be all the Customer objects returned by ActiveRecord

    add_sheet('Customers', data) do |sheet|

We want to add a Sheet to our report. It'll be labeled 'Customers', and include one row for every element in data. To include multiple Sheets in your report, simple add additional add_sheet blocks.

      sheet.add_field('Username', :username)

Adds a column labeled Username to the report, and populates it by invoking the :username method on each record in the data collection.

      sheet.add_field('Full Name', width: 30) { |x| x.first_name + " " x.last_name }

Adds a column labeled 'Full Name' to the report, and populates it by calling the provided block on each record. In this case, it's concatenating the user's name together. We're also setting a field width of 30.

      sheet.add_field('Billing Rate', :billing_rate, format: :money)

Add a column named 'Billing Rate', populate it by invoking :billing_rate on each record, and format the results as money.

      sheet.add_field('SLA %', :sla, format: :percent)

Add a column named 'SLA %', populate it by invoking :sla on each record, and format the results as a percentage.

      sheet.add_field('Placeholder', value: 'TBD')

Add a column 'Plaeholder' and populate it with the supplied value. This seemingly useless capability is surprisingly useful when you have to match a report full of boilerplate.

report = CustomerReport.new
File.write('report.xlsx', report.to_xlsx)

Instantiate the report, then write it to file.

Advanced Usage

Your report object and build_report method are just plain ruby code, so you can add logic to how your reports are generated. For example:

class FancyReport < SimpleReport::Base
  attr_accessor :include_wholesale

  def initialize
    @include_wholesale = true
  end

  def build_report
    data = Product.all
    add_sheet('Products', data) do |sheet|
      sheet.add_field('Name', :name)
      sheet.add_field('MSRP', :msrp, format: :money)
      sheet.add_field('Wholesale', :wholesale, format: :money) if @include_wholesale
    end
  end
end

report = FancyReport.new
report.include_wholesale = false
File.write('my_report.xlsx, report.to_xlsx)

You can also add attributes to the report to pass data into the report, rather than compute the data in the report as our simple examples have thus far:

class AnotherReport < SimpleReport::Base
  attr_accessor :data

  def build_report
    add_sheet 'Something', @data do |sheet|
      // define some fields
    end
  end
end

report = AnotherReport.new
report.data = Customer.all
File.write('another_report.xlsx', report.to_xlsx)

TODO

  • Add to_html method to render out as a simple table-based HTML report. We'll generate a table, with thead/tbody/tfoot, apply css classes based on formats, and apply basic text formatting such as number_to_currency or percent (%) characters. That will give the end-user enough flexibility to do whatever they want with the output, while still keeping the gem extremely simple
  • Add some concept of a footer/summary to the reports that allows operations like sum, average, minimum, maximum. When exported to xlsx, these will be exported as formulas. Actual values should be computed for HTML reports.

Development

After checking out the repo, run bundle install to install dependencies. Then, run rake test to run the tests.

To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/lostapathy/simple_report.

License

The gem is available as open source under the terms of the MIT License.

You can’t perform that action at this time.