Introduction
Introduction Statistics Contact Development Disclaimer Help
allowing to specify grouping on report execution (overrides report's default gr…
Log
Files
Refs
README
---
commit ae05149c92108118879b867ae6c51f1b72113ed4
parent e97c9a62834f5d6a6cea1ff387784c386ba7abb6
Author: Marco Otte-Witte <[email protected]>
Date: Wed, 14 Jan 2009 01:38:19 +0800
allowing to specify grouping on report execution (overrides report's default gr…
Signed-off-by: Marco Otte-Witte <[email protected]>
Diffstat:
M lib/kvlr/reports_as_sparkline/repo… | 25 ++++++++++++++-----------
M lib/kvlr/reports_as_sparkline/repo… | 22 +++++++++++-----------
M spec/classes/report_cache_spec.rb | 76 ++++++++++++++++++++---------…
M spec/classes/report_spec.rb | 26 +++++++++++++++++---------
4 files changed, 91 insertions(+), 58 deletions(-)
---
diff --git a/lib/kvlr/reports_as_sparkline/report.rb b/lib/kvlr/reports_as_spar…
@@ -5,7 +5,7 @@ module Kvlr #:nodoc:
# The Report class that does all the data retrieval and calculations
class Report
- attr_reader :klass, :name, :date_column, :value_column, :grouping, :aggr…
+ attr_reader :klass, :name, :date_column, :value_column, :aggregation, :o…
# ==== Parameters
# * <tt>klass</tt> - The model the report works on (This is the class yo…
@@ -26,12 +26,13 @@ module Kvlr #:nodoc:
@date_column = (options[:date_column] || 'created_at').to_s
@value_column = (options[:value_column] || (options[:aggregation] != :…
@aggregation = options[:aggregation] || :count
- @grouping = Grouping.new(options[:grouping] || :day)
@options = {
- :limit => options[:limit] || 100,
- :conditions => options[:conditions] || []
+ :limit => options[:limit] || 100,
+ :conditions => options[:conditions] || [],
+ :grouping => Grouping.new(options[:grouping] || :day)
}
@options.merge!(options)
+ @options.freeze
end
# Runs the report and returns an array of array of DateTimes and Floats
@@ -39,24 +40,26 @@ module Kvlr #:nodoc:
# ==== Options
# * <tt>:limit</tt> - The number of periods to get
# * <tt>:conditions</tt> - Conditions like in ActiveRecord::Base#find; o…
+ # * <tt>:grouping</tt> - The period records are grouped on (:hour, :day,…
def run(options = {})
ensure_valid_options(options, :run)
custom_conditions = options.key?(:conditions)
options.reverse_merge!(@options)
- ReportCache.process(self, options[:limit], custom_conditions) do |begi…
- read_data(begin_at, options[:conditions])
+ options[:grouping] = Grouping.new(options[:grouping]) unless options[:…
+ ReportCache.process(self, options[:limit], options[:grouping], custom_…
+ read_data(begin_at, options[:grouping], options[:conditions])
end
end
private
- def read_data(begin_at, conditions = []) #:nodoc:
+ def read_data(begin_at, grouping, conditions = []) #:nodoc:
conditions = setup_conditions(begin_at, conditions)
@klass.send(@aggregation,
@value_column,
:conditions => conditions,
- :group => @grouping.to_sql(@date_column),
- :order => "#{@grouping.to_sql(@date_column)} ASC"
+ :group => grouping.to_sql(@date_column),
+ :order => "#{grouping.to_sql(@date_column)} ASC"
)
end
@@ -78,13 +81,13 @@ module Kvlr #:nodoc:
raise ArgumentError.new("Invalid option #{k}") unless [:limit,…
end
raise ArgumentError.new("Invalid aggregation #{options[:aggregat…
- raise ArgumentError.new("Invalid grouping #{options[:grouping]}"…
raise ArgumentError.new('The name of the column holding the valu…
when :run
options.each_key do |k|
- raise ArgumentError.new("Invalid option #{k}") unless [:limit,…
+ raise ArgumentError.new("Invalid option #{k}") unless [:limit,…
end
end
+ raise ArgumentError.new("Invalid grouping #{options[:grouping]}") if…
raise ArgumentError.new("Invalid conditions: #{options[:conditions].…
end
diff --git a/lib/kvlr/reports_as_sparkline/report_cache.rb b/lib/kvlr/reports_a…
@@ -4,11 +4,11 @@ module Kvlr #:nodoc:
class ReportCache < ActiveRecord::Base #:nodoc:
- def self.process(report, limit, no_cache = false, &block)
+ def self.process(report, limit, grouping, no_cache = false, &block)
raise ArgumentError.new('A block must be given') unless block_given?
self.transaction do
cached_data = []
- last_reporting_period_to_read = ReportingPeriod.first(report.groupin…
+ last_reporting_period_to_read = ReportingPeriod.first(grouping, limi…
unless no_cache
cached_data = self.find(
:all,
@@ -16,29 +16,29 @@ module Kvlr #:nodoc:
'model_name = ? AND report_name = ? AND grouping = ? AND aggre…
report.klass.to_s,
report.name.to_s,
- report.grouping.identifier.to_s,
+ grouping.identifier.to_s,
report.aggregation.to_s,
last_reporting_period_to_read.date_time
],
:limit => limit,
:order => 'reporting_period ASC'
)
- last_reporting_period_to_read = ReportingPeriod.new(report.groupin…
+ last_reporting_period_to_read = ReportingPeriod.new(grouping, cach…
end
new_data = yield(last_reporting_period_to_read.date_time)
- prepare_result(new_data, cached_data, last_reporting_period_to_read,…
+ prepare_result(new_data, cached_data, last_reporting_period_to_read,…
end
end
private
- def self.prepare_result(new_data, cached_data, last_reporting_period_t…
- new_data.map! { |data| [ReportingPeriod.from_db_string(report.groupi…
+ def self.prepare_result(new_data, cached_data, last_reporting_period_t…
+ new_data.map! { |data| [ReportingPeriod.from_db_string(grouping, dat…
result = cached_data.map { |cached| [cached.reporting_period, cached…
- current_reporting_period = ReportingPeriod.new(report.grouping)
+ current_reporting_period = ReportingPeriod.new(grouping)
reporting_period = last_reporting_period_to_read
while reporting_period < current_reporting_period
- cached = build_cached_data(report, reporting_period, find_value(ne…
+ cached = build_cached_data(report, grouping, reporting_period, fin…
cached.save! unless no_cache
result << [reporting_period.date_time, cached.value]
reporting_period = reporting_period.next
@@ -52,11 +52,11 @@ module Kvlr #:nodoc:
data ? data[1] : 0.0
end
- def self.build_cached_data(report, reporting_period, value)
+ def self.build_cached_data(report, grouping, reporting_period, value)
self.new(
:model_name => report.klass.to_s,
:report_name => report.name.to_s,
- :grouping => report.grouping.identifier.to_s,
+ :grouping => grouping.identifier.to_s,
:aggregation => report.aggregation.to_s,
:reporting_period => reporting_period.date_time,
:value => value
diff --git a/spec/classes/report_cache_spec.rb b/spec/classes/report_cache_spec…
@@ -15,70 +15,89 @@ describe Kvlr::ReportsAsSparkline::ReportCache do
it 'should raise an ArgumentError if no block is given' do
lambda do
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10)
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.opt…
end.should raise_error(ArgumentError)
end
it 'sould start a transaction' do
Kvlr::ReportsAsSparkline::ReportCache.should_receive(:transaction)
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10) {}
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.optio…
end
it 'should yield to the given block' do
lambda {
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10) { raise Yie…
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.opt…
}.should raise_error(YieldMatchException)
end
- it 'should read existing data for the report from cache' do
+ it 'should read existing data from the cache' do
Kvlr::ReportsAsSparkline::ReportCache.should_receive(:find).once.with(
:all,
:conditions => [
'model_name = ? AND report_name = ? AND grouping = ? AND aggregation…
@report.klass.to_s,
@report.name.to_s,
- @report.grouping.identifier.to_s,
+ @report.options[:grouping].identifier.to_s,
@report.aggregation.to_s,
- Kvlr::ReportsAsSparkline::ReportingPeriod.first(@report.grouping, 10…
+ Kvlr::ReportsAsSparkline::ReportingPeriod.first(@report.options[:gro…
],
:limit => 10,
:order => 'reporting_period ASC'
)
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10) { [] }
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.optio…
+ end
+
+ it "should read existing data from the cache for the correct grouping if o…
+ grouping = Kvlr::ReportsAsSparkline::Grouping.new(:month)
+ Kvlr::ReportsAsSparkline::ReportCache.should_receive(:find).once.with(
+ :all,
+ :conditions => [
+ 'model_name = ? AND report_name = ? AND grouping = ? AND aggregation…
+ @report.klass.to_s,
+ @report.name.to_s,
+ grouping.identifier.to_s,
+ @report.aggregation.to_s,
+ Kvlr::ReportsAsSparkline::ReportingPeriod.first(grouping, 10).date_t…
+ ],
+ :limit => 10,
+ :order => 'reporting_period ASC'
+ )
+
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, grouping) { […
end
it 'should prepare the results before it returns them' do
new_data = []
cached_data = []
Kvlr::ReportsAsSparkline::ReportCache.stub!(:find).and_return(cached_dat…
- last_reporting_period_to_read = Kvlr::ReportsAsSparkline::ReportingPerio…
- Kvlr::ReportsAsSparkline::ReportCache.should_receive(:prepare_result).on…
+ last_reporting_period_to_read = Kvlr::ReportsAsSparkline::ReportingPerio…
+ Kvlr::ReportsAsSparkline::ReportCache.should_receive(:prepare_result).on…
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10) { new_data }
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.optio…
end
it 'should yield the first reporting period if the cache is empty' do
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10) do |begin_at|
- begin_at.should == Kvlr::ReportsAsSparkline::ReportingPeriod.first(@re…
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.optio…
+ begin_at.should == Kvlr::ReportsAsSparkline::ReportingPeriod.first(@re…
[]
end
end
it 'should yield the reporting period after the last one in the cache if t…
- reporting_period = Kvlr::ReportsAsSparkline::ReportingPeriod.new(@report…
+ reporting_period = Kvlr::ReportsAsSparkline::ReportingPeriod.new(@report…
cached = Kvlr::ReportsAsSparkline::ReportCache.new({
:model_name => @report.klass,
:report_name => @report.name,
- :grouping => @report.grouping.identifier.to_s,
+ :grouping => @report.options[:grouping].identifier.to_s,
:aggregation => @report.aggregation.to_s,
:value => 1,
:reporting_period => reporting_period.date_time
})
Kvlr::ReportsAsSparkline::ReportCache.stub!(:find).and_return([cached])
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10) do |begin_at|
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.optio…
begin_at.should == reporting_period.next.date_time
[]
end
@@ -89,12 +108,12 @@ describe Kvlr::ReportsAsSparkline::ReportCache do
it 'should not read any data from cache' do
Kvlr::ReportsAsSparkline::ReportCache.should_not_receive(:find)
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, true) {}
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.opt…
end
it 'should yield the first reporting period' do
- Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, true) do |b…
- begin_at.should == Kvlr::ReportsAsSparkline::ReportingPeriod.first(@…
+ Kvlr::ReportsAsSparkline::ReportCache.process(@report, 10, @report.opt…
+ begin_at.should == Kvlr::ReportsAsSparkline::ReportingPeriod.first(@…
[]
end
end
@@ -106,7 +125,7 @@ describe Kvlr::ReportsAsSparkline::ReportCache do
describe '.prepare_result' do
before do
- @last_reporting_period_to_read = Kvlr::ReportsAsSparkline::ReportingPeri…
+ @last_reporting_period_to_read = Kvlr::ReportsAsSparkline::ReportingPeri…
@new_data = [[@last_reporting_period_to_read.date_time, 1.0]]
Kvlr::ReportsAsSparkline::ReportingPeriod.stub!(:from_db_string).and_ret…
@cached = Kvlr::ReportsAsSparkline::ReportCache.new
@@ -115,44 +134,47 @@ describe Kvlr::ReportsAsSparkline::ReportCache do
end
it 'should convert the date strings from the newly read data to reporting …
- Kvlr::ReportsAsSparkline::ReportingPeriod.should_receive(:from_db_string…
+ Kvlr::ReportsAsSparkline::ReportingPeriod.should_receive(:from_db_string…
- Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, […
+ Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, […
end
it 'should create (:limit - 1) instances of Kvlr::ReportsAsSparkline::Repo…
Kvlr::ReportsAsSparkline::ReportCache.should_receive(:build_cached_data)…
@report,
+ @report.options[:grouping],
anything(),
0.0
).and_return(@cached)
- Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, [], [], @las…
+ Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, [], [], @las…
end
it 'should create a new Kvlr::ReportsAsSparkline::ReportCache with the cor…
Kvlr::ReportsAsSparkline::ReportCache.should_receive(:build_cached_data)…
@report,
+ @report.options[:grouping],
anything(),
0.0
).and_return(@cached)
Kvlr::ReportsAsSparkline::ReportCache.should_receive(:build_cached_data)…
@report,
+ @report.options[:grouping],
@last_reporting_period_to_read,
1.0
).and_return(@cached)
- Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, […
+ Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, […
end
it 'should save the created Kvlr::ReportsAsSparkline::ReportCache if no_ca…
@cached.should_receive(:save!).once
- Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, […
+ Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data, […
end
it 'should return an array of arrays of Dates and Floats' do
- result = Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @ne…
+ result = Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @ne…
result.should be_kind_of(Array)
result[0].should be_kind_of(Array)
@@ -165,14 +187,14 @@ describe Kvlr::ReportsAsSparkline::ReportCache do
it 'should not save the created Kvlr::ReportsAsSparkline::ReportCache' do
@cached.should_not_receive(:save!)
- Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data,…
+ Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data,…
end
it 'should not update the last cached record if new data has been read f…
Kvlr::ReportsAsSparkline::ReportingPeriod.stub!(:from_db_string).and_r…
@cached.should_not_receive(:update_attributes!)
- Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data,…
+ Kvlr::ReportsAsSparkline::ReportCache.send(:prepare_result, @new_data,…
end
end
diff --git a/spec/classes/report_spec.rb b/spec/classes/report_spec.rb
@@ -9,13 +9,13 @@ describe Kvlr::ReportsAsSparkline::Report do
describe '#run' do
it 'should process the data with the report cache' do
- Kvlr::ReportsAsSparkline::ReportCache.should_receive(:process).once.with…
+ Kvlr::ReportsAsSparkline::ReportCache.should_receive(:process).once.with…
@report.run
end
it 'should process the data with the report cache and specify no_cache whe…
- Kvlr::ReportsAsSparkline::ReportCache.should_receive(:process).once.with…
+ Kvlr::ReportsAsSparkline::ReportCache.should_receive(:process).once.with…
@report.run(:conditions => { :some => :condition })
end
@@ -26,6 +26,14 @@ describe Kvlr::ReportsAsSparkline::Report do
result = @report.run(:limit => 3)
end
+ it 'should use a custom grouping if one is specified' do
+ grouping = Kvlr::ReportsAsSparkline::Grouping.new(:month)
+ Kvlr::ReportsAsSparkline::Grouping.should_receive(:new).once.with(:month…
+ Kvlr::ReportsAsSparkline::ReportCache.should_receive(:process).once.with…
+
+ @report.run(:conditions => { :some => :condition }, :grouping => :month)
+ end
+
for grouping in [:hour, :day, :week, :month] do
describe "for grouping #{grouping.to_s}" do
@@ -114,13 +122,13 @@ describe Kvlr::ReportsAsSparkline::Report do
@report = Kvlr::ReportsAsSparkline::Report.new(User, :registrations, :ag…
User.should_receive(:count).once.and_return([])
- @report.send(:read_data, Time.now)
+ @report.send(:read_data, Time.now, @report.options[:grouping])
end
it 'should setup the conditions' do
@report.should_receive(:setup_conditions).once.and_return([])
- @report.send(:read_data, Time.now)
+ @report.send(:read_data, Time.now, @report.options[:grouping])
end
end
@@ -187,6 +195,10 @@ describe Kvlr::ReportsAsSparkline::Report do
lambda { @report.send(:ensure_valid_options, { :conditions => { :first_n…
end
+ it 'should raise an error if an invalid grouping is specified' do
+ lambda { @report.send(:ensure_valid_options, { :grouping => :decade }) }…
+ end
+
describe 'for context :initialize' do
it 'should not raise an error if valid options are specified' do
@@ -208,10 +220,6 @@ describe Kvlr::ReportsAsSparkline::Report do
it 'should raise an error if an invalid aggregation is specified' do
lambda { @report.send(:ensure_valid_options, { :aggregation => :invali…
end
-
- it 'should raise an error if an invalid grouping is specified' do
- lambda { @report.send(:ensure_valid_options, { :grouping => :decade })…
- end
it 'should raise an error if aggregation :sum is spesicied but no :value…
lambda { @report.send(:ensure_valid_options, { :aggregation => :sum })…
@@ -222,7 +230,7 @@ describe Kvlr::ReportsAsSparkline::Report do
describe 'for context :run' do
it 'should not raise an error if valid options are specified' do
- lambda { @report.send(:ensure_valid_options, { :limit => 100, :conditi…
+ lambda { @report.send(:ensure_valid_options, { :limit => 100, :conditi…
}.should_not raise_error(ArgumentError)
end
You are viewing proxied material from jay.scot. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.