Key Performance Indicators for Rails
Latest commit f8fb61e Jan 10, 2014 @sevos Version bump
Failed to load latest commit information.
app/models/kpi Extract ActiveSupport::Memoizable to KPI::Memoizable Dec 3, 2012
lib Removed ActiveSupport Memoizable dependency Jan 10, 2014
test MergedReport takes optional :title option. If no option given - title… May 19, 2011
.gitignore Removed ActiveSupport Memoizable dependency Jan 10, 2014
.rspec Initial commit to kpi. Feb 20, 2011
.ruby-version Removed ActiveSupport Memoizable dependency Jan 10, 2014
Gemfile Update dependencies Dec 3, 2012
Gemfile.lock Update dependencies Dec 3, 2012
LICENSE.txt Initial commit to kpi. Feb 20, 2011
README.rdoc Updating README May 15, 2011
Rakefile Update dependencies Dec 3, 2012
VERSION Removed ActiveSupport Memoizable dependency Jan 10, 2014
kpi.gemspec Version bump Jan 10, 2014


Key Performance Indicators

This gem helps you to track key indicators in your Rails app.

Have you even wanted to get daily reports with information:

  • how many users registered to my app?

  • how many users registered today?

  • what is a total revenue today?

  • how many times certain event occurred in app?

and so on?

This gem lets you define your application state reports with ease. Look:


Rails 3.x

Add to your Gemfile:

gem "kpi"

and run in console in project directory:

$ bundle

Rails 2.x

This gem is tested with Rails 2.3.8.

Defining report

Example report in app/models/daily_kpi_report.rb:

class DailyKpiReport < KPI::Report
  def users_count
    result "Users count", User.count, :description => "Total users count"

  def today_registrations_count
    result "Today registrations count", User.where("created_at > ?", @time - 24.hours)

  def total_income
    result "Total income", Order.sum(:total), :unit => 'EUR'

  def today_income
    result "Total income", Order.where(:created_at => today).sum(:total), :unit => 'EUR'


  def today
    ((time - 24.hours)..time)

Each defined method should return a KPI::Entry object with following accessors:

  • name

  • value

  • description

  • unit

A collection of KPI::Entry is available through Enuberable: Report#entries

It is important, if your indicator depends on current time (last 24 hours, last week), to rely on @time instance variable instead of This lets you to generate report for every moment in history:

report = => 2.days.ago)

Merged reports

You can create merged report from two or more reports of the same type. Look at examples:

> today =
> today.users_count.value
# => 20
> today.today_income.value
# => 350
> yesterday = =>
> yesterday.users_count.value
# => 16
> yesterday.today_income.value
# => 250
> diff =, yesterday) do |today, yesterday|
> "$$ (change)", today.value - yesterday.value
> end
> avg =, yesterday) do |*entries|
> "$$ (avg)", / entries.size
> end
> diff.users_count.value
# => 4
> avg.today_income.value
# => 300
> avg.today_income.unit
# => "EUR"

More reports can be passed to constructor of Merged report:

avg =, week_last, week_next_to_last) do |*entries|
  # ...


  • You can override report title by overriding title method in report:

    class DailyKpiReport < KPI::Report
      def title; "Your daily report"; end
      # ...
  • You can use Whenever gem to generate periodic reports

  • Private methods are not considered as indicator methods and can be used as helper methods:

    class WeeklyReport < KPI::Report
      # ...
      def week_range
        t_prev_mon_begin = time - (time.wday-1)*24*60*60 - time.hour*60*60 - time.min*60 - time.sec
        t_next_sun_end = t_prev_mon_begin + 7*24*60*60 - 1


  • Generators for configuration and reports

  • Database persistence - storing reports in different databases: PostgreSQL & Mongo prioritized - should be done in external gems

  • Support for “result” helper in block passed to constructor of MergedReport

  • Remove ActiveSupport requirement

Contributing to KPI

  • Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet

  • Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it

  • Fork the project

  • Start a feature/bugfix branch

  • Commit and push until you are happy with your contribution

  • Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.

  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.


Copyright © 2011 Artur Roszczyk. See LICENSE.txt for further details.