it's now called Saulable::Reportable instead of Saulabs::ReportsAsSparkline - reportable - Fork of reportable required by WarVox, from hdm/reportable.
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit 1fb6c00be0c30f0a4ce7c97035830c5506e5ffab
 (DIR) parent 933aa44869406866d90de51bd13453b910e8d12f
 (HTM) Author: Marco Otte-Witte <marco.otte-witte@simplabs.com>
       Date:   Wed, 24 Feb 2010 19:18:44 +0100
       
       it's now called Saulable::Reportable instead of Saulabs::ReportsAsSparkline
       
       Diffstat:
         M .gitignore                          |       2 +-
         M generators/reports_as_sparkline_mi… |       2 +-
         M generators/reports_as_sparkline_mi… |      12 ++++++------
         M init.rb                             |       6 +++---
         A lib/saulabs/reportable.rb           |      54 +++++++++++++++++++++++++++++++
         A lib/saulabs/reportable/cumulated_r… |      45 +++++++++++++++++++++++++++++++
         A lib/saulabs/reportable/grouping.rb  |     115 +++++++++++++++++++++++++++++++
         A lib/saulabs/reportable/report.rb    |     119 +++++++++++++++++++++++++++++++
         A lib/saulabs/reportable/report_cach… |     139 ++++++++++++++++++++++++++++++
         A lib/saulabs/reportable/reporting_p… |     103 +++++++++++++++++++++++++++++++
         A lib/saulabs/reportable/sparkline_t… |      47 +++++++++++++++++++++++++++++++
         D lib/saulabs/reports_as_sparkline.rb |      54 -------------------------------
         D lib/saulabs/reports_as_sparkline/c… |      45 -------------------------------
         D lib/saulabs/reports_as_sparkline/g… |     115 -------------------------------
         D lib/saulabs/reports_as_sparkline/r… |     119 -------------------------------
         D lib/saulabs/reports_as_sparkline/r… |     139 ------------------------------
         D lib/saulabs/reports_as_sparkline/r… |     103 -------------------------------
         D lib/saulabs/reports_as_sparkline/s… |      47 -------------------------------
         M spec/classes/cumulated_report_spec… |      28 ++++++++++++++--------------
         M spec/classes/grouping_spec.rb       |      38 ++++++++++++++++----------------
         M spec/classes/report_cache_spec.rb   |     112 ++++++++++++++++----------------
         M spec/classes/report_spec.rb         |      70 ++++++++++++++++----------------
         M spec/classes/reporting_period_spec… |      92 ++++++++++++++++----------------
         M spec/db/database.yml                |      14 +++++++-------
         M spec/db/schema.rb                   |       6 +++---
         M spec/other/report_method_spec.rb    |       6 +++---
         M spec/other/sparkline_tag_helper_sp… |       4 ++--
       
       27 files changed, 818 insertions(+), 818 deletions(-)
       ---
 (DIR) diff --git a/.gitignore b/.gitignore
       @@ -1,4 +1,4 @@
        .DS_Store
        spec/log/spec.log
       -spec/db/reports_as_sparkline.sqlite3.db
       +spec/db/reportable.sqlite3.db
        doc
 (DIR) diff --git a/generators/reports_as_sparkline_migration/reports_as_sparkline_migration_generator.rb b/generators/reports_as_sparkline_migration/reports_as_sparkline_migration_generator.rb
       @@ -1,4 +1,4 @@
       -class ReportsAsSparklineMigrationGenerator < Rails::Generator::NamedBase #:nodoc:
       +class ReportableMigrationGenerator < Rails::Generator::NamedBase #:nodoc:
        
          def manifest
            record do |m|
 (DIR) diff --git a/generators/reports_as_sparkline_migration/templates/migration.erb b/generators/reports_as_sparkline_migration/templates/migration.erb
       @@ -1,7 +1,7 @@
        class <%= class_name %> < ActiveRecord::Migration
        
          def self.up
       -    create_table :reports_as_sparkline_cache, :force => true do |t|
       +    create_table :reportable_cache, :force => true do |t|
              t.string   :model_name,       :null => false
              t.string   :report_name,      :null => false
              t.string   :grouping,         :null => false
       @@ -13,14 +13,14 @@ class <%= class_name %> < ActiveRecord::Migration
              t.timestamps
            end
        
       -    add_index :reports_as_sparkline_cache, [
       +    add_index :reportable_cache, [
              :model_name,
              :report_name,
              :grouping,
              :aggregation,
              :condition
            ], :name => :name_model_grouping_agregation
       -    add_index :reports_as_sparkline_cache, [
       +    add_index :reportable_cache, [
              :model_name,
              :report_name,
              :grouping,
       @@ -31,10 +31,10 @@ class <%= class_name %> < ActiveRecord::Migration
          end
        
          def self.down
       -    remove_index :reports_as_sparkline_cache, :name => :name_model_grouping_agregation
       -    remove_index :reports_as_sparkline_cache, :name => :name_model_grouping_aggregation_period
       +    remove_index :reportable_cache, :name => :name_model_grouping_agregation
       +    remove_index :reportable_cache, :name => :name_model_grouping_aggregation_period
        
       -    drop_table :reports_as_sparkline_cache
       +    drop_table :reportable_cache
          end
        
        end
 (DIR) diff --git a/init.rb b/init.rb
       @@ -1,9 +1,9 @@
       -require 'saulabs/reports_as_sparkline'
       +require 'saulabs/reportable'
        
        ActiveRecord::Base.class_eval do
       -  include Saulabs::ReportsAsSparkline
       +  include Saulabs::Reportable
        end
        
        ActionView::Base.class_eval do
       -  include Saulabs::ReportsAsSparkline::SparklineTagHelper
       +  include Saulabs::Reportable::SparklineTagHelper
        end
 (DIR) diff --git a/lib/saulabs/reportable.rb b/lib/saulabs/reportable.rb
       @@ -0,0 +1,54 @@
       +module Saulabs #:nodoc:
       +
       +  module Reportable
       +
       +    def self.included(base) #:nodoc:
       +      base.extend ClassMethods
       +    end
       +
       +    module ClassMethods
       +
       +      # Generates a report on a model. That report can then be executed via the new method <tt><name>_report</tt> (see documentation of Saulabs::Reportable::Report#run).
       +      # 
       +      # ==== Parameters
       +      #
       +      # * <tt>name</tt> - The name of the report, defines the name of the generated report method (<tt><name>_report</tt>)
       +      #
       +      # ==== Options
       +      #
       +      # * <tt>:date_column</tt> - The name of the date column over that the records are aggregated (defaults to <tt>created_at</tt>)
       +      # * <tt>:value_column</tt> - The name of the column that holds the values to sum up when using aggregation <tt>:sum</tt>
       +      # * <tt>:aggregation</tt> - The aggregation to use (one of <tt>:count</tt>, <tt>:sum</tt>, <tt>:minimum</tt>, <tt>:maximum</tt> or <tt>:average</tt>); when using anything other than <tt>:count</tt>, <tt>:value_column</tt> must also be specified (<b>If you really want to e.g. sum up the values in the <tt>id</tt> column, you have to explicitely say so.</b>); (defaults to <tt>:count</tt>)
       +      # * <tt>:grouping</tt> - The period records are grouped on (<tt>:hour</tt>, <tt>:day</tt>, <tt>:week</tt>, <tt>:month</tt>); <b>Beware that <tt>reportable</tt> treats weeks as starting on monday!</b>
       +      # * <tt>:limit</tt> - The number of reporting periods to get (see <tt>:grouping</tt>), (defaults to 100)
       +      # * <tt>:conditions</tt> - Conditions like in <tt>ActiveRecord::Base#find</tt>; only records that match the conditions are reported; <b>Beware that when conditions are specified, caching is disabled!</b>
       +      # * <tt>:live_data</tt> - Specifies whether data for the current reporting period is to be read; <b>if <tt>:live_data</tt> is <tt>true</tt>, you will experience a performance hit since the request cannot be satisfied from the cache only (defaults to <tt>false</tt>)</b>
       +      # * <tt>:end_date</tt> - When specified, the report will only include data for the <tt>:limit</tt> reporting periods until this date.
       +      #
       +      # ==== Examples
       +      #
       +      #  class User < ActiveRecord::Base
       +      #    reportable :registrations, :aggregation => :count
       +      #    reportable :activations,   :aggregation => :count, :date_column => :activated_at
       +      #    reportable :total_users,   :cumulate => true
       +      #    reportable :rake,          :aggregation => :sum,   :value_column => :profile_visits
       +      #  end
       +      def reportable(name, options = {})
       +        (class << self; self; end).instance_eval do
       +          define_method "#{name.to_s}_report".to_sym do |*args|
       +            if options.delete(:cumulate)
       +              report = Saulabs::Reportable::CumulatedReport.new(self, name, options)
       +            else
       +              report = Saulabs::Reportable::Report.new(self, name, options)
       +            end
       +            raise ArgumentError.new unless args.length == 0 || (args.length == 1 && args[0].is_a?(Hash))
       +            report.run(args.length == 0 ? {} : args[0])
       +          end
       +        end
       +      end
       +
       +    end
       +
       +  end
       +
       +end
 (DIR) diff --git a/lib/saulabs/reportable/cumulated_report.rb b/lib/saulabs/reportable/cumulated_report.rb
       @@ -0,0 +1,45 @@
       +module Saulabs #:nodoc:
       +
       +  module Reportable #:nodoc:
       +
       +    # A special report class that cumulates all data (see Saulabs::Reportable::Report)
       +    #
       +    # ==== Examples
       +    #
       +    # When Saulabs::Reportable::Report returns
       +    #
       +    #  [[<DateTime today>, 1], [<DateTime yesterday>, 2], etc.]
       +    #
       +    # Saulabs::Reportable::CumulatedReport returns
       +    #
       +    #  [[<DateTime today>, 3], [<DateTime yesterday>, 2], etc.]
       +    class CumulatedReport < Report
       +
       +      # Runs the report (see Saulabs::Reportable::Report#run)
       +      def run(options = {})
       +        cumulate(super, options_for_run(options))
       +      end
       +
       +      protected
       +
       +        def cumulate(data, options)
       +          first_reporting_period = ReportingPeriod.first(options[:grouping], options[:limit], options[:end_date])
       +          acc = initial_cumulative_value(first_reporting_period.date_time, options)
       +          result = []
       +          data.each do |element|
       +            acc += element[1].to_f
       +            result << [element[0], acc]
       +          end
       +          result
       +        end
       +
       +        def initial_cumulative_value(date, options)
       +          conditions = setup_conditions(nil, date, options[:conditions])
       +          @klass.send(@aggregation, @value_column, :conditions => conditions)
       +        end
       +
       +    end
       +
       +  end
       +
       +end
 (DIR) diff --git a/lib/saulabs/reportable/grouping.rb b/lib/saulabs/reportable/grouping.rb
       @@ -0,0 +1,115 @@
       +module Saulabs #:nodoc:
       +
       +  module Reportable #:nodoc:
       +
       +    class Grouping #:nodoc:
       +
       +      def initialize(identifier)
       +        raise ArgumentError.new("Invalid grouping #{identifier}") unless [:hour, :day, :week, :month].include?(identifier)
       +        @identifier = identifier
       +      end
       +
       +      def identifier
       +        @identifier
       +      end
       +
       +      def date_parts_from_db_string(db_string)
       +        case ActiveRecord::Base.connection.adapter_name
       +          when /mysql/i
       +            from_mysql_db_string(db_string)
       +          when /sqlite/i
       +            from_sqlite_db_string(db_string)
       +          when /postgres/i
       +            from_postgresql_db_string(db_string)
       +        end
       +      end
       +
       +      def to_sql(date_column) #:nodoc:
       +        case ActiveRecord::Base.connection.adapter_name
       +          when /mysql/i
       +            mysql_format(date_column)
       +          when /sqlite/i
       +            sqlite_format(date_column)
       +          when /postgres/i
       +            postgresql_format(date_column)
       +        end
       +      end
       +
       +      private
       +
       +        def from_mysql_db_string(db_string)
       +          if @identifier == :week
       +            parts = [db_string[0..3], db_string[4..5]].map(&:to_i)
       +          else
       +            db_string.split('/').map(&:to_i)
       +          end
       +        end
       +
       +        def from_sqlite_db_string(db_string)
       +          if @identifier == :week
       +            parts = db_string.split('-').map(&:to_i)
       +            date = Date.new(parts[0], parts[1], parts[2])
       +            return [date.cwyear, date.cweek]
       +          end
       +          db_string.split('/').map(&:to_i)
       +        end
       +
       +        def from_postgresql_db_string(db_string)
       +          case @identifier
       +            when :hour
       +              return (db_string[0..9].split('-') + [db_string[11..12]]).map(&:to_i)
       +            when :day
       +              return db_string[0..9].split('-').map(&:to_i)
       +            when :week
       +              parts = db_string[0..9].split('-').map(&:to_i)
       +              date = Date.new(parts[0], parts[1], parts[2])
       +              return [date.cwyear, date.cweek]
       +            when :month
       +              return db_string[0..6].split('-')[0..1].map(&:to_i)
       +          end
       +        end
       +
       +        def mysql_format(date_column)
       +          case @identifier
       +            when :hour
       +              "DATE_FORMAT(#{date_column}, '%Y/%m/%d/%H')"
       +            when :day
       +              "DATE_FORMAT(#{date_column}, '%Y/%m/%d')"
       +            when :week
       +              "YEARWEEK(#{date_column}, 3)"
       +            when :month
       +              "DATE_FORMAT(#{date_column}, '%Y/%m')"
       +          end
       +        end
       +
       +        def sqlite_format(date_column)
       +          case @identifier
       +            when :hour
       +              "strftime('%Y/%m/%d/%H', #{date_column})"
       +            when :day
       +              "strftime('%Y/%m/%d', #{date_column})"
       +            when :week
       +              "date(#{date_column}, 'weekday 0')"
       +            when :month
       +              "strftime('%Y/%m', #{date_column})"
       +          end
       +        end
       +
       +        def postgresql_format(date_column)
       +          case @identifier
       +            when :hour
       +              "date_trunc('hour', #{date_column})"
       +            when :day
       +              "date_trunc('day', #{date_column})"
       +            when :week
       +              "date_trunc('week', #{date_column})"
       +            when :month
       +              "date_trunc('month', #{date_column})"
       +          end
       +        end
       +
       +    end
       +
       +  end
       +
       +end
 (DIR) diff --git a/lib/saulabs/reportable/report.rb b/lib/saulabs/reportable/report.rb
       @@ -0,0 +1,119 @@
       +module Saulabs #:nodoc:
       +
       +  module Reportable #:nodoc:
       +
       +    # The Report class that does all the data retrieval and calculations
       +    class Report
       +
       +      attr_reader :klass, :name, :date_column, :value_column, :aggregation, :options
       +
       +      # ==== Parameters
       +      # * <tt>klass</tt> - The model the report works on (This is the class you invoke Saulabs::Reportable::ClassMethods#reportable on)
       +      # * <tt>name</tt> - The name of the report (as in Saulabs::Reportable::ClassMethods#reportable)
       +      #
       +      # ==== Options
       +      #
       +      # * <tt>:date_column</tt> - The name of the date column over that the records are aggregated (defaults to <tt>created_at</tt>)
       +      # * <tt>:value_column</tt> - The name of the column that holds the values to sum up when using aggregation <tt>:sum</tt>
       +      # * <tt>:aggregation</tt> - The aggregation to use (one of <tt>:count</tt>, <tt>:sum</tt>, <tt>:minimum</tt>, <tt>:maximum</tt> or <tt>:average</tt>); when using anything other than <tt>:count</tt>, <tt>:value_column</tt> must also be specified (<b>If you really want to e.g. sum up the values in the <tt>id</tt> column, you have to explicitely say so.</b>); (defaults to <tt>:count</tt>)
       +      # * <tt>:grouping</tt> - The period records are grouped on (<tt>:hour</tt>, <tt>:day</tt>, <tt>:week</tt>, <tt>:month</tt>); <b>Beware that <tt>reportable</tt> treats weeks as starting on monday!</b>
       +      # * <tt>:limit</tt> - The number of reporting periods to get (see <tt>:grouping</tt>), (defaults to 100)
       +      # * <tt>:conditions</tt> - Conditions like in <tt>ActiveRecord::Base#find</tt>; only records that match the conditions are reported; <b>Beware that when conditions are specified, caching is disabled!</b>
       +      # * <tt>:live_data</tt> - Specifies whether data for the current reporting period is to be read; <b>if <tt>:live_data</tt> is <tt>true</tt>, you will experience a performance hit since the request cannot be satisfied from the cache only (defaults to <tt>false</tt>)</b>
       +      # * <tt>:end_date</tt> - When specified, the report will only include data for the <tt>:limit</tt> reporting periods until this date.
       +      def initialize(klass, name, options = {})
       +        ensure_valid_options(options)
       +        @klass        = klass
       +        @name         = name
       +        @date_column  = (options[:date_column] || 'created_at').to_s
       +        @aggregation  = options[:aggregation] || :count
       +        @value_column = (options[:value_column] || (@aggregation == :count ? 'id' : name)).to_s
       +        @options = {
       +          :limit      => options[:limit] || 100,
       +          :conditions => options[:conditions] || [],
       +          :grouping   => Grouping.new(options[:grouping] || :day),
       +          :live_data  => options[:live_data] || false,
       +          :end_date   => options[:end_date] || false
       +        }
       +        @options.merge!(options)
       +        @options.freeze
       +      end
       +
       +      # Runs the report and returns an array of array of DateTimes and Floats
       +      #
       +      # ==== Options
       +      # * <tt>:grouping</tt> - The period records are grouped on (<tt>:hour</tt>, <tt>:day</tt>, <tt>:week</tt>, <tt>:month</tt>); <b>Beware that <tt>reportable</tt> treats weeks as starting on monday!</b>
       +      # * <tt>:limit</tt> - The number of reporting periods to get (see <tt>:grouping</tt>), (defaults to 100)
       +      # * <tt>:conditions</tt> - Conditions like in <tt>ActiveRecord::Base#find</tt>; only records that match the conditions are reported
       +      # * <tt>:live_data</tt> - Specifies whether data for the current reporting period is to be read; <b>if <tt>:live_data</tt> is <tt>true</tt>, you will experience a performance hit since the request cannot be satisfied from the cache only (defaults to <tt>false</tt>)</b>
       +      # * <tt>:end_date</tt> - When specified, the report will only include data for the <tt>:limit</tt> reporting periods until this date.
       +      def run(options = {})
       +        options = options_for_run(options)
       +        ReportCache.process(self, options) do |begin_at, end_at|
       +          read_data(begin_at, end_at, options)
       +        end
       +      end
       +
       +      private
       +
       +        def options_for_run(options = {})
       +          options = options.dup
       +          ensure_valid_options(options, :run)
       +          options.reverse_merge!(@options)
       +          options[:grouping] = Grouping.new(options[:grouping]) unless options[:grouping].is_a?(Grouping)
       +          return options
       +        end
       +
       +        def read_data(begin_at, end_at, options)
       +          conditions = setup_conditions(begin_at, end_at, options[:conditions])
       +          @klass.send(@aggregation,
       +            @value_column,
       +            :conditions => conditions,
       +            :group      => options[:grouping].to_sql(@date_column),
       +            :order      => "#{options[:grouping].to_sql(@date_column)} ASC",
       +            :limit      => options[:limit]
       +          )
       +        end
       +
       +        def setup_conditions(begin_at, end_at, custom_conditions = [])
       +          conditions = [@klass.send(:sanitize_sql_for_conditions, custom_conditions) || '']
       +          conditions[0] += "#{(conditions[0].blank? ? '' : ' AND ')}#{ActiveRecord::Base.connection.quote_table_name(@klass.table_name)}.#{ActiveRecord::Base.connection.quote_column_name(@date_column.to_s)} "
       +          conditions[0] += if begin_at && end_at
       +            'BETWEEN ? AND ?'
       +          elsif begin_at
       +            '>= ?'
       +          elsif end_at
       +            '<= ?'
       +          else
       +            raise ArgumentError.new('You must pass either begin_at, end_at or both to setup_conditions.')
       +          end
       +          conditions << begin_at if begin_at
       +          conditions << end_at if end_at
       +          conditions
       +        end
       +
       +        def ensure_valid_options(options, context = :initialize)
       +          case context
       +            when :initialize
       +              options.each_key do |k|
       +                raise ArgumentError.new("Invalid option #{k}!") unless [:limit, :aggregation, :grouping, :date_column, :value_column, :conditions, :live_data, :end_date].include?(k)
       +              end
       +              raise ArgumentError.new("Invalid aggregation #{options[:aggregation]}!") if options[:aggregation] && ![:count, :sum, :maximum, :minimum, :average].include?(options[:aggregation])
       +              raise ArgumentError.new('The name of the column holding the value to sum has to be specified for aggregation :sum!') if [:sum, :maximum, :minimum, :average].include?(options[:aggregation]) && !options.key?(:value_column)
       +            when :run
       +              options.each_key do |k|
       +                raise ArgumentError.new("Invalid option #{k}!") unless [:limit, :conditions, :grouping, :live_data, :end_date].include?(k)
       +              end
       +          end
       +          raise ArgumentError.new('Options :live_data and :end_date may not both be specified!') if options[:live_data] && options[:end_date]
       +          raise ArgumentError.new("Invalid grouping #{options[:grouping]}!") if options[:grouping] && ![:hour, :day, :week, :month].include?(options[:grouping])
       +          raise ArgumentError.new("Invalid conditions: #{options[:conditions].inspect}!") if options[:conditions] && !options[:conditions].is_a?(Array) && !options[:conditions].is_a?(Hash)
       +          raise ArgumentError.new("Invalid end date: #{options[:end_date].inspect}; must be a DateTime!") if options[:end_date] && !options[:end_date].is_a?(DateTime) && !options[:end_date].is_a?(Time)
       +          raise ArgumentError.new('End date may not be in the future!') if options[:end_date] && options[:end_date] > DateTime.now
       +        end
       +
       +    end
       +
       +  end
       +
       +end
 (DIR) diff --git a/lib/saulabs/reportable/report_cache.rb b/lib/saulabs/reportable/report_cache.rb
       @@ -0,0 +1,139 @@
       +module Saulabs #:nodoc:
       +
       +  module Reportable #:nodoc:
       +
       +    # The ReportCache class is a regular +ActiveRecord+ model and represents cached results for single reporting periods (table name is +reportable_cache+)
       +    # ReportCache instances are identified by the combination of +model_name+, +report_name+, +grouping+, +aggregation+ and +reporting_period+
       +    class ReportCache < ActiveRecord::Base
       +
       +      set_table_name :reportable_cache
       +
       +      self.skip_time_zone_conversion_for_attributes = [:reporting_period]
       +
       +      # Clears the cache for the specified +klass+ and +report+
       +      #
       +      # === Parameters
       +      # * <tt>klass</tt> - The model the report to clear the cache for works on
       +      # * <tt>report</tt> - The name of the report to clear the cache for
       +      #
       +      # === Example
       +      # To clear the cache for a report defined as
       +      #  class User < ActiveRecord::Base
       +      #    reportable :registrations
       +      #  end
       +      # just do
       +      #  Saulabs::Reportable::ReportCache.clear_for(User, :registrations)
       +      def self.clear_for(klass, report)
       +        self.delete_all(:conditions => {
       +          :model_name  => klass.name,
       +          :report_name => report.to_s
       +        })
       +      end
       +
       +      def self.process(report, options, &block) #:nodoc:
       +        raise ArgumentError.new('A block must be given') unless block_given?
       +        self.transaction do
       +          cached_data = read_cached_data(report, options)
       +          new_data = read_new_data(cached_data, options, &block)
       +          prepare_result(new_data, cached_data, report, options)
       +        end
       +      end
       +
       +      private
       +
       +        def self.prepare_result(new_data, cached_data, report, options)
       +          new_data = new_data.map { |data| [ReportingPeriod.from_db_string(options[:grouping], data[0]), data[1]] }
       +          cached_data.map! { |cached| [ReportingPeriod.new(options[:grouping], cached.reporting_period), cached.value] }
       +          current_reporting_period = ReportingPeriod.current(options[:grouping])
       +          reporting_period = get_first_reporting_period(options)
       +          result = []
       +          while reporting_period < (options[:end_date] ? ReportingPeriod.new(options[:grouping], options[:end_date]).next : current_reporting_period)
       +            if cached = cached_data.find { |cached| reporting_period == cached[0] }
       +              result << [cached[0].date_time, cached[1]]
       +            else
       +              new_cached = build_cached_data(report, options[:grouping], options[:conditions], reporting_period, find_value(new_data, reporting_period))
       +              new_cached.save!
       +              result << [reporting_period.date_time, new_cached.value]
       +            end
       +            reporting_period = reporting_period.next
       +          end
       +          if options[:live_data]
       +            result << [current_reporting_period.date_time, find_value(new_data, current_reporting_period)]
       +          end
       +          result
       +        end
       +
       +        def self.find_value(data, reporting_period)
       +          data = data.detect { |d| d[0] == reporting_period }
       +          data ? data[1] : 0.0
       +        end
       +
       +        def self.build_cached_data(report, grouping, condition, reporting_period, value)
       +          self.new(
       +            :model_name       => report.klass.to_s,
       +            :report_name      => report.name.to_s,
       +            :grouping         => grouping.identifier.to_s,
       +            :aggregation      => report.aggregation.to_s,
       +            :condition        => condition.to_s,
       +            :reporting_period => reporting_period.date_time,
       +            :value            => value
       +          )
       +        end
       +
       +        def self.read_cached_data(report, options)
       +          conditions = [
       +            'model_name = ? AND report_name = ? AND grouping = ? AND aggregation = ? AND `condition` = ?',
       +            report.klass.to_s,
       +            report.name.to_s,
       +            options[:grouping].identifier.to_s,
       +            report.aggregation.to_s,
       +            options[:conditions].to_s
       +          ]
       +          first_reporting_period = get_first_reporting_period(options)
       +          last_reporting_period = get_last_reporting_period(options)
       +          if last_reporting_period
       +            conditions.first << ' AND reporting_period BETWEEN ? AND ?'
       +            conditions << first_reporting_period.date_time
       +            conditions << last_reporting_period.date_time
       +          else
       +            conditions.first << ' AND reporting_period >= ?'
       +            conditions << first_reporting_period.date_time
       +          end
       +          self.all(
       +            :conditions => conditions,
       +            :limit      => options[:limit],
       +            :order      => 'reporting_period ASC'
       +          )
       +        end
       +
       +        def self.read_new_data(cached_data, options, &block)
       +          if !options[:live_data] && cached_data.length == options[:limit]
       +            []
       +          else
       +            first_reporting_period_to_read = if cached_data.length < options[:limit]
       +              get_first_reporting_period(options)
       +            else
       +              ReportingPeriod.new(options[:grouping], cached_data.last.reporting_period).next
       +            end
       +            last_reporting_period_to_read = options[:end_date] ? ReportingPeriod.new(options[:grouping], options[:end_date]).last_date_time : nil
       +            yield(first_reporting_period_to_read.date_time, last_reporting_period_to_read)
       +          end
       +        end
       +
       +        def self.get_first_reporting_period(options)
       +          if options[:end_date]
       +            ReportingPeriod.first(options[:grouping], options[:limit] - 1, options[:end_date])
       +          else
       +            ReportingPeriod.first(options[:grouping], options[:limit])
       +          end
       +        end
       +
       +        def self.get_last_reporting_period(options)
       +          return ReportingPeriod.new(options[:grouping], options[:end_date]) if options[:end_date]
       +        end
       +
       +    end
       +
       +  end
       +
       +end
 (DIR) diff --git a/lib/saulabs/reportable/reporting_period.rb b/lib/saulabs/reportable/reporting_period.rb
       @@ -0,0 +1,103 @@
       +module Saulabs #:nodoc:
       +
       +  module Reportable #:nodoc:
       +
       +    class ReportingPeriod #:nodoc:
       +
       +      attr_reader :date_time, :grouping
       +
       +      def initialize(grouping, date_time = nil)
       +        @grouping  = grouping
       +        @date_time = parse_date_time(date_time || DateTime.now)
       +      end
       +
       +      def offset(offset)
       +        self.class.new(@grouping, @date_time + offset.send(@grouping.identifier))
       +      end
       +
       +      def self.first(grouping, limit, end_date = nil)
       +        self.new(grouping, end_date).offset(-limit)
       +      end
       +
       +      def self.current(grouping)
       +        self.new(grouping, Time.now)
       +      end
       +
       +      def self.from_db_string(grouping, db_string)
       +        parts = grouping.date_parts_from_db_string(db_string)
       +        result = case grouping.identifier
       +          when :hour
       +            self.new(grouping, DateTime.new(parts[0], parts[1], parts[2], parts[3], 0, 0))
       +          when :day
       +            self.new(grouping, Date.new(parts[0], parts[1], parts[2]))
       +          when :week
       +            self.new(grouping, Date.commercial(parts[0], parts[1], 1))
       +          when :month
       +            self.new(grouping, Date.new(parts[0], parts[1], 1))
       +        end
       +        result
       +      end
       +
       +      def next
       +        self.offset(1)
       +      end
       +
       +      def previous
       +        self.offset(-1)
       +      end
       +
       +      def ==(other)
       +        if other.is_a?(Saulabs::Reportable::ReportingPeriod)
       +          @date_time.to_s == other.date_time.to_s && @grouping.identifier.to_s == other.grouping.identifier.to_s
       +        elsif other.is_a?(Time) || other.is_a?(DateTime)
       +          @date_time == parse_date_time(other)
       +        else
       +          raise ArgumentError.new("Can only compare instances of #{self.class.name}")
       +        end
       +      end
       +
       +      def <(other)
       +        if other.is_a?(Saulabs::Reportable::ReportingPeriod)
       +          return @date_time < other.date_time
       +        elsif other.is_a?(Time) || other.is_a?(DateTime)
       +          @date_time < parse_date_time(other)
       +        else
       +          raise ArgumentError.new("Can only compare instances of #{self.class.name}")
       +        end
       +      end
       +
       +      def last_date_time
       +        case @grouping.identifier
       +          when :hour
       +            DateTime.new(@date_time.year, @date_time.month, @date_time.day, @date_time.hour, 59, 59)
       +          when :day
       +            DateTime.new(@date_time.year, @date_time.month, @date_time.day, 23, 59, 59)
       +          when :week
       +            date_time = (@date_time - @date_time.wday.days) + 7.days
       +            Date.new(date_time.year, date_time.month, date_time.day)
       +          when :month
       +            Date.new(@date_time.year, @date_time.month, (Date.new(@date_time.year, 12, 31) << (12 - @date_time.month)).day)
       +        end
       +      end
       +
       +      private
       +
       +        def parse_date_time(date_time)
       +          case @grouping.identifier
       +            when :hour
       +              DateTime.new(date_time.year, date_time.month, date_time.day, date_time.hour)
       +            when :day
       +              date_time.to_date
       +            when :week
       +              date_time = (date_time - date_time.wday.days) + 1.day
       +              Date.new(date_time.year, date_time.month, date_time.day)
       +            when :month
       +              Date.new(date_time.year, date_time.month, 1)
       +          end
       +        end
       +
       +    end
       +
       +  end
       +
       +end
 (DIR) diff --git a/lib/saulabs/reportable/sparkline_tag_helper.rb b/lib/saulabs/reportable/sparkline_tag_helper.rb
       @@ -0,0 +1,47 @@
       +module Saulabs #:nodoc:
       +
       +  module Reportable #:nodoc:
       +
       +    module SparklineTagHelper
       +
       +      # Renders a sparkline with the given data.
       +      #
       +      # ==== Parameters
       +      #
       +      # * <tt>data</tt> - The data to render the sparkline for, is retrieved from a report like <tt>User.registration_report</tt>
       +      #
       +      # ==== Options
       +      #
       +      # * <tt>width</tt> - The width of the generated image
       +      # * <tt>height</tt> - The height of the generated image
       +      # * <tt>line_color</tt> - The line color of the sparkline (hex code)
       +      # * <tt>fill_color</tt> - The color to fill the area below the sparkline with (hex code)
       +      # * <tt>labels</tt> - The axes to render lables for (Array of <tt>:x</tt>, <tt>:y+</tt>, <tt>:r</tt>, <tt>:t</tt>; this is x axis, y axis, right, top)
       +      # * <tt>alt</tt> - The HTML img alt tag
       +      # * <tt>title</tt> - The HTML img title tag
       +      #
       +      # ==== Example
       +      # <tt><%= sparkline_tag(User.registrations_report, :width => 200, :height => 100, :color => '000') %></tt>
       +      def sparkline_tag(data, options = {})
       +        options.reverse_merge!({ :width => 300, :height => 34, :line_color => '0077cc', :fill_color => 'e6f2fa', :labels => [], :alt => '', :title => '' })
       +        data = data.collect { |d| d[1] }
       +        labels = ""
       +        unless options[:labels].empty?
       +          chxr = {}
       +          options[:labels].each_with_index do |l, i|
       +            chxr[l] = "#{i}," + ([:x, :t].include?(l) ? "0,#{data.length}" : "#{[data.min, 0].min},#{data.max}")
       +          end
       +          labels = "&chxt=#{options[:labels].map(&:to_s).join(',')}&chxr=#{options[:labels].collect{|l| chxr[l]}.join('|')}"
       +        end
       +        image_tag(
       +          "http://chart.apis.google.com/chart?cht=ls&chs=#{options[:width]}x#{options[:height]}&chd=t:#{data.join(',')}&chco=#{options[:line_color]}&chm=B,#{options[:fill_color]},0,0,0&chls=1,0,0&chds=#{data.min},#{data.max}#{labels}",
       +          :alt => options[:alt],
       +          :title => options[:title]
       +        )
       +      end
       +
       +    end
       +
       +  end
       +
       +end
 (DIR) diff --git a/lib/saulabs/reports_as_sparkline.rb b/lib/saulabs/reports_as_sparkline.rb
       @@ -1,54 +0,0 @@
       -module Saulabs #:nodoc:
       -
       -  module ReportsAsSparkline
       -
       -    def self.included(base) #:nodoc:
       -      base.extend ClassMethods
       -    end
       -
       -    module ClassMethods
       -
       -      # Generates a report on a model. That report can then be executed via the new method <tt><name>_report</tt> (see documentation of Saulabs::ReportsAsSparkline::Report#run).
       -      # 
       -      # ==== Parameters
       -      #
       -      # * <tt>name</tt> - The name of the report, defines the name of the generated report method (<tt><name>_report</tt>)
       -      #
       -      # ==== Options
       -      #
       -      # * <tt>:date_column</tt> - The name of the date column over that the records are aggregated (defaults to <tt>created_at</tt>)
       -      # * <tt>:value_column</tt> - The name of the column that holds the values to sum up when using aggregation <tt>:sum</tt>
       -      # * <tt>:aggregation</tt> - The aggregation to use (one of <tt>:count</tt>, <tt>:sum</tt>, <tt>:minimum</tt>, <tt>:maximum</tt> or <tt>:average</tt>); when using anything other than <tt>:count</tt>, <tt>:value_column</tt> must also be specified (<b>If you really want to e.g. sum up the values in the <tt>id</tt> column, you have to explicitely say so.</b>); (defaults to <tt>:count</tt>)
       -      # * <tt>:grouping</tt> - The period records are grouped on (<tt>:hour</tt>, <tt>:day</tt>, <tt>:week</tt>, <tt>:month</tt>); <b>Beware that <tt>reports_as_sparkline</tt> treats weeks as starting on monday!</b>
       -      # * <tt>:limit</tt> - The number of reporting periods to get (see <tt>:grouping</tt>), (defaults to 100)
       -      # * <tt>:conditions</tt> - Conditions like in <tt>ActiveRecord::Base#find</tt>; only records that match the conditions are reported; <b>Beware that when conditions are specified, caching is disabled!</b>
       -      # * <tt>:live_data</tt> - Specifies whether data for the current reporting period is to be read; <b>if <tt>:live_data</tt> is <tt>true</tt>, you will experience a performance hit since the request cannot be satisfied from the cache only (defaults to <tt>false</tt>)</b>
       -      # * <tt>:end_date</tt> - When specified, the report will only include data for the <tt>:limit</tt> reporting periods until this date.
       -      #
       -      # ==== Examples
       -      #
       -      #  class User < ActiveRecord::Base
       -      #    reports_as_sparkline :registrations, :aggregation => :count
       -      #    reports_as_sparkline :activations,   :aggregation => :count, :date_column => :activated_at
       -      #    reports_as_sparkline :total_users,   :cumulate => true
       -      #    reports_as_sparkline :rake,          :aggregation => :sum,   :value_column => :profile_visits
       -      #  end
       -      def reports_as_sparkline(name, options = {})
       -        (class << self; self; end).instance_eval do
       -          define_method "#{name.to_s}_report".to_sym do |*args|
       -            if options.delete(:cumulate)
       -              report = Saulabs::ReportsAsSparkline::CumulatedReport.new(self, name, options)
       -            else
       -              report = Saulabs::ReportsAsSparkline::Report.new(self, name, options)
       -            end
       -            raise ArgumentError.new unless args.length == 0 || (args.length == 1 && args[0].is_a?(Hash))
       -            report.run(args.length == 0 ? {} : args[0])
       -          end
       -        end
       -      end
       -
       -    end
       -
       -  end
       -
       -end
 (DIR) diff --git a/lib/saulabs/reports_as_sparkline/cumulated_report.rb b/lib/saulabs/reports_as_sparkline/cumulated_report.rb
       @@ -1,45 +0,0 @@
       -module Saulabs #:nodoc:
       -
       -  module ReportsAsSparkline #:nodoc:
       -
       -    # A special report class that cumulates all data (see Saulabs::ReportsAsSparkline::Report)
       -    #
       -    # ==== Examples
       -    #
       -    # When Saulabs::ReportsAsSparkline::Report returns
       -    #
       -    #  [[<DateTime today>, 1], [<DateTime yesterday>, 2], etc.]
       -    #
       -    # Saulabs::ReportsAsSparkline::CumulatedReport returns
       -    #
       -    #  [[<DateTime today>, 3], [<DateTime yesterday>, 2], etc.]
       -    class CumulatedReport < Report
       -
       -      # Runs the report (see Saulabs::ReportsAsSparkline::Report#run)
       -      def run(options = {})
       -        cumulate(super, options_for_run(options))
       -      end
       -
       -      protected
       -
       -        def cumulate(data, options)
       -          first_reporting_period = ReportingPeriod.first(options[:grouping], options[:limit], options[:end_date])
       -          acc = initial_cumulative_value(first_reporting_period.date_time, options)
       -          result = []
       -          data.each do |element|
       -            acc += element[1].to_f
       -            result << [element[0], acc]
       -          end
       -          result
       -        end
       -
       -        def initial_cumulative_value(date, options)
       -          conditions = setup_conditions(nil, date, options[:conditions])
       -          @klass.send(@aggregation, @value_column, :conditions => conditions)
       -        end
       -
       -    end
       -
       -  end
       -
       -end
 (DIR) diff --git a/lib/saulabs/reports_as_sparkline/grouping.rb b/lib/saulabs/reports_as_sparkline/grouping.rb
       @@ -1,115 +0,0 @@
       -module Saulabs #:nodoc:
       -
       -  module ReportsAsSparkline #:nodoc:
       -
       -    class Grouping #:nodoc:
       -
       -      def initialize(identifier)
       -        raise ArgumentError.new("Invalid grouping #{identifier}") unless [:hour, :day, :week, :month].include?(identifier)
       -        @identifier = identifier
       -      end
       -
       -      def identifier
       -        @identifier
       -      end
       -
       -      def date_parts_from_db_string(db_string)
       -        case ActiveRecord::Base.connection.adapter_name
       -          when /mysql/i
       -            from_mysql_db_string(db_string)
       -          when /sqlite/i
       -            from_sqlite_db_string(db_string)
       -          when /postgres/i
       -            from_postgresql_db_string(db_string)
       -        end
       -      end
       -
       -      def to_sql(date_column) #:nodoc:
       -        case ActiveRecord::Base.connection.adapter_name
       -          when /mysql/i
       -            mysql_format(date_column)
       -          when /sqlite/i
       -            sqlite_format(date_column)
       -          when /postgres/i
       -            postgresql_format(date_column)
       -        end
       -      end
       -
       -      private
       -
       -        def from_mysql_db_string(db_string)
       -          if @identifier == :week
       -            parts = [db_string[0..3], db_string[4..5]].map(&:to_i)
       -          else
       -            db_string.split('/').map(&:to_i)
       -          end
       -        end
       -
       -        def from_sqlite_db_string(db_string)
       -          if @identifier == :week
       -            parts = db_string.split('-').map(&:to_i)
       -            date = Date.new(parts[0], parts[1], parts[2])
       -            return [date.cwyear, date.cweek]
       -          end
       -          db_string.split('/').map(&:to_i)
       -        end
       -
       -        def from_postgresql_db_string(db_string)
       -          case @identifier
       -            when :hour
       -              return (db_string[0..9].split('-') + [db_string[11..12]]).map(&:to_i)
       -            when :day
       -              return db_string[0..9].split('-').map(&:to_i)
       -            when :week
       -              parts = db_string[0..9].split('-').map(&:to_i)
       -              date = Date.new(parts[0], parts[1], parts[2])
       -              return [date.cwyear, date.cweek]
       -            when :month
       -              return db_string[0..6].split('-')[0..1].map(&:to_i)
       -          end
       -        end
       -
       -        def mysql_format(date_column)
       -          case @identifier
       -            when :hour
       -              "DATE_FORMAT(#{date_column}, '%Y/%m/%d/%H')"
       -            when :day
       -              "DATE_FORMAT(#{date_column}, '%Y/%m/%d')"
       -            when :week
       -              "YEARWEEK(#{date_column}, 3)"
       -            when :month
       -              "DATE_FORMAT(#{date_column}, '%Y/%m')"
       -          end
       -        end
       -
       -        def sqlite_format(date_column)
       -          case @identifier
       -            when :hour
       -              "strftime('%Y/%m/%d/%H', #{date_column})"
       -            when :day
       -              "strftime('%Y/%m/%d', #{date_column})"
       -            when :week
       -              "date(#{date_column}, 'weekday 0')"
       -            when :month
       -              "strftime('%Y/%m', #{date_column})"
       -          end
       -        end
       -
       -        def postgresql_format(date_column)
       -          case @identifier
       -            when :hour
       -              "date_trunc('hour', #{date_column})"
       -            when :day
       -              "date_trunc('day', #{date_column})"
       -            when :week
       -              "date_trunc('week', #{date_column})"
       -            when :month
       -              "date_trunc('month', #{date_column})"
       -          end
       -        end
       -
       -    end
       -
       -  end
       -
       -end
 (DIR) diff --git a/lib/saulabs/reports_as_sparkline/report.rb b/lib/saulabs/reports_as_sparkline/report.rb
       @@ -1,119 +0,0 @@
       -module Saulabs #:nodoc:
       -
       -  module ReportsAsSparkline #:nodoc:
       -
       -    # The Report class that does all the data retrieval and calculations
       -    class Report
       -
       -      attr_reader :klass, :name, :date_column, :value_column, :aggregation, :options
       -
       -      # ==== Parameters
       -      # * <tt>klass</tt> - The model the report works on (This is the class you invoke Saulabs::ReportsAsSparkline::ClassMethods#reports_as_sparkline on)
       -      # * <tt>name</tt> - The name of the report (as in Saulabs::ReportsAsSparkline::ClassMethods#reports_as_sparkline)
       -      #
       -      # ==== Options
       -      #
       -      # * <tt>:date_column</tt> - The name of the date column over that the records are aggregated (defaults to <tt>created_at</tt>)
       -      # * <tt>:value_column</tt> - The name of the column that holds the values to sum up when using aggregation <tt>:sum</tt>
       -      # * <tt>:aggregation</tt> - The aggregation to use (one of <tt>:count</tt>, <tt>:sum</tt>, <tt>:minimum</tt>, <tt>:maximum</tt> or <tt>:average</tt>); when using anything other than <tt>:count</tt>, <tt>:value_column</tt> must also be specified (<b>If you really want to e.g. sum up the values in the <tt>id</tt> column, you have to explicitely say so.</b>); (defaults to <tt>:count</tt>)
       -      # * <tt>:grouping</tt> - The period records are grouped on (<tt>:hour</tt>, <tt>:day</tt>, <tt>:week</tt>, <tt>:month</tt>); <b>Beware that <tt>reports_as_sparkline</tt> treats weeks as starting on monday!</b>
       -      # * <tt>:limit</tt> - The number of reporting periods to get (see <tt>:grouping</tt>), (defaults to 100)
       -      # * <tt>:conditions</tt> - Conditions like in <tt>ActiveRecord::Base#find</tt>; only records that match the conditions are reported; <b>Beware that when conditions are specified, caching is disabled!</b>
       -      # * <tt>:live_data</tt> - Specifies whether data for the current reporting period is to be read; <b>if <tt>:live_data</tt> is <tt>true</tt>, you will experience a performance hit since the request cannot be satisfied from the cache only (defaults to <tt>false</tt>)</b>
       -      # * <tt>:end_date</tt> - When specified, the report will only include data for the <tt>:limit</tt> reporting periods until this date.
       -      def initialize(klass, name, options = {})
       -        ensure_valid_options(options)
       -        @klass        = klass
       -        @name         = name
       -        @date_column  = (options[:date_column] || 'created_at').to_s
       -        @aggregation  = options[:aggregation] || :count
       -        @value_column = (options[:value_column] || (@aggregation == :count ? 'id' : name)).to_s
       -        @options = {
       -          :limit      => options[:limit] || 100,
       -          :conditions => options[:conditions] || [],
       -          :grouping   => Grouping.new(options[:grouping] || :day),
       -          :live_data  => options[:live_data] || false,
       -          :end_date   => options[:end_date] || false
       -        }
       -        @options.merge!(options)
       -        @options.freeze
       -      end
       -
       -      # Runs the report and returns an array of array of DateTimes and Floats
       -      #
       -      # ==== Options
       -      # * <tt>:grouping</tt> - The period records are grouped on (<tt>:hour</tt>, <tt>:day</tt>, <tt>:week</tt>, <tt>:month</tt>); <b>Beware that <tt>reports_as_sparkline</tt> treats weeks as starting on monday!</b>
       -      # * <tt>:limit</tt> - The number of reporting periods to get (see <tt>:grouping</tt>), (defaults to 100)
       -      # * <tt>:conditions</tt> - Conditions like in <tt>ActiveRecord::Base#find</tt>; only records that match the conditions are reported
       -      # * <tt>:live_data</tt> - Specifies whether data for the current reporting period is to be read; <b>if <tt>:live_data</tt> is <tt>true</tt>, you will experience a performance hit since the request cannot be satisfied from the cache only (defaults to <tt>false</tt>)</b>
       -      # * <tt>:end_date</tt> - When specified, the report will only include data for the <tt>:limit</tt> reporting periods until this date.
       -      def run(options = {})
       -        options = options_for_run(options)
       -        ReportCache.process(self, options) do |begin_at, end_at|
       -          read_data(begin_at, end_at, options)
       -        end
       -      end
       -
       -      private
       -
       -        def options_for_run(options = {})
       -          options = options.dup
       -          ensure_valid_options(options, :run)
       -          options.reverse_merge!(@options)
       -          options[:grouping] = Grouping.new(options[:grouping]) unless options[:grouping].is_a?(Grouping)
       -          return options
       -        end
       -
       -        def read_data(begin_at, end_at, options)
       -          conditions = setup_conditions(begin_at, end_at, options[:conditions])
       -          @klass.send(@aggregation,
       -            @value_column,
       -            :conditions => conditions,
       -            :group      => options[:grouping].to_sql(@date_column),
       -            :order      => "#{options[:grouping].to_sql(@date_column)} ASC",
       -            :limit      => options[:limit]
       -          )
       -        end
       -
       -        def setup_conditions(begin_at, end_at, custom_conditions = [])
       -          conditions = [@klass.send(:sanitize_sql_for_conditions, custom_conditions) || '']
       -          conditions[0] += "#{(conditions[0].blank? ? '' : ' AND ')}#{ActiveRecord::Base.connection.quote_table_name(@klass.table_name)}.#{ActiveRecord::Base.connection.quote_column_name(@date_column.to_s)} "
       -          conditions[0] += if begin_at && end_at
       -            'BETWEEN ? AND ?'
       -          elsif begin_at
       -            '>= ?'
       -          elsif end_at
       -            '<= ?'
       -          else
       -            raise ArgumentError.new('You must pass either begin_at, end_at or both to setup_conditions.')
       -          end
       -          conditions << begin_at if begin_at
       -          conditions << end_at if end_at
       -          conditions
       -        end
       -
       -        def ensure_valid_options(options, context = :initialize)
       -          case context
       -            when :initialize
       -              options.each_key do |k|
       -                raise ArgumentError.new("Invalid option #{k}!") unless [:limit, :aggregation, :grouping, :date_column, :value_column, :conditions, :live_data, :end_date].include?(k)
       -              end
       -              raise ArgumentError.new("Invalid aggregation #{options[:aggregation]}!") if options[:aggregation] && ![:count, :sum, :maximum, :minimum, :average].include?(options[:aggregation])
       -              raise ArgumentError.new('The name of the column holding the value to sum has to be specified for aggregation :sum!') if [:sum, :maximum, :minimum, :average].include?(options[:aggregation]) && !options.key?(:value_column)
       -            when :run
       -              options.each_key do |k|
       -                raise ArgumentError.new("Invalid option #{k}!") unless [:limit, :conditions, :grouping, :live_data, :end_date].include?(k)
       -              end
       -          end
       -          raise ArgumentError.new('Options :live_data and :end_date may not both be specified!') if options[:live_data] && options[:end_date]
       -          raise ArgumentError.new("Invalid grouping #{options[:grouping]}!") if options[:grouping] && ![:hour, :day, :week, :month].include?(options[:grouping])
       -          raise ArgumentError.new("Invalid conditions: #{options[:conditions].inspect}!") if options[:conditions] && !options[:conditions].is_a?(Array) && !options[:conditions].is_a?(Hash)
       -          raise ArgumentError.new("Invalid end date: #{options[:end_date].inspect}; must be a DateTime!") if options[:end_date] && !options[:end_date].is_a?(DateTime) && !options[:end_date].is_a?(Time)
       -          raise ArgumentError.new('End date may not be in the future!') if options[:end_date] && options[:end_date] > DateTime.now
       -        end
       -
       -    end
       -
       -  end
       -
       -end
 (DIR) diff --git a/lib/saulabs/reports_as_sparkline/report_cache.rb b/lib/saulabs/reports_as_sparkline/report_cache.rb
       @@ -1,139 +0,0 @@
       -module Saulabs #:nodoc:
       -
       -  module ReportsAsSparkline #:nodoc:
       -
       -    # The ReportCache class is a regular +ActiveRecord+ model and represents cached results for single reporting periods (table name is +reports_as_sparkline_cache+)
       -    # ReportCache instances are identified by the combination of +model_name+, +report_name+, +grouping+, +aggregation+ and +reporting_period+
       -    class ReportCache < ActiveRecord::Base
       -
       -      set_table_name :reports_as_sparkline_cache
       -
       -      self.skip_time_zone_conversion_for_attributes = [:reporting_period]
       -
       -      # Clears the cache for the specified +klass+ and +report+
       -      #
       -      # === Parameters
       -      # * <tt>klass</tt> - The model the report to clear the cache for works on
       -      # * <tt>report</tt> - The name of the report to clear the cache for
       -      #
       -      # === Example
       -      # To clear the cache for a report defined as
       -      #  class User < ActiveRecord::Base
       -      #    reports_as_sparkline :registrations
       -      #  end
       -      # just do
       -      #  Saulabs::ReportsAsSparkline::ReportCache.clear_for(User, :registrations)
       -      def self.clear_for(klass, report)
       -        self.delete_all(:conditions => {
       -          :model_name  => klass.name,
       -          :report_name => report.to_s
       -        })
       -      end
       -
       -      def self.process(report, options, &block) #:nodoc:
       -        raise ArgumentError.new('A block must be given') unless block_given?
       -        self.transaction do
       -          cached_data = read_cached_data(report, options)
       -          new_data = read_new_data(cached_data, options, &block)
       -          prepare_result(new_data, cached_data, report, options)
       -        end
       -      end
       -
       -      private
       -
       -        def self.prepare_result(new_data, cached_data, report, options)
       -          new_data = new_data.map { |data| [ReportingPeriod.from_db_string(options[:grouping], data[0]), data[1]] }
       -          cached_data.map! { |cached| [ReportingPeriod.new(options[:grouping], cached.reporting_period), cached.value] }
       -          current_reporting_period = ReportingPeriod.current(options[:grouping])
       -          reporting_period = get_first_reporting_period(options)
       -          result = []
       -          while reporting_period < (options[:end_date] ? ReportingPeriod.new(options[:grouping], options[:end_date]).next : current_reporting_period)
       -            if cached = cached_data.find { |cached| reporting_period == cached[0] }
       -              result << [cached[0].date_time, cached[1]]
       -            else
       -              new_cached = build_cached_data(report, options[:grouping], options[:conditions], reporting_period, find_value(new_data, reporting_period))
       -              new_cached.save!
       -              result << [reporting_period.date_time, new_cached.value]
       -            end
       -            reporting_period = reporting_period.next
       -          end
       -          if options[:live_data]
       -            result << [current_reporting_period.date_time, find_value(new_data, current_reporting_period)]
       -          end
       -          result
       -        end
       -
       -        def self.find_value(data, reporting_period)
       -          data = data.detect { |d| d[0] == reporting_period }
       -          data ? data[1] : 0.0
       -        end
       -
       -        def self.build_cached_data(report, grouping, condition, reporting_period, value)
       -          self.new(
       -            :model_name       => report.klass.to_s,
       -            :report_name      => report.name.to_s,
       -            :grouping         => grouping.identifier.to_s,
       -            :aggregation      => report.aggregation.to_s,
       -            :condition        => condition.to_s,
       -            :reporting_period => reporting_period.date_time,
       -            :value            => value
       -          )
       -        end
       -
       -        def self.read_cached_data(report, options)
       -          conditions = [
       -            'model_name = ? AND report_name = ? AND grouping = ? AND aggregation = ? AND `condition` = ?',
       -            report.klass.to_s,
       -            report.name.to_s,
       -            options[:grouping].identifier.to_s,
       -            report.aggregation.to_s,
       -            options[:conditions].to_s
       -          ]
       -          first_reporting_period = get_first_reporting_period(options)
       -          last_reporting_period = get_last_reporting_period(options)
       -          if last_reporting_period
       -            conditions.first << ' AND reporting_period BETWEEN ? AND ?'
       -            conditions << first_reporting_period.date_time
       -            conditions << last_reporting_period.date_time
       -          else
       -            conditions.first << ' AND reporting_period >= ?'
       -            conditions << first_reporting_period.date_time
       -          end
       -          self.all(
       -            :conditions => conditions,
       -            :limit      => options[:limit],
       -            :order      => 'reporting_period ASC'
       -          )
       -        end
       -
       -        def self.read_new_data(cached_data, options, &block)
       -          if !options[:live_data] && cached_data.length == options[:limit]
       -            []
       -          else
       -            first_reporting_period_to_read = if cached_data.length < options[:limit]
       -              get_first_reporting_period(options)
       -            else
       -              ReportingPeriod.new(options[:grouping], cached_data.last.reporting_period).next
       -            end
       -            last_reporting_period_to_read = options[:end_date] ? ReportingPeriod.new(options[:grouping], options[:end_date]).last_date_time : nil
       -            yield(first_reporting_period_to_read.date_time, last_reporting_period_to_read)
       -          end
       -        end
       -
       -        def self.get_first_reporting_period(options)
       -          if options[:end_date]
       -            ReportingPeriod.first(options[:grouping], options[:limit] - 1, options[:end_date])
       -          else
       -            ReportingPeriod.first(options[:grouping], options[:limit])
       -          end
       -        end
       -
       -        def self.get_last_reporting_period(options)
       -          return ReportingPeriod.new(options[:grouping], options[:end_date]) if options[:end_date]
       -        end
       -
       -    end
       -
       -  end
       -
       -end
 (DIR) diff --git a/lib/saulabs/reports_as_sparkline/reporting_period.rb b/lib/saulabs/reports_as_sparkline/reporting_period.rb
       @@ -1,103 +0,0 @@
       -module Saulabs #:nodoc:
       -
       -  module ReportsAsSparkline #:nodoc:
       -
       -    class ReportingPeriod #:nodoc:
       -
       -      attr_reader :date_time, :grouping
       -
       -      def initialize(grouping, date_time = nil)
       -        @grouping  = grouping
       -        @date_time = parse_date_time(date_time || DateTime.now)
       -      end
       -
       -      def offset(offset)
       -        self.class.new(@grouping, @date_time + offset.send(@grouping.identifier))
       -      end
       -
       -      def self.first(grouping, limit, end_date = nil)
       -        self.new(grouping, end_date).offset(-limit)
       -      end
       -
       -      def self.current(grouping)
       -        self.new(grouping, Time.now)
       -      end
       -
       -      def self.from_db_string(grouping, db_string)
       -        parts = grouping.date_parts_from_db_string(db_string)
       -        result = case grouping.identifier
       -          when :hour
       -            self.new(grouping, DateTime.new(parts[0], parts[1], parts[2], parts[3], 0, 0))
       -          when :day
       -            self.new(grouping, Date.new(parts[0], parts[1], parts[2]))
       -          when :week
       -            self.new(grouping, Date.commercial(parts[0], parts[1], 1))
       -          when :month
       -            self.new(grouping, Date.new(parts[0], parts[1], 1))
       -        end
       -        result
       -      end
       -
       -      def next
       -        self.offset(1)
       -      end
       -
       -      def previous
       -        self.offset(-1)
       -      end
       -
       -      def ==(other)
       -        if other.is_a?(Saulabs::ReportsAsSparkline::ReportingPeriod)
       -          @date_time.to_s == other.date_time.to_s && @grouping.identifier.to_s == other.grouping.identifier.to_s
       -        elsif other.is_a?(Time) || other.is_a?(DateTime)
       -          @date_time == parse_date_time(other)
       -        else
       -          raise ArgumentError.new("Can only compare instances of #{self.class.name}")
       -        end
       -      end
       -
       -      def <(other)
       -        if other.is_a?(Saulabs::ReportsAsSparkline::ReportingPeriod)
       -          return @date_time < other.date_time
       -        elsif other.is_a?(Time) || other.is_a?(DateTime)
       -          @date_time < parse_date_time(other)
       -        else
       -          raise ArgumentError.new("Can only compare instances of #{self.class.name}")
       -        end
       -      end
       -
       -      def last_date_time
       -        case @grouping.identifier
       -          when :hour
       -            DateTime.new(@date_time.year, @date_time.month, @date_time.day, @date_time.hour, 59, 59)
       -          when :day
       -            DateTime.new(@date_time.year, @date_time.month, @date_time.day, 23, 59, 59)
       -          when :week
       -            date_time = (@date_time - @date_time.wday.days) + 7.days
       -            Date.new(date_time.year, date_time.month, date_time.day)
       -          when :month
       -            Date.new(@date_time.year, @date_time.month, (Date.new(@date_time.year, 12, 31) << (12 - @date_time.month)).day)
       -        end
       -      end
       -
       -      private
       -
       -        def parse_date_time(date_time)
       -          case @grouping.identifier
       -            when :hour
       -              DateTime.new(date_time.year, date_time.month, date_time.day, date_time.hour)
       -            when :day
       -              date_time.to_date
       -            when :week
       -              date_time = (date_time - date_time.wday.days) + 1.day
       -              Date.new(date_time.year, date_time.month, date_time.day)
       -            when :month
       -              Date.new(date_time.year, date_time.month, 1)
       -          end
       -        end
       -
       -    end
       -
       -  end
       -
       -end
 (DIR) diff --git a/lib/saulabs/reports_as_sparkline/sparkline_tag_helper.rb b/lib/saulabs/reports_as_sparkline/sparkline_tag_helper.rb
       @@ -1,47 +0,0 @@
       -module Saulabs #:nodoc:
       -
       -  module ReportsAsSparkline #:nodoc:
       -
       -    module SparklineTagHelper
       -
       -      # Renders a sparkline with the given data.
       -      #
       -      # ==== Parameters
       -      #
       -      # * <tt>data</tt> - The data to render the sparkline for, is retrieved from a report like <tt>User.registration_report</tt>
       -      #
       -      # ==== Options
       -      #
       -      # * <tt>width</tt> - The width of the generated image
       -      # * <tt>height</tt> - The height of the generated image
       -      # * <tt>line_color</tt> - The line color of the sparkline (hex code)
       -      # * <tt>fill_color</tt> - The color to fill the area below the sparkline with (hex code)
       -      # * <tt>labels</tt> - The axes to render lables for (Array of <tt>:x</tt>, <tt>:y+</tt>, <tt>:r</tt>, <tt>:t</tt>; this is x axis, y axis, right, top)
       -      # * <tt>alt</tt> - The HTML img alt tag
       -      # * <tt>title</tt> - The HTML img title tag
       -      #
       -      # ==== Example
       -      # <tt><%= sparkline_tag(User.registrations_report, :width => 200, :height => 100, :color => '000') %></tt>
       -      def sparkline_tag(data, options = {})
       -        options.reverse_merge!({ :width => 300, :height => 34, :line_color => '0077cc', :fill_color => 'e6f2fa', :labels => [], :alt => '', :title => '' })
       -        data = data.collect { |d| d[1] }
       -        labels = ""
       -        unless options[:labels].empty?
       -          chxr = {}
       -          options[:labels].each_with_index do |l, i|
       -            chxr[l] = "#{i}," + ([:x, :t].include?(l) ? "0,#{data.length}" : "#{[data.min, 0].min},#{data.max}")
       -          end
       -          labels = "&chxt=#{options[:labels].map(&:to_s).join(',')}&chxr=#{options[:labels].collect{|l| chxr[l]}.join('|')}"
       -        end
       -        image_tag(
       -          "http://chart.apis.google.com/chart?cht=ls&chs=#{options[:width]}x#{options[:height]}&chd=t:#{data.join(',')}&chco=#{options[:line_color]}&chm=B,#{options[:fill_color]},0,0,0&chls=1,0,0&chds=#{data.min},#{data.max}#{labels}",
       -          :alt => options[:alt],
       -          :title => options[:title]
       -        )
       -      end
       -
       -    end
       -
       -  end
       -
       -end
 (DIR) diff --git a/spec/classes/cumulated_report_spec.rb b/spec/classes/cumulated_report_spec.rb
       @@ -1,9 +1,9 @@
        require File.join(File.dirname(__FILE__), '..', 'spec_helper')
        
       -describe Saulabs::ReportsAsSparkline::CumulatedReport do
       +describe Saulabs::Reportable::CumulatedReport do
        
          before do
       -    @report = Saulabs::ReportsAsSparkline::CumulatedReport.new(User, :cumulated_registrations)
       +    @report = Saulabs::Reportable::CumulatedReport.new(User, :cumulated_registrations)
          end
        
          describe '#run' do
       @@ -15,13 +15,13 @@ describe Saulabs::ReportsAsSparkline::CumulatedReport do
            end
        
            it 'should return an array of the same length as the specified limit when :live_data is false' do
       -      @report = Saulabs::ReportsAsSparkline::CumulatedReport.new(User, :cumulated_registrations, :limit => 10, :live_data => false)
       +      @report = Saulabs::Reportable::CumulatedReport.new(User, :cumulated_registrations, :limit => 10, :live_data => false)
        
              @report.run.length.should == 10
            end
        
            it 'should return an array of the same length as the specified limit + 1 when :live_data is true' do
       -      @report = Saulabs::ReportsAsSparkline::CumulatedReport.new(User, :cumulated_registrations, :limit => 10, :live_data => true)
       +      @report = Saulabs::Reportable::CumulatedReport.new(User, :cumulated_registrations, :limit => 10, :live_data => true)
        
              @report.run.length.should == 11
            end
       @@ -45,8 +45,8 @@ describe Saulabs::ReportsAsSparkline::CumulatedReport do
                    describe 'the returned result' do
        
                      before do
       -                @grouping = Saulabs::ReportsAsSparkline::Grouping.new(grouping)
       -                @report = Saulabs::ReportsAsSparkline::CumulatedReport.new(User, :cumulated_registrations,
       +                @grouping = Saulabs::Reportable::Grouping.new(grouping)
       +                @report = Saulabs::Reportable::CumulatedReport.new(User, :cumulated_registrations,
                          :grouping  => grouping,
                          :limit     => 10,
                          :live_data => live_data
       @@ -55,23 +55,23 @@ describe Saulabs::ReportsAsSparkline::CumulatedReport do
                      end
        
                      it "should be an array starting reporting period (Time.now - limit.#{grouping.to_s})" do
       -                @result.first[0].should == Saulabs::ReportsAsSparkline::ReportingPeriod.new(@grouping, Time.now - 10.send(grouping)).date_time
       +                @result.first[0].should == Saulabs::Reportable::ReportingPeriod.new(@grouping, Time.now - 10.send(grouping)).date_time
                      end
        
                      if live_data
                        it "should be data ending with the current reporting period" do
       -                  @result.last[0].should == Saulabs::ReportsAsSparkline::ReportingPeriod.new(@grouping).date_time
       +                  @result.last[0].should == Saulabs::Reportable::ReportingPeriod.new(@grouping).date_time
                        end
                      else
                        it "should be data ending with the reporting period before the current" do
       -                  @result.last[0].should == Saulabs::ReportsAsSparkline::ReportingPeriod.new(@grouping).previous.date_time
       +                  @result.last[0].should == Saulabs::Reportable::ReportingPeriod.new(@grouping).previous.date_time
                        end
                      end
        
                    end
        
                    it 'should return correct data for aggregation :count' do
       -              @report = Saulabs::ReportsAsSparkline::CumulatedReport.new(User, :registrations,
       +              @report = Saulabs::Reportable::CumulatedReport.new(User, :registrations,
                        :aggregation => :count,
                        :grouping    => grouping,
                        :limit       => 10,
       @@ -87,7 +87,7 @@ describe Saulabs::ReportsAsSparkline::CumulatedReport do
                    end
        
                    it 'should return correct data for aggregation :sum' do
       -              @report = Saulabs::ReportsAsSparkline::CumulatedReport.new(User, :registrations,
       +              @report = Saulabs::Reportable::CumulatedReport.new(User, :registrations,
                        :aggregation  => :sum,
                        :grouping     => grouping,
                        :value_column => :profile_visits,
       @@ -104,7 +104,7 @@ describe Saulabs::ReportsAsSparkline::CumulatedReport do
                    end
        
                    it 'should return correct data for aggregation :count when custom conditions are specified' do
       -              @report = Saulabs::ReportsAsSparkline::CumulatedReport.new(User, :registrations,
       +              @report = Saulabs::Reportable::CumulatedReport.new(User, :registrations,
                        :aggregation => :count,
                        :grouping    => grouping,
                        :limit       => 10,
       @@ -120,7 +120,7 @@ describe Saulabs::ReportsAsSparkline::CumulatedReport do
                    end
        
                    it 'should return correct data for aggregation :sum when custom conditions are specified' do
       -              @report = Saulabs::ReportsAsSparkline::CumulatedReport.new(User, :registrations,
       +              @report = Saulabs::Reportable::CumulatedReport.new(User, :registrations,
                        :aggregation  => :sum,
                        :grouping     => grouping,
                        :value_column => :profile_visits,
       @@ -149,7 +149,7 @@ describe Saulabs::ReportsAsSparkline::CumulatedReport do
            end
        
            after(:each) do
       -      Saulabs::ReportsAsSparkline::ReportCache.destroy_all
       +      Saulabs::Reportable::ReportCache.destroy_all
            end
        
          end
 (DIR) diff --git a/spec/classes/grouping_spec.rb b/spec/classes/grouping_spec.rb
       @@ -1,11 +1,11 @@
        require File.join(File.dirname(__FILE__), '..', 'spec_helper')
        
       -describe Saulabs::ReportsAsSparkline::Grouping do
       +describe Saulabs::Reportable::Grouping do
        
          describe '#new' do
        
            it 'should raise an error if an unsupported grouping is specified' do
       -      lambda { Saulabs::ReportsAsSparkline::Grouping.new(:unsupported) }.should raise_error(ArgumentError)
       +      lambda { Saulabs::Reportable::Grouping.new(:unsupported) }.should raise_error(ArgumentError)
            end
        
          end
       @@ -19,19 +19,19 @@ describe Saulabs::ReportsAsSparkline::Grouping do
              end
        
              it 'should use DATE_FORMAT with format string "%Y/%m/%d/%H" for grouping :hour' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:hour).send(:to_sql, 'created_at').should == "DATE_FORMAT(created_at, '%Y/%m/%d/%H')"
       +        Saulabs::Reportable::Grouping.new(:hour).send(:to_sql, 'created_at').should == "DATE_FORMAT(created_at, '%Y/%m/%d/%H')"
              end
        
              it 'should use DATE_FORMAT with format string "%Y/%m/%d" for grouping :day' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:day).send(:to_sql, 'created_at').should == "DATE_FORMAT(created_at, '%Y/%m/%d')"
       +        Saulabs::Reportable::Grouping.new(:day).send(:to_sql, 'created_at').should == "DATE_FORMAT(created_at, '%Y/%m/%d')"
              end
        
              it 'should use YEARWEEK with mode 3 for grouping :week' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:week).send(:to_sql, 'created_at').should == "YEARWEEK(created_at, 3)"
       +        Saulabs::Reportable::Grouping.new(:week).send(:to_sql, 'created_at').should == "YEARWEEK(created_at, 3)"
              end
        
              it 'should use DATE_FORMAT with format string "%Y/%m" for grouping :month' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:month).send(:to_sql, 'created_at').should == "DATE_FORMAT(created_at, '%Y/%m')"
       +        Saulabs::Reportable::Grouping.new(:month).send(:to_sql, 'created_at').should == "DATE_FORMAT(created_at, '%Y/%m')"
              end
        
            end
       @@ -45,7 +45,7 @@ describe Saulabs::ReportsAsSparkline::Grouping do
              for grouping in [:hour, :day, :week, :month] do
        
                it "should use date_trunc with truncation identifier \"#{grouping.to_s}\" for grouping :#{grouping.to_s}" do
       -          Saulabs::ReportsAsSparkline::Grouping.new(grouping).send(:to_sql, 'created_at').should == "date_trunc('#{grouping.to_s}', created_at)"
       +          Saulabs::Reportable::Grouping.new(grouping).send(:to_sql, 'created_at').should == "date_trunc('#{grouping.to_s}', created_at)"
                end
        
              end
       @@ -59,19 +59,19 @@ describe Saulabs::ReportsAsSparkline::Grouping do
              end
        
              it 'should use strftime with format string "%Y/%m/%d/%H" for grouping :hour' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:hour).send(:to_sql, 'created_at').should == "strftime('%Y/%m/%d/%H', created_at)"
       +        Saulabs::Reportable::Grouping.new(:hour).send(:to_sql, 'created_at').should == "strftime('%Y/%m/%d/%H', created_at)"
              end
        
              it 'should use strftime with format string "%Y/%m/%d" for grouping :day' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:day).send(:to_sql, 'created_at').should == "strftime('%Y/%m/%d', created_at)"
       +        Saulabs::Reportable::Grouping.new(:day).send(:to_sql, 'created_at').should == "strftime('%Y/%m/%d', created_at)"
              end
        
              it 'should use date with mode "weekday 0" for grouping :week' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:week).send(:to_sql, 'created_at').should == "date(created_at, 'weekday 0')"
       +        Saulabs::Reportable::Grouping.new(:week).send(:to_sql, 'created_at').should == "date(created_at, 'weekday 0')"
              end
        
              it 'should use strftime with format string "%Y/%m" for grouping :month' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:month).send(:to_sql, 'created_at').should == "strftime('%Y/%m', created_at)"
       +        Saulabs::Reportable::Grouping.new(:month).send(:to_sql, 'created_at').should == "strftime('%Y/%m', created_at)"
              end
        
            end
       @@ -89,7 +89,7 @@ describe Saulabs::ReportsAsSparkline::Grouping do
              for grouping in [[:hour, '2008/12/31/12'], [:day, '2008/12/31'], [:month, '2008/12']] do
        
                it "should split the string with '/' for grouping :#{grouping[0].to_s}" do
       -          Saulabs::ReportsAsSparkline::Grouping.new(grouping[0]).date_parts_from_db_string(grouping[1]).should == grouping[1].split('/').map(&:to_i)
       +          Saulabs::Reportable::Grouping.new(grouping[0]).date_parts_from_db_string(grouping[1]).should == grouping[1].split('/').map(&:to_i)
                end
        
              end
       @@ -98,7 +98,7 @@ describe Saulabs::ReportsAsSparkline::Grouping do
                db_string = '2008-2-1'
                expected = [2008, 5]
        
       -        Saulabs::ReportsAsSparkline::Grouping.new(:week).date_parts_from_db_string(db_string).should == expected
       +        Saulabs::Reportable::Grouping.new(:week).date_parts_from_db_string(db_string).should == expected
              end
        
            end
       @@ -110,19 +110,19 @@ describe Saulabs::ReportsAsSparkline::Grouping do
              end
        
              it 'should split the date part of the string with "-" and read out the hour for grouping :hour' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:hour).date_parts_from_db_string('2008-12-03 06:00:00').should == [2008, 12, 03, 6]
       +        Saulabs::Reportable::Grouping.new(:hour).date_parts_from_db_string('2008-12-03 06:00:00').should == [2008, 12, 03, 6]
              end
        
              it 'should split the date part of the string with "-" for grouping :day' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:day).date_parts_from_db_string('2008-12-03 00:00:00').should == [2008, 12, 03]
       +        Saulabs::Reportable::Grouping.new(:day).date_parts_from_db_string('2008-12-03 00:00:00').should == [2008, 12, 03]
              end
        
              it 'should split the date part of the string with "-" and calculate the calendar week for grouping :week' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:week).date_parts_from_db_string('2008-12-01 00:00:00').should == [2008, 49]
       +        Saulabs::Reportable::Grouping.new(:week).date_parts_from_db_string('2008-12-01 00:00:00').should == [2008, 49]
              end
        
              it 'should split the date part of the string with "-" and return year and month for grouping :month' do
       -        Saulabs::ReportsAsSparkline::Grouping.new(:month).date_parts_from_db_string('2008-12-01 00:00:00').should == [2008, 12]
       +        Saulabs::Reportable::Grouping.new(:month).date_parts_from_db_string('2008-12-01 00:00:00').should == [2008, 12]
              end
        
            end
       @@ -136,7 +136,7 @@ describe Saulabs::ReportsAsSparkline::Grouping do
              for grouping in [[:hour, '2008/12/31/12'], [:day, '2008/12/31'], [:month, '2008/12']] do
        
                it "should split the string with '/' for grouping :#{grouping[0].to_s}" do
       -          Saulabs::ReportsAsSparkline::Grouping.new(grouping[0]).date_parts_from_db_string(grouping[1]).should == grouping[1].split('/').map(&:to_i)
       +          Saulabs::Reportable::Grouping.new(grouping[0]).date_parts_from_db_string(grouping[1]).should == grouping[1].split('/').map(&:to_i)
                end
        
              end
       @@ -145,7 +145,7 @@ describe Saulabs::ReportsAsSparkline::Grouping do
                db_string = '200852'
                expected = [2008, 52]
        
       -        Saulabs::ReportsAsSparkline::Grouping.new(:week).date_parts_from_db_string(db_string).should == expected
       +        Saulabs::Reportable::Grouping.new(:week).date_parts_from_db_string(db_string).should == expected
              end
        
            end
 (DIR) diff --git a/spec/classes/report_cache_spec.rb b/spec/classes/report_cache_spec.rb
       @@ -1,20 +1,20 @@
        require File.join(File.dirname(__FILE__), '..', 'spec_helper')
        
       -describe Saulabs::ReportsAsSparkline::ReportCache do
       +describe Saulabs::Reportable::ReportCache do
        
          before do
       -    @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations, :limit => 10)
       +    @report = Saulabs::Reportable::Report.new(User, :registrations, :limit => 10)
          end
        
          describe '.clear_for' do
        
            it 'should delete all entries in the cache for the klass and report name' do
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:delete_all).once.with(:conditions => {
       +      Saulabs::Reportable::ReportCache.should_receive(:delete_all).once.with(:conditions => {
                :model_name  => User.name,
                :report_name => 'registrations'
              })
        
       -      Saulabs::ReportsAsSparkline::ReportCache.clear_for(User, :registrations)
       +      Saulabs::Reportable::ReportCache.clear_for(User, :registrations)
            end
        
          end
       @@ -22,20 +22,20 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
          describe '.process' do
        
            before do
       -      Saulabs::ReportsAsSparkline::ReportCache.stub!(:find).and_return([])
       -      Saulabs::ReportsAsSparkline::ReportCache.stub!(:prepare_result).and_return([])
       +      Saulabs::Reportable::ReportCache.stub!(:find).and_return([])
       +      Saulabs::Reportable::ReportCache.stub!(:prepare_result).and_return([])
            end
        
            it 'should raise an ArgumentError if no block is given' do
              lambda do
       -        Saulabs::ReportsAsSparkline::ReportCache.process(@report, @report.options)
       +        Saulabs::Reportable::ReportCache.process(@report, @report.options)
              end.should raise_error(ArgumentError)
            end
        
            it 'sould start a transaction' do
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:transaction)
       +      Saulabs::Reportable::ReportCache.should_receive(:transaction)
        
       -      Saulabs::ReportsAsSparkline::ReportCache.process(@report, @report.options) {}
       +      Saulabs::Reportable::ReportCache.process(@report, @report.options) {}
            end
        
            describe 'with :live_data = true' do
       @@ -46,34 +46,34 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
        
              it 'should yield to the given block' do
                lambda {
       -          Saulabs::ReportsAsSparkline::ReportCache.process(@report, @options) { raise YieldMatchException.new }
       +          Saulabs::Reportable::ReportCache.process(@report, @options) { raise YieldMatchException.new }
                }.should raise_error(YieldMatchException)
              end
        
              it 'should yield the first reporting period if not all required data could be retrieved from the cache' do
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(
                  @report.options[:grouping],
                  Time.now - 3.send(@report.options[:grouping].identifier)
                )
       -        Saulabs::ReportsAsSparkline::ReportCache.stub!(:all).and_return([Saulabs::ReportsAsSparkline::ReportCache.new])
       +        Saulabs::Reportable::ReportCache.stub!(:all).and_return([Saulabs::Reportable::ReportCache.new])
        
       -        Saulabs::ReportsAsSparkline::ReportCache.process(@report, @options) do |begin_at, end_at|
       -          begin_at.should == Saulabs::ReportsAsSparkline::ReportingPeriod.first(@report.options[:grouping], @report.options[:limit]).date_time
       +        Saulabs::Reportable::ReportCache.process(@report, @options) do |begin_at, end_at|
       +          begin_at.should == Saulabs::Reportable::ReportingPeriod.first(@report.options[:grouping], @report.options[:limit]).date_time
                  end_at.should   == nil
                  []
                end
              end
        
              it 'should yield the reporting period after the last one in the cache if all required data could be retrieved from the cache' do
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(
                  @report.options[:grouping],
                  Time.now - @report.options[:limit].send(@report.options[:grouping].identifier)
                )
       -        cached = Saulabs::ReportsAsSparkline::ReportCache.new
       +        cached = Saulabs::Reportable::ReportCache.new
                cached.stub!(:reporting_period).and_return(reporting_period.date_time)
       -        Saulabs::ReportsAsSparkline::ReportCache.stub!(:all).and_return(Array.new(@report.options[:limit] - 1, Saulabs::ReportsAsSparkline::ReportCache.new), cached)
       +        Saulabs::Reportable::ReportCache.stub!(:all).and_return(Array.new(@report.options[:limit] - 1, Saulabs::Reportable::ReportCache.new), cached)
        
       -        Saulabs::ReportsAsSparkline::ReportCache.process(@report, @options) do |begin_at, end_at|
       +        Saulabs::Reportable::ReportCache.process(@report, @options) do |begin_at, end_at|
                  begin_at.should == reporting_period.date_time
                  end_at.should   == nil
                  []
       @@ -85,18 +85,18 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
            describe 'with :live_data = false' do
        
              it 'should not yield if all required data could be retrieved from the cache' do
       -        Saulabs::ReportsAsSparkline::ReportCache.stub!(:all).and_return(Array.new(@report.options[:limit], Saulabs::ReportsAsSparkline::ReportCache.new))
       +        Saulabs::Reportable::ReportCache.stub!(:all).and_return(Array.new(@report.options[:limit], Saulabs::Reportable::ReportCache.new))
        
                lambda {
       -          Saulabs::ReportsAsSparkline::ReportCache.process(@report, @report.options) { raise YieldMatchException.new }
       +          Saulabs::Reportable::ReportCache.process(@report, @report.options) { raise YieldMatchException.new }
                }.should_not raise_error(YieldMatchException)
              end
        
              it 'should yield to the block if no data could be retrieved from the cache' do
       -        Saulabs::ReportsAsSparkline::ReportCache.stub!(:all).and_return([])
       +        Saulabs::Reportable::ReportCache.stub!(:all).and_return([])
        
                lambda {
       -          Saulabs::ReportsAsSparkline::ReportCache.process(@report, @report.options) { raise YieldMatchException.new }
       +          Saulabs::Reportable::ReportCache.process(@report, @report.options) { raise YieldMatchException.new }
                }.should raise_error(YieldMatchException)
              end
        
       @@ -107,9 +107,9 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
                end
        
                it 'should yield the last date and time of the reporting period for the specified end date' do
       -          reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(@report.options[:grouping], @options[:end_date])
       +          reporting_period = Saulabs::Reportable::ReportingPeriod.new(@report.options[:grouping], @options[:end_date])
        
       -          Saulabs::ReportsAsSparkline::ReportCache.process(@report, @options) do |begin_at, end_at|
       +          Saulabs::Reportable::ReportCache.process(@report, @options) do |begin_at, end_at|
                    end_at.should   == reporting_period.last_date_time
                    []
                  end
       @@ -120,7 +120,7 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
            end
        
            it 'should read existing data from the cache' do
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:all).once.with(
       +      Saulabs::Reportable::ReportCache.should_receive(:all).once.with(
                :conditions => [
                  'model_name = ? AND report_name = ? AND grouping = ? AND aggregation = ? AND `condition` = ? AND reporting_period >= ?',
                  @report.klass.to_s,
       @@ -128,18 +128,18 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
                  @report.options[:grouping].identifier.to_s,
                  @report.aggregation.to_s,
                  @report.options[:conditions].to_s,
       -          Saulabs::ReportsAsSparkline::ReportingPeriod.first(@report.options[:grouping], 10).date_time
       +          Saulabs::Reportable::ReportingPeriod.first(@report.options[:grouping], 10).date_time
                ],
                :limit => 10,
                :order => 'reporting_period ASC'
              ).and_return([])
        
       -      Saulabs::ReportsAsSparkline::ReportCache.process(@report, @report.options) { [] }
       +      Saulabs::Reportable::ReportCache.process(@report, @report.options) { [] }
            end
        
            it 'should utilize the end_date in the conditions' do
              end_date = Time.now
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:all).once.with(
       +      Saulabs::Reportable::ReportCache.should_receive(:all).once.with(
                :conditions => [
                  'model_name = ? AND report_name = ? AND grouping = ? AND aggregation = ? AND `condition` = ? AND reporting_period BETWEEN ? AND ?',
                  @report.klass.to_s,
       @@ -147,19 +147,19 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
                  @report.options[:grouping].identifier.to_s,
                  @report.aggregation.to_s,
                  @report.options[:conditions].to_s,
       -          Saulabs::ReportsAsSparkline::ReportingPeriod.first(@report.options[:grouping], 9).date_time,
       -          Saulabs::ReportsAsSparkline::ReportingPeriod.new(@report.options[:grouping], end_date).date_time
       +          Saulabs::Reportable::ReportingPeriod.first(@report.options[:grouping], 9).date_time,
       +          Saulabs::Reportable::ReportingPeriod.new(@report.options[:grouping], end_date).date_time
                ],
                :limit => 10,
                :order => 'reporting_period ASC'
              ).and_return([])
        
       -      Saulabs::ReportsAsSparkline::ReportCache.process(@report, @report.options.merge(:end_date => end_date)) { [] }
       +      Saulabs::Reportable::ReportCache.process(@report, @report.options.merge(:end_date => end_date)) { [] }
            end
        
            it "should read existing data from the cache for the correct grouping if one other than the report's default grouping is specified" do
       -      grouping = Saulabs::ReportsAsSparkline::Grouping.new(:month)
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:find).once.with(
       +      grouping = Saulabs::Reportable::Grouping.new(:month)
       +      Saulabs::Reportable::ReportCache.should_receive(:find).once.with(
                :all,
                :conditions => [
                  'model_name = ? AND report_name = ? AND grouping = ? AND aggregation = ? AND `condition` = ? AND reporting_period >= ?',
       @@ -168,18 +168,18 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
                  grouping.identifier.to_s,
                  @report.aggregation.to_s,
                  @report.options[:conditions].to_s,
       -          Saulabs::ReportsAsSparkline::ReportingPeriod.first(grouping, 10).date_time
       +          Saulabs::Reportable::ReportingPeriod.first(grouping, 10).date_time
                ],
                :limit => 10,
                :order => 'reporting_period ASC'
              ).and_return([])
        
       -      Saulabs::ReportsAsSparkline::ReportCache.process(@report, { :limit => 10, :grouping => grouping }) { [] }
       +      Saulabs::Reportable::ReportCache.process(@report, { :limit => 10, :grouping => grouping }) { [] }
            end
        
            it 'should yield the first reporting period if the cache is empty' do
       -      Saulabs::ReportsAsSparkline::ReportCache.process(@report, @report.options) do |begin_at, end_at|
       -        begin_at.should == Saulabs::ReportsAsSparkline::ReportingPeriod.first(@report.options[:grouping], 10).date_time
       +      Saulabs::Reportable::ReportCache.process(@report, @report.options) do |begin_at, end_at|
       +        begin_at.should == Saulabs::Reportable::ReportingPeriod.first(@report.options[:grouping], 10).date_time
                end_at.should == nil
                []
              end
       @@ -189,16 +189,16 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
          describe '.prepare_result' do
        
            before do
       -      @current_reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(@report.options[:grouping])
       +      @current_reporting_period = Saulabs::Reportable::ReportingPeriod.new(@report.options[:grouping])
              @new_data = [[@current_reporting_period.previous.date_time, 1.0]]
       -      Saulabs::ReportsAsSparkline::ReportingPeriod.stub!(:from_db_string).and_return(@current_reporting_period.previous)
       -      @cached = Saulabs::ReportsAsSparkline::ReportCache.new
       +      Saulabs::Reportable::ReportingPeriod.stub!(:from_db_string).and_return(@current_reporting_period.previous)
       +      @cached = Saulabs::Reportable::ReportCache.new
              @cached.stub!(:save!)
       -      Saulabs::ReportsAsSparkline::ReportCache.stub!(:build_cached_data).and_return(@cached)
       +      Saulabs::Reportable::ReportCache.stub!(:build_cached_data).and_return(@cached)
            end
        
       -    it 'should create :limit instances of Saulabs::ReportsAsSparkline::ReportCache with value 0.0 if no new data has been read and nothing was cached' do
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:build_cached_data).exactly(10).times.with(
       +    it 'should create :limit instances of Saulabs::Reportable::ReportCache with value 0.0 if no new data has been read and nothing was cached' do
       +      Saulabs::Reportable::ReportCache.should_receive(:build_cached_data).exactly(10).times.with(
                @report,
                @report.options[:grouping],
                @report.options[:conditions],
       @@ -206,18 +206,18 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
                0.0
              ).and_return(@cached)
        
       -      Saulabs::ReportsAsSparkline::ReportCache.send(:prepare_result, [], [], @report, @report.options)
       +      Saulabs::Reportable::ReportCache.send(:prepare_result, [], [], @report, @report.options)
            end
        
       -    it 'should create a new Saulabs::ReportsAsSparkline::ReportCache with the correct value if new data has been read' do
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:build_cached_data).exactly(9).times.with(
       +    it 'should create a new Saulabs::Reportable::ReportCache with the correct value if new data has been read' do
       +      Saulabs::Reportable::ReportCache.should_receive(:build_cached_data).exactly(9).times.with(
                @report,
                @report.options[:grouping],
                @report.options[:conditions],
                anything(),
                0.0
              ).and_return(@cached)
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:build_cached_data).once.with(
       +      Saulabs::Reportable::ReportCache.should_receive(:build_cached_data).once.with(
                @report,
                @report.options[:grouping],
                @report.options[:conditions],
       @@ -225,17 +225,17 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
                1.0
              ).and_return(@cached)
        
       -      Saulabs::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, [], @report, @report.options)
       +      Saulabs::Reportable::ReportCache.send(:prepare_result, @new_data, [], @report, @report.options)
            end
        
       -    it 'should save the created Saulabs::ReportsAsSparkline::ReportCache' do
       +    it 'should save the created Saulabs::Reportable::ReportCache' do
              @cached.should_receive(:save!).once
        
       -      Saulabs::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, [], @report, @report.options)
       +      Saulabs::Reportable::ReportCache.send(:prepare_result, @new_data, [], @report, @report.options)
            end
        
            it 'should return an array of arrays of Dates and Floats' do
       -      result = Saulabs::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, [], @report, @report.options)
       +      result = Saulabs::Reportable::ReportCache.send(:prepare_result, @new_data, [], @report, @report.options)
        
              result.should be_kind_of(Array)
              result[0].should be_kind_of(Array)
       @@ -246,7 +246,7 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
            describe 'with :live_data = false' do
        
              before do
       -        @result = Saulabs::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, [], @report, @report.options)
       +        @result = Saulabs::Reportable::ReportCache.send(:prepare_result, @new_data, [], @report, @report.options)
              end
        
              it 'should return an array of length :limit' do
       @@ -263,7 +263,7 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
        
              before do
                options = @report.options.merge(:live_data => true)
       -        @result = Saulabs::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, [], @report, options)
       +        @result = Saulabs::Reportable::ReportCache.send(:prepare_result, @new_data, [], @report, options)
              end
        
              it 'should return an array of length (:limit + 1)' do
       @@ -280,15 +280,15 @@ describe Saulabs::ReportsAsSparkline::ReportCache do
          describe '.find_value' do
        
            before do
       -      @data = [[Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:day)), 3.0]]
       +      @data = [[Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:day)), 3.0]]
            end
        
            it 'should return the correct value when new data has been read for the reporting period' do
       -      Saulabs::ReportsAsSparkline::ReportCache.send(:find_value, @data, @data[0][0]).should == 3.0
       +      Saulabs::Reportable::ReportCache.send(:find_value, @data, @data[0][0]).should == 3.0
            end
        
            it 'should return 0.0 when no data has been read for the reporting period' do
       -      Saulabs::ReportsAsSparkline::ReportCache.send(:find_value, @data, @data[0][0].next).should == 0.0
       +      Saulabs::Reportable::ReportCache.send(:find_value, @data, @data[0][0].next).should == 0.0
            end
        
          end
 (DIR) diff --git a/spec/classes/report_spec.rb b/spec/classes/report_spec.rb
       @@ -1,9 +1,9 @@
        require File.join(File.dirname(__FILE__), '..', 'spec_helper')
        
       -describe Saulabs::ReportsAsSparkline::Report do
       +describe Saulabs::Reportable::Report do
        
          before do
       -    @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations)
       +    @report = Saulabs::Reportable::Report.new(User, :registrations)
            @now    = Time.now
            DateTime.stub!(:now).and_return(@now)
          end
       @@ -19,7 +19,7 @@ describe Saulabs::ReportsAsSparkline::Report do
          describe '#run' do
        
            it 'should process the data with the report cache' do
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:process).once.with(
       +      Saulabs::Reportable::ReportCache.should_receive(:process).once.with(
                @report,
                { :limit => 100, :grouping => @report.options[:grouping], :conditions => [], :live_data => false, :end_date => false }
              )
       @@ -28,7 +28,7 @@ describe Saulabs::ReportsAsSparkline::Report do
            end
        
            it 'should process the data with the report cache when custom conditions are given' do
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:process).once.with(
       +      Saulabs::Reportable::ReportCache.should_receive(:process).once.with(
                @report,
                { :limit => 100, :grouping => @report.options[:grouping], :conditions => { :some => :condition }, :live_data => false, :end_date => false }
              )
       @@ -43,9 +43,9 @@ describe Saulabs::ReportsAsSparkline::Report do
            end
        
            it 'should use a custom grouping if one is specified' do
       -      grouping = Saulabs::ReportsAsSparkline::Grouping.new(:month)
       -      Saulabs::ReportsAsSparkline::Grouping.should_receive(:new).once.with(:month).and_return(grouping)
       -      Saulabs::ReportsAsSparkline::ReportCache.should_receive(:process).once.with(
       +      grouping = Saulabs::Reportable::Grouping.new(:month)
       +      Saulabs::Reportable::Grouping.should_receive(:new).once.with(:month).and_return(grouping)
       +      Saulabs::Reportable::ReportCache.should_receive(:process).once.with(
                @report,
                { :limit => 100, :grouping => grouping, :conditions => [], :live_data => false, :end_date => false }
              )
       @@ -54,13 +54,13 @@ describe Saulabs::ReportsAsSparkline::Report do
            end
        
            it 'should return an array of the same length as the specified limit when :live_data is false' do
       -      @report = Saulabs::ReportsAsSparkline::Report.new(User, :cumulated_registrations, :limit => 10, :live_data => false)
       +      @report = Saulabs::Reportable::Report.new(User, :cumulated_registrations, :limit => 10, :live_data => false)
        
              @report.run.length.should == 10
            end
        
            it 'should return an array of the same length as the specified limit + 1 when :live_data is true' do
       -      @report = Saulabs::ReportsAsSparkline::Report.new(User, :cumulated_registrations, :limit => 10, :live_data => true)
       +      @report = Saulabs::Reportable::Report.new(User, :cumulated_registrations, :limit => 10, :live_data => true)
        
              @report.run.length.should == 11
            end
       @@ -79,7 +79,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                describe 'when :end_date is specified' do
        
                  it 'should not raise a SQL duplicate key error after multiple runs' do
       -            @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +            @report = Saulabs::Reportable::Report.new(User, :registrations,
                      :limit    => 2,
                      :grouping => grouping,
                      :end_date => Date.yesterday.to_datetime
       @@ -92,8 +92,8 @@ describe Saulabs::ReportsAsSparkline::Report do
        
                    before do
                      @end_date = DateTime.now - 1.send(grouping)
       -              @grouping = Saulabs::ReportsAsSparkline::Grouping.new(grouping)
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @grouping = Saulabs::Reportable::Grouping.new(grouping)
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :grouping => grouping,
                        :limit    => 10,
                        :end_date => @end_date
       @@ -102,11 +102,11 @@ describe Saulabs::ReportsAsSparkline::Report do
                    end
        
                    it "should start with the reporting period (end_date - limit.#{grouping.to_s})" do
       -              @result.first[0].should == Saulabs::ReportsAsSparkline::ReportingPeriod.new(@grouping, @end_date - 9.send(grouping)).date_time
       +              @result.first[0].should == Saulabs::Reportable::ReportingPeriod.new(@grouping, @end_date - 9.send(grouping)).date_time
                    end
        
                    it "should end with the reporting period of the specified end date" do
       -              @result.last[0].should == Saulabs::ReportsAsSparkline::ReportingPeriod.new(@grouping, @end_date).date_time
       +              @result.last[0].should == Saulabs::Reportable::ReportingPeriod.new(@grouping, @end_date).date_time
                    end
        
                  end
       @@ -120,9 +120,9 @@ describe Saulabs::ReportsAsSparkline::Report do
                    describe 'the returned result' do
        
                      before do
       -                Saulabs::ReportsAsSparkline::ReportCache.delete_all
       -                @grouping = Saulabs::ReportsAsSparkline::Grouping.new(grouping)
       -                @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +                Saulabs::Reportable::ReportCache.delete_all
       +                @grouping = Saulabs::Reportable::Grouping.new(grouping)
       +                @report = Saulabs::Reportable::Report.new(User, :registrations,
                          :grouping  => grouping,
                          :limit     => 10,
                          :live_data => live_data
       @@ -131,23 +131,23 @@ describe Saulabs::ReportsAsSparkline::Report do
                      end
        
                      it "should be an array starting reporting period (Time.now - limit.#{grouping.to_s})" do
       -                @result.first[0].should == Saulabs::ReportsAsSparkline::ReportingPeriod.new(@grouping, Time.now - 10.send(grouping)).date_time
       +                @result.first[0].should == Saulabs::Reportable::ReportingPeriod.new(@grouping, Time.now - 10.send(grouping)).date_time
                      end
        
                      if live_data
                        it "should be data ending with the current reporting period" do
       -                  @result.last[0].should == Saulabs::ReportsAsSparkline::ReportingPeriod.new(@grouping).date_time
       +                  @result.last[0].should == Saulabs::Reportable::ReportingPeriod.new(@grouping).date_time
                        end
                      else
                        it "should be data ending with the reporting period before the current" do
       -                  @result.last[0].should == Saulabs::ReportsAsSparkline::ReportingPeriod.new(@grouping).previous.date_time
       +                  @result.last[0].should == Saulabs::Reportable::ReportingPeriod.new(@grouping).previous.date_time
                        end
                      end
        
                    end
        
                    it 'should return correct data for aggregation :count' do
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :aggregation => :count,
                        :grouping    => grouping,
                        :limit       => 10,
       @@ -163,7 +163,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                    end
        
                    it 'should return correct data for aggregation :sum' do
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :aggregation  => :sum,
                        :grouping     => grouping,
                        :value_column => :profile_visits,
       @@ -180,7 +180,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                    end
        
                    it 'should return correct data for aggregation :maximum' do
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :aggregation  => :maximum,
                        :grouping     => grouping,
                        :value_column => :profile_visits,
       @@ -197,7 +197,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                    end
        
                    it 'should return correct data for aggregation :minimum' do
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :aggregation  => :minimum,
                        :grouping     => grouping,
                        :value_column => :profile_visits,
       @@ -214,7 +214,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                    end
        
                    it 'should return correct data for aggregation :average' do
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :aggregation  => :average,
                        :grouping     => grouping,
                        :value_column => :profile_visits,
       @@ -231,7 +231,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                    end
        
                    it 'should return correct data for aggregation :count when custom conditions are specified' do
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :aggregation => :count,
                        :grouping    => grouping,
                        :limit       => 10,
       @@ -247,7 +247,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                    end
        
                    it 'should return correct data for aggregation :sum when custom conditions are specified' do
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :aggregation  => :sum,
                        :grouping     => grouping,
                        :value_column => :profile_visits,
       @@ -264,7 +264,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                    end
        
                    it 'should return correct results when run twice in a row with a higher limit on the second run' do
       -              @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +              @report = Saulabs::Reportable::Report.new(User, :registrations,
                        :aggregation => :count,
                        :grouping    => grouping,
                        :limit       => 10,
       @@ -288,7 +288,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                    unless live_data
        
                      it 'should return correct data for aggregation :count when :end_date is specified' do
       -                @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +                @report = Saulabs::Reportable::Report.new(User, :registrations,
                          :aggregation => :count,
                          :grouping    => grouping,
                          :limit       => 10,
       @@ -303,7 +303,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                      end
        
                      it 'should return correct data for aggregation :sum when :end_date is specified' do
       -                @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +                @report = Saulabs::Reportable::Report.new(User, :registrations,
                          :aggregation  => :sum,
                          :grouping     => grouping,
                          :value_column => :profile_visits,
       @@ -319,7 +319,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                      end
        
                      it 'should return correct results when run twice in a row with an end date further in the past on the second run' do
       -                @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +                @report = Saulabs::Reportable::Report.new(User, :registrations,
                          :aggregation => :count,
                          :grouping    => grouping,
                          :limit       => 10,
       @@ -367,7 +367,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                end
        
                it 'should return correct data for aggregation :count' do
       -          @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +          @report = Saulabs::Reportable::Report.new(User, :registrations,
                    :aggregation => :count,
                    :grouping    => :week,
                    :limit       => 10
       @@ -396,7 +396,7 @@ describe Saulabs::ReportsAsSparkline::Report do
                end
        
                it 'should return correct data for aggregation :count' do
       -          @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations,
       +          @report = Saulabs::Reportable::Report.new(User, :registrations,
                    :aggregation => :count,
                    :grouping    => :week,
                    :limit       => 10
       @@ -415,7 +415,7 @@ describe Saulabs::ReportsAsSparkline::Report do
            end
        
            after do
       -      Saulabs::ReportsAsSparkline::ReportCache.destroy_all
       +      Saulabs::Reportable::ReportCache.destroy_all
            end
        
            after(:all) do
       @@ -427,7 +427,7 @@ describe Saulabs::ReportsAsSparkline::Report do
          describe '#read_data' do
        
            it 'should invoke the aggregation method on the model' do
       -      @report = Saulabs::ReportsAsSparkline::Report.new(User, :registrations, :aggregation => :count)
       +      @report = Saulabs::Reportable::Report.new(User, :registrations, :aggregation => :count)
              User.should_receive(:count).once.and_return([])
        
              @report.send(:read_data, Time.now, 5.days.from_now, { :grouping => @report.options[:grouping], :conditions => [] })
 (DIR) diff --git a/spec/classes/reporting_period_spec.rb b/spec/classes/reporting_period_spec.rb
       @@ -1,19 +1,19 @@
        require File.join(File.dirname(__FILE__), '..', 'spec_helper')
        
       -describe Saulabs::ReportsAsSparkline::ReportingPeriod do
       +describe Saulabs::Reportable::ReportingPeriod do
        
          describe '#date_time' do
        
            it 'should return the date and time with minutes = seconds = 0 for grouping :hour' do
              date_time = DateTime.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:hour), date_time)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:hour), date_time)
        
              reporting_period.date_time.should == DateTime.new(date_time.year, date_time.month, date_time.day, date_time.hour, 0, 0)
            end
        
            it 'should return the date part only for grouping :day' do
              date_time = DateTime.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:day), date_time)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:day), date_time)
        
              reporting_period.date_time.should == date_time.to_date
            end
       @@ -22,28 +22,28 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
              it 'should return the date of the monday of the week date_time is in for any day in that week' do
                date_time = DateTime.new(2008, 11, 27)
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), date_time)
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), date_time)
        
                reporting_period.date_time.should == Date.new(date_time.year, date_time.month, 24)
              end
        
              it 'should return the date of the monday of the week date_time is in when the specified date is a monday already' do
                date_time = DateTime.new(2008, 11, 24)
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), date_time)
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), date_time)
        
                reporting_period.date_time.should == Date.new(date_time.year, date_time.month, 24)
              end
        
              it 'should return the date of the monday of the week date_time is in when the monday is in a different month than the specified date' do
                date_time = DateTime.new(2008, 11, 1)
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), date_time)
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), date_time)
        
                reporting_period.date_time.should == Date.new(2008, 10, 27)
              end
        
              it 'should return the date of the monday of the week date_time is in when the monday is in a different year than the specified date' do
                date_time = DateTime.new(2009, 1, 1)
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), date_time)
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), date_time)
        
                reporting_period.date_time.should == Date.new(2008, 12, 29)
              end
       @@ -52,7 +52,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return the date with day = 1 for grouping :month' do
              date_time = Time.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), date_time)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), date_time)
        
              reporting_period.date_time.should == Date.new(date_time.year, date_time.month, 1)
            end
       @@ -63,14 +63,14 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return the date and time with minutes = seconds = 59 for grouping :hour' do
              date_time = DateTime.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:hour), date_time)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:hour), date_time)
        
              reporting_period.last_date_time.should == DateTime.new(date_time.year, date_time.month, date_time.day, date_time.hour, 59, 59)
            end
        
            it 'should return the date part with hour = 23 and minute = seconds = 59 for grouping :day' do
              date_time = DateTime.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:day), date_time)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:day), date_time)
        
              reporting_period.last_date_time.should == DateTime.new(date_time.year, date_time.month, date_time.day, 23, 59, 59)
            end
       @@ -79,21 +79,21 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
              it 'should return the date of the sunday of the week date_time is in for any day in that week' do
                date_time = DateTime.new(2008, 11, 27)
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), date_time)
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), date_time)
        
                reporting_period.last_date_time.should == Date.new(date_time.year, date_time.month, 30)
              end
        
              it 'should return the date of the sunday of the week date_time is in when the sunday is in a different month than the specified date' do
                date_time = DateTime.new(2008, 10, 30)
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), date_time)
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), date_time)
        
                reporting_period.last_date_time.should == Date.new(2008, 11, 2)
              end
        
              it 'should return the date of the sunday of the week date_time is in when the sunday is in a different year than the specified date' do
                date_time = DateTime.new(2008, 12, 29)
       -        reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), date_time)
       +        reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), date_time)
        
                reporting_period.last_date_time.should == Date.new(2009, 1, 4)
              end
       @@ -102,7 +102,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return the date of the last day of the month for grouping :month' do
              date_time = DateTime.new(2009, 4, 29)
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), date_time)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), date_time)
        
              reporting_period.last_date_time.should == Date.new(date_time.year, date_time.month, 30)
            end
       @@ -112,31 +112,31 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
          describe '.from_db_string' do
        
            it 'should return a reporting period with the correct date and time and with minutes = seconds = 0 for grouping :hour' do
       -      grouping = Saulabs::ReportsAsSparkline::Grouping.new(:hour)
       +      grouping = Saulabs::Reportable::Grouping.new(:hour)
              grouping.stub!(:date_parts_from_db_string).and_return([2008, 1, 1, 12])
        
       -      Saulabs::ReportsAsSparkline::ReportingPeriod.from_db_string(grouping, '').date_time.should == DateTime.new(2008, 1, 1, 12, 0, 0)
       +      Saulabs::Reportable::ReportingPeriod.from_db_string(grouping, '').date_time.should == DateTime.new(2008, 1, 1, 12, 0, 0)
            end
        
            it 'should return a reporting period with the date part only for grouping :day' do
       -      grouping = Saulabs::ReportsAsSparkline::Grouping.new(:day)
       +      grouping = Saulabs::Reportable::Grouping.new(:day)
              grouping.stub!(:date_parts_from_db_string).and_return([2008, 1, 1])
        
       -      Saulabs::ReportsAsSparkline::ReportingPeriod.from_db_string(grouping, '').date_time.should == Date.new(2008, 1, 1)
       +      Saulabs::Reportable::ReportingPeriod.from_db_string(grouping, '').date_time.should == Date.new(2008, 1, 1)
            end
        
            it 'should return a reporting period with the date part of the monday of the week the date is in for grouping :week' do
       -      grouping = Saulabs::ReportsAsSparkline::Grouping.new(:week)
       +      grouping = Saulabs::Reportable::Grouping.new(:week)
              grouping.stub!(:date_parts_from_db_string).and_return([2008, 1])
        
       -      Saulabs::ReportsAsSparkline::ReportingPeriod.from_db_string(grouping, '').date_time.should == Date.new(2007, 12, 31)
       +      Saulabs::Reportable::ReportingPeriod.from_db_string(grouping, '').date_time.should == Date.new(2007, 12, 31)
            end
        
            it 'should return a reporting period with the correct date and with day = 1 for grouping :month' do
       -      grouping = Saulabs::ReportsAsSparkline::Grouping.new(:month)
       +      grouping = Saulabs::Reportable::Grouping.new(:month)
              grouping.stub!(:date_parts_from_db_string).and_return([2008, 1])
        
       -      Saulabs::ReportsAsSparkline::ReportingPeriod.from_db_string(grouping, '').date_time.should == Date.new(2008, 1, 1)
       +      Saulabs::Reportable::ReportingPeriod.from_db_string(grouping, '').date_time.should == Date.new(2008, 1, 1)
            end
        
          end
       @@ -145,7 +145,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with date and time one hour after the current period for grouping :hour' do
              now = Time.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:hour), now)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:hour), now)
              expected = now + 1.hour
        
              reporting_period.next.date_time.should == DateTime.new(expected.year, expected.month, expected.day, expected.hour)
       @@ -153,7 +153,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with date one day after the current period for grouping :day' do
              now = Time.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:day), now)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:day), now)
              expected = now + 1.day
        
              reporting_period.next.date_time.should == Date.new(expected.year, expected.month, expected.day)
       @@ -161,7 +161,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with date one week after the current period for grouping :week' do
              now = DateTime.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), now)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), now)
              expected = reporting_period.date_time + 1.week
        
              reporting_period.next.date_time.should == Date.new(expected.year, expected.month, expected.day)
       @@ -169,7 +169,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with date of the first day in the month one month after the current period' do
              now = Time.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), now)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), now)
              expected = reporting_period.date_time + 1.month
        
              reporting_period.next.date_time.should == Date.new(expected.year, expected.month, 1)
       @@ -181,7 +181,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with date and time one hour before the current period for grouping :hour' do
              now = Time.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:hour), now)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:hour), now)
              expected = now - 1.hour
        
              reporting_period.previous.date_time.should == DateTime.new(expected.year, expected.month, expected.day, expected.hour)
       @@ -189,7 +189,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with date one day before the current period for grouping :day' do
              now = Time.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:day), now)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:day), now)
              expected = now - 1.day
        
              reporting_period.previous.date_time.should == Date.new(expected.year, expected.month, expected.day)
       @@ -197,7 +197,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with date one week before the current period for grouping :week' do
              now = DateTime.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), now)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), now)
              expected = reporting_period.date_time - 1.week
        
              reporting_period.previous.date_time.should == Date.new(expected.year, expected.month, expected.day)
       @@ -205,7 +205,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with date of the first day in the month one month before the current period' do
              now = Time.now
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), now)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), now)
              expected = reporting_period.date_time - 1.month
        
              reporting_period.previous.date_time.should == Date.new(expected.year, expected.month, 1)
       @@ -217,30 +217,30 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return true for 2 reporting periods with the same date_time and grouping' do
              now = DateTime.now
       -      reporting_period1 = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), now)
       -      reporting_period2 = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), now)
       +      reporting_period1 = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), now)
       +      reporting_period2 = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), now)
        
              (reporting_period1 == reporting_period2).should == true
            end
        
            it 'should return false for 2 reporting periods with the same date_time but different groupings' do
              now = Time.now
       -      reporting_period1 = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), now)
       -      reporting_period2 = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:day), now)
       +      reporting_period1 = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), now)
       +      reporting_period2 = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:day), now)
        
              (reporting_period1 == reporting_period2).should == false
            end
        
            it 'should return true for 2 reporting periods with the same grouping but different date_times if the date times evaluate to the same reporting period identifier' do
       -      reporting_period1 = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), Time.now)
       -      reporting_period2 = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), Time.now + 1.day)
       +      reporting_period1 = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), Time.now)
       +      reporting_period2 = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), Time.now + 1.day)
        
              (reporting_period1 == reporting_period2).should == true
            end
        
            it 'should return false for 2 reporting periods with the same grouping but different date_times if the date times evaluate to different reporting period identifiers' do
       -      reporting_period1 = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), Time.now)
       -      reporting_period2 = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), Time.now + 2.months)
       +      reporting_period1 = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), Time.now)
       +      reporting_period2 = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), Time.now + 2.months)
        
              (reporting_period1 == reporting_period2).should == false
            end
       @@ -251,7 +251,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
                it 'should return true when the date and hour are equal' do
                  date_time = DateTime.new(2008, 10, 30, 12)
       -          reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:hour), date_time)
       +          reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:hour), date_time)
        
                  reporting_period.should == date_time
                end
       @@ -262,7 +262,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
                it 'should return true when the date is equal' do
                  date_time = DateTime.new(2008, 10, 30)
       -          reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:day), date_time)
       +          reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:day), date_time)
        
                  reporting_period.should == date_time
                end
       @@ -273,7 +273,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
                it 'should return true when the date of the first day in that week is equal' do
                  date_time = DateTime.new(2009, 5, 4) #monday (first day of the week for reports_asp_sparkline)
       -          reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:week), date_time)
       +          reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:week), date_time)
        
                  reporting_period.should == DateTime.new(2009, 5, 7) #thursday of same week, should be equal
                end
       @@ -284,7 +284,7 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
                it 'should return true when the date of the first day in that month is equal' do
                  date_time = DateTime.new(2009, 5, 1)
       -          reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.new(Saulabs::ReportsAsSparkline::Grouping.new(:month), date_time)
       +          reporting_period = Saulabs::Reportable::ReportingPeriod.new(Saulabs::Reportable::Grouping.new(:month), date_time)
        
                  reporting_period.should == DateTime.new(2009, 5, 17)
                end
       @@ -303,14 +303,14 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
            end
        
            it 'should return a reporting period with the date part of (DateTime.now - limit.hours with minutes = seconds = 0 for grouping :hour' do
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.first(Saulabs::ReportsAsSparkline::Grouping.new(:hour), 3)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.first(Saulabs::Reportable::Grouping.new(:hour), 3)
              expected = @now - 3.hours
        
              reporting_period.date_time.should == DateTime.new(expected.year, expected.month, expected.day, expected.hour, 0, 0)
            end
        
            it 'should return a reporting period with the date part of (DateTime.now - limit.days) for grouping :day' do
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.first(Saulabs::ReportsAsSparkline::Grouping.new(:day), 3)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.first(Saulabs::Reportable::Grouping.new(:day), 3)
              expected = @now - 3.days
        
              reporting_period.date_time.should == Date.new(expected.year, expected.month, expected.day)
       @@ -318,14 +318,14 @@ describe Saulabs::ReportsAsSparkline::ReportingPeriod do
        
            it 'should return a reporting period with the date of the first day of the month at (DateTime.now - limit.months) for grouping :month' do
              DateTime.stub!(:now).and_return(DateTime.new(2008, 12, 31, 0, 0, 0))
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.first(Saulabs::ReportsAsSparkline::Grouping.new(:month), 3)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.first(Saulabs::Reportable::Grouping.new(:month), 3)
        
              reporting_period.date_time.should == DateTime.new(2008, 9, 1)
            end
        
            it 'should return a reporting period with the date of the monday of the week at (DateTime.now - limit.weeks) for grouping :week' do
              DateTime.stub!(:now).and_return(DateTime.new(2008, 12, 31, 0, 0, 0)) #wednesday
       -      reporting_period = Saulabs::ReportsAsSparkline::ReportingPeriod.first(Saulabs::ReportsAsSparkline::Grouping.new(:week), 3)
       +      reporting_period = Saulabs::Reportable::ReportingPeriod.first(Saulabs::Reportable::Grouping.new(:week), 3)
        
              reporting_period.date_time.should == DateTime.new(2008, 12, 8) #the monday 3 weeks earlier
            end
 (DIR) diff --git a/spec/db/database.yml b/spec/db/database.yml
       @@ -1,15 +1,15 @@
        sqlite3:
          adapter:  sqlite3
       -  database: spec/db/reports_as_sparkline.sqlite3.db
       +  database: spec/db/reportable.sqlite3.db
        mysql:
          adapter:  mysql
       -  database: reports_as_sparkline_test
       -  username: reports_as_sparkline
       -  password: reports_as_sparkline
       +  database: reportable_test
       +  username: reportable
       +  password: reportable
          host:     localhost
        postgresql:
          adapter:  postgresql
       -  database: reports_as_sparkline_test
       -  username: reports_as_sparkline
       -  password: reports_as_sparkline
       +  database: reportable_test
       +  username: reportable
       +  password: reportable
          host:     localhost
        \ No newline at end of file
 (DIR) diff --git a/spec/db/schema.rb b/spec/db/schema.rb
       @@ -8,7 +8,7 @@ ActiveRecord::Schema.define(:version => 1) do
            t.timestamps
          end
        
       -  create_table :reports_as_sparkline_cache, :force => true do |t|
       +  create_table :reportable_cache, :force => true do |t|
            t.string   :model_name,       :null => false
            t.string   :report_name,      :null => false
            t.string   :grouping,         :null => false
       @@ -19,14 +19,14 @@ ActiveRecord::Schema.define(:version => 1) do
        
            t.timestamps
          end
       -  add_index :reports_as_sparkline_cache, [
       +  add_index :reportable_cache, [
            :model_name,
            :report_name,
            :grouping,
            :aggregation,
            :condition
          ], :name => :name_model_grouping_agregation
       -  add_index :reports_as_sparkline_cache, [
       +  add_index :reportable_cache, [
            :model_name,
            :report_name,
            :grouping,
 (DIR) diff --git a/spec/other/report_method_spec.rb b/spec/other/report_method_spec.rb
       @@ -1,6 +1,6 @@
        require File.join(File.dirname(__FILE__), '..', 'spec_helper')
        
       -describe Saulabs::ReportsAsSparkline do
       +describe Saulabs::Reportable do
        
          describe 'for inherited models' do
        
       @@ -32,13 +32,13 @@ describe Saulabs::ReportsAsSparkline do
          end
        
          after do
       -    Saulabs::ReportsAsSparkline::ReportCache.destroy_all
       +    Saulabs::Reportable::ReportCache.destroy_all
          end
        
        end
        
        class User < ActiveRecord::Base
       -  reports_as_sparkline :registrations, :limit => 10
       +  reportable :registrations, :limit => 10
        end
        
        class SpecialUser < User; end
 (DIR) diff --git a/spec/other/sparkline_tag_helper_spec.rb b/spec/other/sparkline_tag_helper_spec.rb
       @@ -1,6 +1,6 @@
        require File.join(File.dirname(__FILE__), '..', 'spec_helper')
        
       -describe Saulabs::ReportsAsSparkline::SparklineTagHelper do
       +describe Saulabs::Reportable::SparklineTagHelper do
        
          before do
            @helper = TestHelper.new
       @@ -41,6 +41,6 @@ end
        
        class TestHelper
        
       -  include Saulabs::ReportsAsSparkline::SparklineTagHelper
       +  include Saulabs::Reportable::SparklineTagHelper
        
        end