| update to v1.3 - reportable - Fork of reportable required by WarVox, from hdm/r… | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| commit 1b697477d79a5e17e869b4acc1f6be4330226037 | |
| parent 80083c77564a3a852216fa1891136540ada6963b | |
| Author: Marco Otte-Witte <[email protected]> | |
| Date: Tue, 5 May 2009 19:17:04 +0200 | |
| update to v1.3 | |
| Diffstat: | |
| M HISTORY.rdoc | 6 ++++++ | |
| M README.rdoc | 29 +++++++++++++++++++++++++++++ | |
| M doc/classes/Simplabs/ReportsAsSpar… | 35 +++++++++++++--------------… | |
| M doc/classes/Simplabs/ReportsAsSpar… | 2 +- | |
| M doc/classes/Simplabs/ReportsAsSpar… | 24 ++++++++++++------------ | |
| M doc/classes/Simplabs/ReportsAsSpar… | 68 +++++++++++++++++++++++++++… | |
| M doc/created.rid | 2 +- | |
| M doc/files/README_rdoc.html | 44 +++++++++++++++++++++++++++++… | |
| M doc/files/lib/simplabs/reports_as_… | 2 +- | |
| M doc/files/lib/simplabs/reports_as_… | 2 +- | |
| M doc/files/lib/simplabs/reports_as_… | 2 +- | |
| M doc/files/lib/simplabs/reports_as_… | 2 +- | |
| M doc/files/lib/simplabs/reports_as_… | 2 +- | |
| M doc/fr_method_index.html | 5 +++-- | |
| M lib/simplabs/reports_as_sparkline.… | 5 ----- | |
| M lib/simplabs/reports_as_sparkline/… | 10 +++++----- | |
| M lib/simplabs/reports_as_sparkline/… | 5 +++-- | |
| M lib/simplabs/reports_as_sparkline/… | 93 +++++++++++++++++++--------… | |
| M lib/simplabs/reports_as_sparkline/… | 28 +++++++++++++++++++--------- | |
| M spec/classes/report_cache_spec.rb | 93 +++++++++++++++++++----------… | |
| M spec/classes/report_spec.rb | 20 ++++++++++++++++++++ | |
| M spec/classes/reporting_period_spec… | 48 +++++++++++++++++++++++++++… | |
| M spec/db/schema.rb | 7 ++----- | |
| 23 files changed, 393 insertions(+), 141 deletions(-) | |
| --- | |
| diff --git a/HISTORY.rdoc b/HISTORY.rdoc | |
| @@ -1,3 +1,9 @@ | |
| +=== v1.3 | |
| + | |
| +* Fixed another bug where two report runs with different :end_date values woul… | |
| +* Entries in the report cache entry are no longer identified by the :limit the… | |
| +* rewrote the data retrieval and processing code, it's now much more compact a… | |
| + | |
| === v1.2 | |
| * FIXED: duplicate key error when using custom time zones (thanks to myronmars… | |
| diff --git a/README.rdoc b/README.rdoc | |
| @@ -43,6 +43,26 @@ When invoking the report, you can override some of the optio… | |
| User.registrations_report(:conditions => ['last_name LIKE 'A%']) | |
| User.activations_report(:grouping => :week, :limit => 5) | |
| +=== The Report cache | |
| + | |
| +Unless you specify parameters that make it impossible to cache report results,… | |
| +<b>Beware that when you modify data in the cache, report results may be incorr… | |
| +method. | |
| + | |
| +<b>Example</b> | |
| + | |
| +For a report defined as | |
| + | |
| + class User < ActiveRecord::Base | |
| + reports_as_sparkline :registrations | |
| + end | |
| + | |
| +you can clear the cache with | |
| + | |
| + Simplabs::ReportsAsSparkline::ReportCache.clear_for(User, :registrations) | |
| + | |
| +=== In your views | |
| + | |
| You can than render sparklines for these reports with sparkline_tag in your vi… | |
| <%= sparkline_tag(User.registrations_report) %> | |
| @@ -91,6 +111,15 @@ If you are on PostgreSQL, you should add functional indices: | |
| add_index :[table], :[date_column], :functional => "date_trunc('week', [date… | |
| add_index :[table], :[date_column], :functional => "date_trunc('year', [date… | |
| +You might also want to use fragment caching in your views since processing the… | |
| +aso takes some time. | |
| + | |
| +<b>Example</b> | |
| + | |
| + <%- cache do -%> | |
| + <%= sparkline_tag(User.registrations_report) %> | |
| + <%- end -%> | |
| + | |
| == TODOs/ future plans | |
| * support for Oracle and DB2 (and others?) missing | |
| diff --git a/doc/classes/Simplabs/ReportsAsSparkline/ClassMethods.html b/doc/cl… | |
| @@ -115,7 +115,7 @@ | |
| <p> | |
| Generates a report on a model. That report can then be executed via the new | |
| method <tt><name>_report</tt> (see documentation of <a | |
| -href="Report.html#M000007">Simplabs::ReportsAsSparkline::Report#run</a>). | |
| +href="Report.html#M000008">Simplabs::ReportsAsSparkline::Report#run</a>). | |
| </p> | |
| <h4>Parameters</h4> | |
| <ul> | |
| @@ -170,11 +170,6 @@ the <tt>:limit</tt> reporting periods until this date. | |
| </ul> | |
| <h4>Examples</h4> | |
| <pre> | |
| - class Game < ActiveRecord::Base | |
| - reports_as_sparkline :games_per_day | |
| - reports_as_sparkline :games_played_total, :cumulate => true | |
| - end | |
| - | |
| class User < ActiveRecord::Base | |
| reports_as_sparkline :registrations, :aggregation => :count | |
| reports_as_sparkline :activations, :aggregation => :count, :date_colum… | |
| @@ -186,20 +181,20 @@ the <tt>:limit</tt> reporting periods until this date. | |
| onclick="toggleCode('M000002-source');return false;">[Source]</a><… | |
| <div class="method-source-code" id="M000002-source"> | |
| <pre> | |
| - <span class="ruby-comment cmt"># File lib/simplabs/reports_as_sparkline.rb… | |
| -41: <span class="ruby-keyword kw">def</span> <span class="ruby-identifie… | |
| -42: (<span class="ruby-keyword kw">class</span> <span class="ruby-oper… | |
| -43: <span class="ruby-identifier">define_method</span> <span class="… | |
| -44: <span class="ruby-keyword kw">if</span> <span class="ruby-iden… | |
| -45: <span class="ruby-identifier">report</span> = <span class="r… | |
| -46: <span class="ruby-keyword kw">else</span> | |
| -47: <span class="ruby-identifier">report</span> = <span class="r… | |
| -48: <span class="ruby-keyword kw">end</span> | |
| -49: <span class="ruby-identifier">raise</span> <span class="ruby-c… | |
| -50: <span class="ruby-identifier">report</span>.<span class="ruby-… | |
| -51: <span class="ruby-keyword kw">end</span> | |
| -52: <span class="ruby-keyword kw">end</span> | |
| -53: <span class="ruby-keyword kw">end</span> | |
| + <span class="ruby-comment cmt"># File lib/simplabs/reports_as_sparkline.rb… | |
| +36: <span class="ruby-keyword kw">def</span> <span class="ruby-identifie… | |
| +37: (<span class="ruby-keyword kw">class</span> <span class="ruby-oper… | |
| +38: <span class="ruby-identifier">define_method</span> <span class="… | |
| +39: <span class="ruby-keyword kw">if</span> <span class="ruby-iden… | |
| +40: <span class="ruby-identifier">report</span> = <span class="r… | |
| +41: <span class="ruby-keyword kw">else</span> | |
| +42: <span class="ruby-identifier">report</span> = <span class="r… | |
| +43: <span class="ruby-keyword kw">end</span> | |
| +44: <span class="ruby-identifier">raise</span> <span class="ruby-c… | |
| +45: <span class="ruby-identifier">report</span>.<span class="ruby-… | |
| +46: <span class="ruby-keyword kw">end</span> | |
| +47: <span class="ruby-keyword kw">end</span> | |
| +48: <span class="ruby-keyword kw">end</span> | |
| </pre> | |
| </div> | |
| </div> | |
| diff --git a/doc/classes/Simplabs/ReportsAsSparkline/CumulatedReport.html b/doc… | |
| @@ -146,7 +146,7 @@ returns | |
| <div class="method-description"> | |
| <p> | |
| Runs the report (see <a | |
| -href="Report.html#M000007">Simplabs::ReportsAsSparkline::Report#run</a>) | |
| +href="Report.html#M000008">Simplabs::ReportsAsSparkline::Report#run</a>) | |
| </p> | |
| <p><a class="source-toggle" href="#" | |
| onclick="toggleCode('M000003-source');return false;">[Source]</a><… | |
| diff --git a/doc/classes/Simplabs/ReportsAsSparkline/Report.html b/doc/classes/… | |
| @@ -93,8 +93,8 @@ and calculations | |
| <h3 class="section-bar">Methods</h3> | |
| <div class="name-list"> | |
| - <a href="#M000006">new</a> | |
| - <a href="#M000007">run</a> | |
| + <a href="#M000007">new</a> | |
| + <a href="#M000008">run</a> | |
| </div> | |
| </div> | |
| @@ -154,11 +154,11 @@ and calculations | |
| <div id="methods"> | |
| <h3 class="section-bar">Public Class methods</h3> | |
| - <div id="method-M000006" class="method-detail"> | |
| - <a name="M000006"></a> | |
| + <div id="method-M000007" class="method-detail"> | |
| + <a name="M000007"></a> | |
| <div class="method-heading"> | |
| - <a href="#M000006" class="method-signature"> | |
| + <a href="#M000007" class="method-signature"> | |
| <span class="method-name">new</span><span class="method-args">(klass… | |
| </a> | |
| </div> | |
| @@ -221,8 +221,8 @@ the <tt>:limit</tt> reporting periods until this date. | |
| </li> | |
| </ul> | |
| <p><a class="source-toggle" href="#" | |
| - onclick="toggleCode('M000006-source');return false;">[Source]</a><… | |
| - <div class="method-source-code" id="M000006-source"> | |
| + onclick="toggleCode('M000007-source');return false;">[Source]</a><… | |
| + <div class="method-source-code" id="M000007-source"> | |
| <pre> | |
| <span class="ruby-comment cmt"># File lib/simplabs/reports_as_sparkline/re… | |
| 24: <span class="ruby-keyword kw">def</span> <span class="ruby-identifie… | |
| @@ -249,11 +249,11 @@ the <tt>:limit</tt> reporting periods until this date. | |
| <h3 class="section-bar">Public Instance methods</h3> | |
| - <div id="method-M000007" class="method-detail"> | |
| - <a name="M000007"></a> | |
| + <div id="method-M000008" class="method-detail"> | |
| + <a name="M000008"></a> | |
| <div class="method-heading"> | |
| - <a href="#M000007" class="method-signature"> | |
| + <a href="#M000008" class="method-signature"> | |
| <span class="method-name">run</span><span class="method-args">(optio… | |
| </a> | |
| </div> | |
| @@ -290,8 +290,8 @@ the <tt>:limit</tt> reporting periods until this date. | |
| </li> | |
| </ul> | |
| <p><a class="source-toggle" href="#" | |
| - onclick="toggleCode('M000007-source');return false;">[Source]</a><… | |
| - <div class="method-source-code" id="M000007-source"> | |
| + onclick="toggleCode('M000008-source');return false;">[Source]</a><… | |
| + <div class="method-source-code" id="M000008-source"> | |
| <pre> | |
| <span class="ruby-comment cmt"># File lib/simplabs/reports_as_sparkline/re… | |
| 50: <span class="ruby-keyword kw">def</span> <span class="ruby-identifie… | |
| diff --git a/doc/classes/Simplabs/ReportsAsSparkline/ReportCache.html b/doc/cla… | |
| @@ -85,8 +85,7 @@ The <a href="ReportCache.html">ReportCache</a> class is a reg… | |
| reporting periods (table name is <tt>reports_as_sparkline_cache</tt>) <a | |
| href="ReportCache.html">ReportCache</a> instances are identified by the | |
| combination of <tt>model_name</tt>, <tt>report_name</tt>, | |
| -<tt>grouping</tt>, <tt>aggregation</tt>, <tt>reporting_period</tt>, | |
| -<tt>run_limit</tt> | |
| +<tt>grouping</tt>, <tt>aggregation</tt> and <tt>reporting_period</tt> | |
| </p> | |
| </div> | |
| @@ -94,6 +93,13 @@ combination of <tt>model_name</tt>, <tt>report_name</tt>, | |
| </div> | |
| + <div id="method-list"> | |
| + <h3 class="section-bar">Methods</h3> | |
| + | |
| + <div class="name-list"> | |
| + <a href="#M000006">clear_for</a> | |
| + </div> | |
| + </div> | |
| </div> | |
| @@ -110,6 +116,64 @@ combination of <tt>model_name</tt>, <tt>report_name</tt>, | |
| <!-- if method_list --> | |
| + <div id="methods"> | |
| + <h3 class="section-bar">Public Class methods</h3> | |
| + | |
| + <div id="method-M000006" class="method-detail"> | |
| + <a name="M000006"></a> | |
| + | |
| + <div class="method-heading"> | |
| + <a href="#M000006" class="method-signature"> | |
| + <span class="method-name">clear_for</span><span class="method-args">… | |
| + </a> | |
| + </div> | |
| + | |
| + <div class="method-description"> | |
| + <p> | |
| +Clears the cache for the specified <tt>klass</tt> and <tt>report</tt> | |
| +</p> | |
| +<h3>Parameters</h3> | |
| +<ul> | |
| +<li><tt>klass</tt> - The model the report to clear the cache for works on | |
| + | |
| +</li> | |
| +<li><tt>report</tt> - The name of the report to clear the cache for | |
| + | |
| +</li> | |
| +</ul> | |
| +<h3>Example</h3> | |
| +<p> | |
| +To clear the cache for a report defined as | |
| +</p> | |
| +<pre> | |
| + class User < ActiveRecord::Base | |
| + reports_as_sparkline :registrations | |
| + end | |
| +</pre> | |
| +<p> | |
| +just do | |
| +</p> | |
| +<pre> | |
| + Simplabs::ReportsAsSparkline::ReportCache.clear_for(User, :registrations) | |
| +</pre> | |
| + <p><a class="source-toggle" href="#" | |
| + onclick="toggleCode('M000006-source');return false;">[Source]</a><… | |
| + <div class="method-source-code" id="M000006-source"> | |
| +<pre> | |
| + <span class="ruby-comment cmt"># File lib/simplabs/reports_as_sparkline/re… | |
| +26: <span class="ruby-keyword kw">def</span> <span class="ruby-keyword k… | |
| +27: <span class="ruby-keyword kw">self</span>.<span class="ruby-identi… | |
| +28: <span class="ruby-identifier">:model_name</span> =<span class="… | |
| +29: <span class="ruby-identifier">:report_name</span> =<span class="… | |
| +30: }) | |
| +31: <span class="ruby-keyword kw">end</span> | |
| +</pre> | |
| + </div> | |
| + </div> | |
| + </div> | |
| + | |
| + | |
| + </div> | |
| </div> | |
| diff --git a/doc/created.rid b/doc/created.rid | |
| @@ -1 +1 @@ | |
| -Wed, 29 Apr 2009 19:32:38 +0200 | |
| +Tue, 05 May 2009 19:07:12 +0200 | |
| diff --git a/doc/files/README_rdoc.html b/doc/files/README_rdoc.html | |
| @@ -56,7 +56,7 @@ | |
| </tr> | |
| <tr class="top-aligned-row"> | |
| <td><strong>Last Update:</strong></td> | |
| - <td>Wed Apr 29 19:32:28 +0200 2009</td> | |
| + <td>Tue May 05 19:07:10 +0200 2009</td> | |
| </tr> | |
| </table> | |
| </div> | |
| @@ -177,6 +177,35 @@ the <tt>:limit</tt> reporting periods until this date. | |
| User.registrations_report(:conditions => ['last_name LIKE 'A%']) | |
| User.activations_report(:grouping => :week, :limit => 5) | |
| </pre> | |
| +<h3>The Report cache</h3> | |
| +<p> | |
| +Unless you specify parameters that make it impossible to cache report | |
| +results, all results will be cached. You can access the cache via the | |
| +<tt><a | |
| +href="../classes/Simplabs/ReportsAsSparkline/ReportCache.html">Simplabs::Repor… | |
| +class. <b>Beware that when you modify data in the cache, report results may | |
| +be incorrect or execurting reports may even fail completely!</b> To clear | |
| +the cache for a specific report, use the | |
| +<tt>Simplabs::ReportsAsSparkline::ReportCache.clear_for</tt> method. | |
| +</p> | |
| +<p> | |
| +<b>Example</b> | |
| +</p> | |
| +<p> | |
| +For a report defined as | |
| +</p> | |
| +<pre> | |
| + class User < ActiveRecord::Base | |
| + reports_as_sparkline :registrations | |
| + end | |
| +</pre> | |
| +<p> | |
| +you can clear the cache with | |
| +</p> | |
| +<pre> | |
| + Simplabs::ReportsAsSparkline::ReportCache.clear_for(User, :registrations) | |
| +</pre> | |
| +<h3>In your views</h3> | |
| <p> | |
| You can than render sparklines for these reports with sparkline_tag in your | |
| view: | |
| @@ -255,6 +284,19 @@ If you are on PostgreSQL, you should add functional indice… | |
| add_index :[table], :[date_column], :functional => "date_trunc('week… | |
| add_index :[table], :[date_column], :functional => "date_trunc('year… | |
| </pre> | |
| +<p> | |
| +You might also want to use fragment caching in your views since processing | |
| +the data read from the db (either from the model‘s table or from the | |
| +cache) aso takes some time. | |
| +</p> | |
| +<p> | |
| +<b>Example</b> | |
| +</p> | |
| +<pre> | |
| + <%- cache(:key => 'UserRegistrationsReport') do -%> | |
| + <%= sparkline_tag(User.registrations_report) %> | |
| + <%- end -%> | |
| +</pre> | |
| <h2>TODOs/ future plans</h2> | |
| <ul> | |
| <li>support for Oracle and DB2 (and others?) missing | |
| diff --git a/doc/files/lib/simplabs/reports_as_sparkline/grouping_rb.html b/doc… | |
| @@ -56,7 +56,7 @@ | |
| </tr> | |
| <tr class="top-aligned-row"> | |
| <td><strong>Last Update:</strong></td> | |
| - <td>Wed Apr 29 19:18:36 +0200 2009</td> | |
| + <td>Tue May 05 18:20:50 +0200 2009</td> | |
| </tr> | |
| </table> | |
| </div> | |
| diff --git a/doc/files/lib/simplabs/reports_as_sparkline/report_cache_rb.html b… | |
| @@ -56,7 +56,7 @@ | |
| </tr> | |
| <tr class="top-aligned-row"> | |
| <td><strong>Last Update:</strong></td> | |
| - <td>Wed Apr 29 19:20:49 +0200 2009</td> | |
| + <td>Tue May 05 19:02:15 +0200 2009</td> | |
| </tr> | |
| </table> | |
| </div> | |
| diff --git a/doc/files/lib/simplabs/reports_as_sparkline/report_rb.html b/doc/f… | |
| @@ -56,7 +56,7 @@ | |
| </tr> | |
| <tr class="top-aligned-row"> | |
| <td><strong>Last Update:</strong></td> | |
| - <td>Wed Apr 29 19:26:39 +0200 2009</td> | |
| + <td>Tue May 05 15:51:11 +0200 2009</td> | |
| </tr> | |
| </table> | |
| </div> | |
| diff --git a/doc/files/lib/simplabs/reports_as_sparkline/reporting_period_rb.ht… | |
| @@ -56,7 +56,7 @@ | |
| </tr> | |
| <tr class="top-aligned-row"> | |
| <td><strong>Last Update:</strong></td> | |
| - <td>Wed Apr 29 19:18:27 +0200 2009</td> | |
| + <td>Tue May 05 18:41:47 +0200 2009</td> | |
| </tr> | |
| </table> | |
| </div> | |
| diff --git a/doc/files/lib/simplabs/reports_as_sparkline_rb.html b/doc/files/li… | |
| @@ -56,7 +56,7 @@ | |
| </tr> | |
| <tr class="top-aligned-row"> | |
| <td><strong>Last Update:</strong></td> | |
| - <td>Wed Apr 29 19:29:22 +0200 2009</td> | |
| + <td>Tue May 05 19:02:42 +0200 2009</td> | |
| </tr> | |
| </table> | |
| </div> | |
| diff --git a/doc/fr_method_index.html b/doc/fr_method_index.html | |
| @@ -20,12 +20,13 @@ | |
| <div id="index"> | |
| <h1 class="section-bar">Methods</h1> | |
| <div id="index-entries"> | |
| + <a href="classes/Simplabs/ReportsAsSparkline/ReportCache.html#M000006">cle… | |
| <a href="classes/Simplabs/ReportsAsSparkline/CumulatedReport.html#M000004"… | |
| <a href="classes/Simplabs/ReportsAsSparkline/CumulatedReport.html#M000005"… | |
| - <a href="classes/Simplabs/ReportsAsSparkline/Report.html#M000006">new (Sim… | |
| + <a href="classes/Simplabs/ReportsAsSparkline/Report.html#M000007">new (Sim… | |
| <a href="classes/Simplabs/ReportsAsSparkline/ClassMethods.html#M000002">re… | |
| <a href="classes/Simplabs/ReportsAsSparkline/CumulatedReport.html#M000003"… | |
| - <a href="classes/Simplabs/ReportsAsSparkline/Report.html#M000007">run (Sim… | |
| + <a href="classes/Simplabs/ReportsAsSparkline/Report.html#M000008">run (Sim… | |
| <a href="classes/Simplabs/ReportsAsSparkline/SparklineTagHelper.html#M0000… | |
| </div> | |
| </div> | |
| diff --git a/lib/simplabs/reports_as_sparkline.rb b/lib/simplabs/reports_as_spa… | |
| @@ -27,11 +27,6 @@ module Simplabs #:nodoc: | |
| # | |
| # ==== Examples | |
| # | |
| - # class Game < ActiveRecord::Base | |
| - # reports_as_sparkline :games_per_day | |
| - # reports_as_sparkline :games_played_total, :cumulate => true | |
| - # end | |
| - # | |
| # class User < ActiveRecord::Base | |
| # reports_as_sparkline :registrations, :aggregation => :count | |
| # reports_as_sparkline :activations, :aggregation => :count, :date_… | |
| diff --git a/lib/simplabs/reports_as_sparkline/grouping.rb b/lib/simplabs/repor… | |
| @@ -14,7 +14,7 @@ module Simplabs #:nodoc: | |
| end | |
| def date_parts_from_db_string(db_string) | |
| - return case ActiveRecord::Base.connection.adapter_name | |
| + case ActiveRecord::Base.connection.adapter_name | |
| when /mysql/i | |
| from_mysql_db_string(db_string) | |
| when /sqlite/i | |
| @@ -25,7 +25,7 @@ module Simplabs #:nodoc: | |
| end | |
| def to_sql(date_column) #:nodoc: | |
| - return case ActiveRecord::Base.connection.adapter_name | |
| + case ActiveRecord::Base.connection.adapter_name | |
| when /mysql/i | |
| mysql_format(date_column) | |
| when /sqlite/i | |
| @@ -70,7 +70,7 @@ module Simplabs #:nodoc: | |
| end | |
| def mysql_format(date_column) | |
| - return case @identifier | |
| + case @identifier | |
| when :hour | |
| "DATE_FORMAT(#{date_column}, '%Y/%m/%d/%H')" | |
| when :day | |
| @@ -83,7 +83,7 @@ module Simplabs #:nodoc: | |
| end | |
| def sqlite_format(date_column) | |
| - return case @identifier | |
| + case @identifier | |
| when :hour | |
| "strftime('%Y/%m/%d/%H', #{date_column})" | |
| when :day | |
| @@ -96,7 +96,7 @@ module Simplabs #:nodoc: | |
| end | |
| def postgresql_format(date_column) | |
| - return case @identifier | |
| + case @identifier | |
| when :hour | |
| "date_trunc('hour', #{date_column})" | |
| when :day | |
| diff --git a/lib/simplabs/reports_as_sparkline/report.rb b/lib/simplabs/reports… | |
| @@ -70,8 +70,9 @@ module Simplabs #:nodoc: | |
| @klass.send(@aggregation, | |
| @value_column, | |
| :conditions => conditions, | |
| - :group => options[:grouping].to_sql(@date_column), | |
| - :order => "#{options[:grouping].to_sql(@date_column)} ASC" | |
| + :group => options[:grouping].to_sql(@date_column), | |
| + :order => "#{options[:grouping].to_sql(@date_column)} ASC", | |
| + :limit => options[:limit] | |
| ) | |
| end | |
| diff --git a/lib/simplabs/reports_as_sparkline/report_cache.rb b/lib/simplabs/r… | |
| @@ -3,32 +3,41 @@ module Simplabs #:nodoc: | |
| module ReportsAsSparkline #:nodoc: | |
| # The ReportCache class is a regular +ActiveRecord+ model and represents c… | |
| - # ReportCache instances are identified by the combination of +model_name+,… | |
| + # ReportCache instances are identified by the combination of +model_name+,… | |
| class ReportCache < ActiveRecord::Base | |
| set_table_name :reports_as_sparkline_cache | |
| - # When reporting_period has a time zone conversion performed, we get dup… | |
| - # This occurs because find_cached_data will return a record set that is … | |
| - # have an end date. The SQL criteria reporting_period BETWEEN before_da… | |
| - # the last record that it should, because our end_date will not be time-… | |
| - # the reported_period in the database will be time-zone converted (ex: 5… | |
| 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 | |
| + # Simplabs::ReportsAsSparkline::ReportCache.clear_for(User, :registrati… | |
| + 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, cache = true, &block) #:nodoc: | |
| raise ArgumentError.new('A block must be given') unless block_given? | |
| self.transaction do | |
| cached_data = [] | |
| - first_reporting_period = get_first_reporting_period(options) | |
| - last_reporting_period = options[:end_date] ? ReportingPeriod.new(opt… | |
| - | |
| if cache | |
| - cached_data = find_cached_data(report, options, first_reporting_pe… | |
| - first_cached_reporting_period = cached_data.empty? ? nil : Reporti… | |
| - last_cached_reporting_period = cached_data.empty? ? nil : Reportin… | |
| + cached_data = read_cached_data(report, options) | |
| end | |
| - new_data = read_new_data(first_reporting_period, last_reporting_peri… | |
| - | |
| + new_data = read_new_data(cached_data, options, &block) | |
| prepare_result(new_data, cached_data, report, options, cache) | |
| end | |
| end | |
| @@ -37,17 +46,22 @@ module Simplabs #:nodoc: | |
| def self.prepare_result(new_data, cached_data, report, options, cache … | |
| new_data = new_data.map { |data| [ReportingPeriod.from_db_string(opt… | |
| - result = cached_data.map { |cached| [cached.reporting_period, cached… | |
| - last_reporting_period = ReportingPeriod.new(options[:grouping]) | |
| - reporting_period = cached_data.empty? ? get_first_reporting_period(o… | |
| - while reporting_period < (options[:end_date] ? ReportingPeriod.new(o… | |
| - cached = build_cached_data(report, options[:grouping], options[:li… | |
| - cached.save! if cache | |
| - result << [reporting_period.date_time, cached.value] | |
| + cached_data.map! { |cached| [ReportingPeriod.new(options[:grouping],… | |
| + current_reporting_period = ReportingPeriod.current(options[:grouping… | |
| + reporting_period = get_first_reporting_period(options) | |
| + result = [] | |
| + while reporting_period < (options[:end_date] ? ReportingPeriod.new(o… | |
| + if cached = cached_data.find { |cached| reporting_period == cached… | |
| + result << cached | |
| + else | |
| + new_cached = build_cached_data(report, options[:grouping], repor… | |
| + new_cached.save! if cache | |
| + result << [reporting_period.date_time, new_cached.value] | |
| + end | |
| reporting_period = reporting_period.next | |
| end | |
| if options[:live_data] | |
| - result << [last_reporting_period.date_time, find_value(new_data, l… | |
| + result << [current_reporting_period.date_time, find_value(new_data… | |
| end | |
| result | |
| end | |
| @@ -57,27 +71,27 @@ module Simplabs #:nodoc: | |
| data ? data[1] : 0.0 | |
| end | |
| - def self.build_cached_data(report, grouping, limit, reporting_period, … | |
| + 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 => grouping.identifier.to_s, | |
| :aggregation => report.aggregation.to_s, | |
| :reporting_period => reporting_period.date_time, | |
| - :value => value, | |
| - :run_limit => limit | |
| + :value => value | |
| ) | |
| end | |
| - def self.find_cached_data(report, options, first_reporting_period, las… | |
| + def self.read_cached_data(report, options) | |
| conditions = [ | |
| - 'model_name = ? AND report_name = ? AND grouping = ? AND aggregati… | |
| + 'model_name = ? AND report_name = ? AND grouping = ? AND aggregati… | |
| report.klass.to_s, | |
| report.name.to_s, | |
| options[:grouping].identifier.to_s, | |
| - report.aggregation.to_s, | |
| - options[:limit] | |
| + report.aggregation.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 | |
| @@ -88,17 +102,22 @@ module Simplabs #:nodoc: | |
| end | |
| self.all( | |
| :conditions => conditions, | |
| - :limit => options[:limit], | |
| - :order => 'reporting_period ASC' | |
| + :limit => options[:limit], | |
| + :order => 'reporting_period ASC' | |
| ) | |
| end | |
| - def self.read_new_data(first_reporting_period, last_reporting_period, … | |
| - if !options[:live_data] && last_cached_reporting_period == Reporting… | |
| + def self.read_new_data(cached_data, options, &block) | |
| + if !options[:live_data] && cached_data.length == options[:limit] | |
| [] | |
| else | |
| - end_date = options[:live_data] ? nil : (options[:end_date] ? last_… | |
| - yield((last_cached_reporting_period.next rescue first_reporting_pe… | |
| + first_reporting_period_to_read = if cached_data.length < options[:… | |
| + get_first_reporting_period(options) | |
| + else | |
| + ReportingPeriod.new(options[:grouping], cached_data.last.reporti… | |
| + end | |
| + last_reporting_period_to_read = options[:end_date] ? ReportingPeri… | |
| + yield(first_reporting_period_to_read.date_time, last_reporting_per… | |
| end | |
| end | |
| @@ -110,6 +129,10 @@ module Simplabs #:nodoc: | |
| end | |
| end | |
| + def self.get_last_reporting_period(options) | |
| + return ReportingPeriod.new(options[:grouping], options[:end_date]) i… | |
| + end | |
| + | |
| end | |
| end | |
| diff --git a/lib/simplabs/reports_as_sparkline/reporting_period.rb b/lib/simpla… | |
| @@ -11,10 +11,18 @@ module Simplabs #:nodoc: | |
| @date_time = parse_date_time(date_time || DateTime.now) | |
| end | |
| + def offset(offset) | |
| + self.class.new(@grouping, @date_time + offset.send(@grouping.identifie… | |
| + 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 | |
| @@ -38,22 +46,24 @@ module Simplabs #:nodoc: | |
| self.offset(-1) | |
| end | |
| - def offset(offset) | |
| - self.class.new(@grouping, @date_time + offset.send(@grouping.identifie… | |
| - end | |
| - | |
| def ==(other) | |
| - if other.class == Simplabs::ReportsAsSparkline::ReportingPeriod | |
| - return @date_time.to_s == other.date_time.to_s && @grouping.identifi… | |
| + if other.is_a?(Simplabs::ReportsAsSparkline::ReportingPeriod) | |
| + @date_time.to_s == other.date_time.to_s && @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.… | |
| end | |
| - false | |
| end | |
| def <(other) | |
| - if other.class == Simplabs::ReportsAsSparkline::ReportingPeriod | |
| + if other.is_a?(Simplabs::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.… | |
| end | |
| - raise ArgumentError.new("Can only compare instances of #{Simplabs::Rep… | |
| end | |
| def last_date_time | |
| diff --git a/spec/classes/report_cache_spec.rb b/spec/classes/report_cache_spec… | |
| @@ -6,6 +6,19 @@ describe Simplabs::ReportsAsSparkline::ReportCache do | |
| @report = Simplabs::ReportsAsSparkline::Report.new(User, :registrations, :… | |
| end | |
| + describe '.clear_for' do | |
| + | |
| + it 'should delete all entries in the cache for the klass and report name' … | |
| + Simplabs::ReportsAsSparkline::ReportCache.should_receive(:delete_all).on… | |
| + :model_name => User.name, | |
| + :report_name => 'registrations' | |
| + }) | |
| + | |
| + Simplabs::ReportsAsSparkline::ReportCache.clear_for(User, :registrations) | |
| + end | |
| + | |
| + end | |
| + | |
| describe '.process' do | |
| before do | |
| @@ -37,17 +50,31 @@ describe Simplabs::ReportsAsSparkline::ReportCache do | |
| }.should raise_error(YieldMatchException) | |
| end | |
| - it 'should yield the reporting period after the last one in the cache if… | |
| + it 'should yield the first reporting period if not all required data cou… | |
| reporting_period = Simplabs::ReportsAsSparkline::ReportingPeriod.new( | |
| @report.options[:grouping], | |
| Time.now - 3.send(@report.options[:grouping].identifier) | |
| ) | |
| + Simplabs::ReportsAsSparkline::ReportCache.stub!(:all).and_return([Simp… | |
| + | |
| + Simplabs::ReportsAsSparkline::ReportCache.process(@report, @options) d… | |
| + begin_at.should == Simplabs::ReportsAsSparkline::ReportingPeriod.fir… | |
| + end_at.should == nil | |
| + [] | |
| + end | |
| + end | |
| + | |
| + it 'should yield the reporting period after the last one in the cache if… | |
| + reporting_period = Simplabs::ReportsAsSparkline::ReportingPeriod.new( | |
| + @report.options[:grouping], | |
| + Time.now - @report.options[:limit].send(@report.options[:grouping].i… | |
| + ) | |
| cached = Simplabs::ReportsAsSparkline::ReportCache.new | |
| cached.stub!(:reporting_period).and_return(reporting_period.date_time) | |
| - Simplabs::ReportsAsSparkline::ReportCache.stub!(:find).and_return([cac… | |
| + Simplabs::ReportsAsSparkline::ReportCache.stub!(:all).and_return(Array… | |
| Simplabs::ReportsAsSparkline::ReportCache.process(@report, @options) d… | |
| - begin_at.should == reporting_period.next.date_time | |
| + begin_at.should == reporting_period.date_time | |
| end_at.should == nil | |
| [] | |
| end | |
| @@ -57,75 +84,73 @@ describe Simplabs::ReportsAsSparkline::ReportCache do | |
| describe 'with :live_data = false' do | |
| - it 'should not yield to the block if data for the reporting period befor… | |
| - cached = Simplabs::ReportsAsSparkline::ReportCache.new | |
| - cached.stub!(:reporting_period).and_return(Simplabs::ReportsAsSparklin… | |
| - Simplabs::ReportsAsSparkline::ReportCache.stub!(:find).and_return([cac… | |
| + it 'should not yield if all required data could be retrieved from the ca… | |
| + Simplabs::ReportsAsSparkline::ReportCache.stub!(:all).and_return(Array… | |
| + | |
| lambda { | |
| Simplabs::ReportsAsSparkline::ReportCache.process(@report, @report.o… | |
| }.should_not raise_error(YieldMatchException) | |
| end | |
| - it 'should yield to the block if no data for the reporting period before… | |
| + it 'should yield to the block if no data could be retrieved from the cac… | |
| + Simplabs::ReportsAsSparkline::ReportCache.stub!(:all).and_return([]) | |
| + | |
| lambda { | |
| Simplabs::ReportsAsSparkline::ReportCache.process(@report, @report.o… | |
| }.should raise_error(YieldMatchException) | |
| end | |
| - it 'should yield the reporting period after the last one in the cache if… | |
| - reporting_period = Simplabs::ReportsAsSparkline::ReportingPeriod.new( | |
| - @report.options[:grouping], | |
| - Time.now - 3.send(@report.options[:grouping].identifier) | |
| - ) | |
| - cached = Simplabs::ReportsAsSparkline::ReportCache.new | |
| - cached.stub!(:reporting_period).and_return(reporting_period.date_time) | |
| - Simplabs::ReportsAsSparkline::ReportCache.stub!(:find).and_return([cac… | |
| + describe 'with :end_date = <some date>' do | |
| - Simplabs::ReportsAsSparkline::ReportCache.process(@report, @report.opt… | |
| - begin_at.should == reporting_period.next.date_time | |
| - end_at.should == nil | |
| - [] | |
| + before do | |
| + @options = @report.options.merge(:end_date => Time.now) | |
| end | |
| + | |
| + it 'should yield the last date and time of the reporting period for th… | |
| + reporting_period = Simplabs::ReportsAsSparkline::ReportingPeriod.new… | |
| + | |
| + Simplabs::ReportsAsSparkline::ReportCache.process(@report, @options)… | |
| + end_at.should == reporting_period.last_date_time | |
| + [] | |
| + end | |
| + end | |
| + | |
| end | |
| end | |
| it 'should read existing data from the cache' do | |
| - Simplabs::ReportsAsSparkline::ReportCache.should_receive(:find).once.wit… | |
| - :all, | |
| + Simplabs::ReportsAsSparkline::ReportCache.should_receive(:all).once.with( | |
| :conditions => [ | |
| - 'model_name = ? AND report_name = ? AND grouping = ? AND aggregation… | |
| + 'model_name = ? AND report_name = ? AND grouping = ? AND aggregation… | |
| @report.klass.to_s, | |
| @report.name.to_s, | |
| @report.options[:grouping].identifier.to_s, | |
| @report.aggregation.to_s, | |
| - 10, | |
| Simplabs::ReportsAsSparkline::ReportingPeriod.first(@report.options[… | |
| ], | |
| :limit => 10, | |
| :order => 'reporting_period ASC' | |
| - ) | |
| + ).and_return([]) | |
| Simplabs::ReportsAsSparkline::ReportCache.process(@report, @report.optio… | |
| end | |
| it 'should utilize the end_date in the conditions' do | |
| end_date = Time.now | |
| - Simplabs::ReportsAsSparkline::ReportCache.should_receive(:find).once.wit… | |
| - :all, | |
| + Simplabs::ReportsAsSparkline::ReportCache.should_receive(:all).once.with( | |
| :conditions => [ | |
| - 'model_name = ? AND report_name = ? AND grouping = ? AND aggregation… | |
| + 'model_name = ? AND report_name = ? AND grouping = ? AND aggregation… | |
| @report.klass.to_s, | |
| @report.name.to_s, | |
| @report.options[:grouping].identifier.to_s, | |
| @report.aggregation.to_s, | |
| - 10, | |
| Simplabs::ReportsAsSparkline::ReportingPeriod.first(@report.options[… | |
| Simplabs::ReportsAsSparkline::ReportingPeriod.new(@report.options[:g… | |
| ], | |
| :limit => 10, | |
| :order => 'reporting_period ASC' | |
| - ) | |
| + ).and_return([]) | |
| Simplabs::ReportsAsSparkline::ReportCache.process(@report, @report.optio… | |
| end | |
| @@ -135,17 +160,16 @@ describe Simplabs::ReportsAsSparkline::ReportCache do | |
| Simplabs::ReportsAsSparkline::ReportCache.should_receive(:find).once.wit… | |
| :all, | |
| :conditions => [ | |
| - 'model_name = ? AND report_name = ? AND grouping = ? AND aggregation… | |
| + '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, | |
| - 10, | |
| Simplabs::ReportsAsSparkline::ReportingPeriod.first(grouping, 10).da… | |
| ], | |
| :limit => 10, | |
| :order => 'reporting_period ASC' | |
| - ) | |
| + ).and_return([]) | |
| Simplabs::ReportsAsSparkline::ReportCache.process(@report, { :limit => 1… | |
| end | |
| @@ -193,7 +217,6 @@ describe Simplabs::ReportsAsSparkline::ReportCache do | |
| Simplabs::ReportsAsSparkline::ReportCache.should_receive(:build_cached_d… | |
| @report, | |
| @report.options[:grouping], | |
| - 10, | |
| anything(), | |
| 0.0 | |
| ).and_return(@cached) | |
| @@ -205,14 +228,12 @@ describe Simplabs::ReportsAsSparkline::ReportCache do | |
| Simplabs::ReportsAsSparkline::ReportCache.should_receive(:build_cached_d… | |
| @report, | |
| @report.options[:grouping], | |
| - 10, | |
| anything(), | |
| 0.0 | |
| ).and_return(@cached) | |
| Simplabs::ReportsAsSparkline::ReportCache.should_receive(:build_cached_d… | |
| @report, | |
| @report.options[:grouping], | |
| - 10, | |
| @current_reporting_period.previous, | |
| 1.0 | |
| ).and_return(@cached) | |
| diff --git a/spec/classes/report_spec.rb b/spec/classes/report_spec.rb | |
| @@ -321,6 +321,26 @@ describe Simplabs::ReportsAsSparkline::Report do | |
| result[6][1].should == 0.0 | |
| end | |
| + it 'should return correct results when run twice in a row with a… | |
| + @report = Simplabs::ReportsAsSparkline::Report.new(User, :regi… | |
| + :aggregation => :count, | |
| + :grouping => grouping, | |
| + :limit => 10, | |
| + :live_data => live_data | |
| + ) | |
| + result = @report.run(:end_date => Time.now - 1.send(grouping))… | |
| + | |
| + result[9][1].should == 1.0 | |
| + result[8][1].should == 0.0 | |
| + result[7][1].should == 2.0 | |
| + | |
| + result = @report.run(:end_date => Time.now - 3.send(grouping))… | |
| + | |
| + result[9][1].should == 2.0 | |
| + result[8][1].should == 0.0 | |
| + result[7][1].should == 0.0 | |
| + end | |
| + | |
| end | |
| end | |
| diff --git a/spec/classes/reporting_period_spec.rb b/spec/classes/reporting_per… | |
| @@ -245,6 +245,54 @@ describe Simplabs::ReportsAsSparkline::ReportingPeriod do | |
| (reporting_period1 == reporting_period2).should == false | |
| end | |
| + describe 'when invoked with DateTimes or Times' do | |
| + | |
| + describe 'for grouping :hour' do | |
| + | |
| + it 'should return true when the date and hour are equal' do | |
| + date_time = DateTime.new(2008, 10, 30, 12) | |
| + reporting_period = Simplabs::ReportsAsSparkline::ReportingPeriod.new… | |
| + | |
| + reporting_period.should == date_time | |
| + end | |
| + | |
| + end | |
| + | |
| + describe 'for grouping :day' do | |
| + | |
| + it 'should return true when the date is equal' do | |
| + date_time = DateTime.new(2008, 10, 30) | |
| + reporting_period = Simplabs::ReportsAsSparkline::ReportingPeriod.new… | |
| + | |
| + reporting_period.should == date_time | |
| + end | |
| + | |
| + end | |
| + | |
| + describe 'for grouping :week' do | |
| + | |
| + it 'should return true when the date of the first day in that week is … | |
| + date_time = DateTime.new(2009, 5, 4) #monday (first day of the week … | |
| + reporting_period = Simplabs::ReportsAsSparkline::ReportingPeriod.new… | |
| + | |
| + reporting_period.should == DateTime.new(2009, 5, 7) #thursday of sam… | |
| + end | |
| + | |
| + end | |
| + | |
| + describe 'for grouping :month' do | |
| + | |
| + it 'should return true when the date of the first day in that month is… | |
| + date_time = DateTime.new(2009, 5, 1) | |
| + reporting_period = Simplabs::ReportsAsSparkline::ReportingPeriod.new… | |
| + | |
| + reporting_period.should == DateTime.new(2009, 5, 17) | |
| + end | |
| + | |
| + end | |
| + | |
| + end | |
| + | |
| end | |
| describe '.first' do | |
| diff --git a/spec/db/schema.rb b/spec/db/schema.rb | |
| @@ -15,7 +15,6 @@ ActiveRecord::Schema.define(:version => 1) do | |
| t.string :aggregation, :null => false | |
| t.float :value, :null => false, :default => 0 | |
| t.datetime :reporting_period, :null => false | |
| - t.integer :run_limit, :null => false | |
| t.timestamps | |
| end | |
| @@ -23,16 +22,14 @@ ActiveRecord::Schema.define(:version => 1) do | |
| :model_name, | |
| :report_name, | |
| :grouping, | |
| - :aggregation, | |
| - :run_limit | |
| + :aggregation | |
| ], :name => :name_model_grouping_agregation_run_limit | |
| add_index :reports_as_sparkline_cache, [ | |
| :model_name, | |
| :report_name, | |
| :grouping, | |
| :aggregation, | |
| - :reporting_period, | |
| - :run_limit | |
| + :reporting_period | |
| ], :unique => true, :name => :name_model_grouping_aggregation_period_run_lim… | |
| end |