| Merge branch 'master' of git://github.com/saulabs/reportable - reportable - For… | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| commit e2a282013a4ad968a58806bb0fc8e3efbab5bd8d | |
| parent f8532eeb704c511bb75b5bd4fd9656243c9b118b | |
| Author: HD Moore <[email protected]> | |
| Date: Mon, 8 Sep 2014 01:08:56 -0500 | |
| Merge branch 'master' of git://github.com/saulabs/reportable | |
| Conflicts: | |
| lib/saulabs/reportable/report.rb | |
| lib/saulabs/reportable/report_cache.rb | |
| spec/classes/report_spec.rb | |
| Diffstat: | |
| M .travis.yml | 2 ++ | |
| M Gemfile | 14 +++++--------- | |
| M README.md | 1 + | |
| M lib/saulabs/reportable.rb | 2 ++ | |
| M lib/saulabs/reportable/cumulated_r… | 2 +- | |
| M lib/saulabs/reportable/report.rb | 23 +++++++++++++---------- | |
| M lib/saulabs/reportable/report_cach… | 65 +++++++++++++--------------… | |
| M lib/saulabs/reportable/reporting_p… | 2 +- | |
| M reportable.gemspec | 2 +- | |
| M spec/classes/report_cache_spec.rb | 27 +++++++-------------------- | |
| M spec/classes/report_spec.rb | 9 +++++---- | |
| M spec/spec_helper.rb | 10 ++++++++-- | |
| 12 files changed, 74 insertions(+), 85 deletions(-) | |
| --- | |
| diff --git a/.travis.yml b/.travis.yml | |
| @@ -1,4 +1,6 @@ | |
| language: ruby | |
| rvm: | |
| - 1.9.3 | |
| + - 2.0.0 | |
| + - 2.1.1 | |
| script: "rake spec" | |
| diff --git a/Gemfile b/Gemfile | |
| @@ -1,16 +1,12 @@ | |
| source "http://rubygems.org" | |
| -gem 'rails', '~> 3.2.0' | |
| -gem 'activerecord', '~> 3.2.0', :require => 'active_record' | |
| -gem 'activesupport', '~> 3.2.0', :require => 'active_support' | |
| -gem 'actionpack', '~> 3.2.0', :require => 'action_pack' | |
| +gem 'rails', '~> 4.1.0' | |
| +gem 'protected_attributes' | |
| -gem 'sqlite3-ruby', '>= 1.2.0' | |
| -gem 'mysql', '>= 2.8.0' | |
| -gem 'pg', '>= 0.9.0' | |
| -gem 'tzinfo', '>= 0.3.0' | |
| +gem 'sqlite3' | |
| +# gem 'mysql', '>= 2.8.0' | |
| +gem 'pg' | |
| -gem 'rake', '>= 0.8.7' | |
| gem 'rspec', '~> 2.8.0' | |
| gem 'simplecov' | |
| gem 'excellent', '>= 1.5.4' | |
| diff --git a/README.md b/README.md | |
| @@ -1,5 +1,6 @@ | |
| Reportable | |
| ========== | |
| +[](… | |
| Reportable allows for the easy creation of reports based on `ActiveRecord` mod… | |
| diff --git a/lib/saulabs/reportable.rb b/lib/saulabs/reportable.rb | |
| @@ -38,6 +38,8 @@ module Saulabs | |
| # the number of reporting periods to get (see +:grouping+) | |
| # @option options [Hash] :conditions ({}) | |
| # conditions like in +ActiveRecord::Base#find+; only records that ma… | |
| + # @option options [Hash] :include ({}) | |
| + # include like in +ActiveRecord::Base#find+; names associations that… | |
| # @option options [Boolean] :live_data (false) | |
| # specifies whether data for the current reporting period is to be r… | |
| # @option options [DateTime, Boolean] :end_date (false) | |
| diff --git a/lib/saulabs/reportable/cumulated_report.rb b/lib/saulabs/reportabl… | |
| @@ -34,7 +34,7 @@ module Saulabs | |
| def initial_cumulative_value(date, options) | |
| conditions = setup_conditions(nil, date, options[:conditions]) | |
| - @klass.send(@aggregation, @value_column, :conditions => conditions) | |
| + @klass.where(conditions).calculate(@aggregation, @value_column) | |
| end | |
| end | |
| diff --git a/lib/saulabs/reportable/report.rb b/lib/saulabs/reportable/report.rb | |
| @@ -54,6 +54,8 @@ module Saulabs | |
| # the number of reporting periods to get (see +:grouping+) | |
| # @option options [Hash] :conditions ({}) | |
| # conditions like in +ActiveRecord::Base#find+; only records that matc… | |
| + # @option options [Hash] :include ({}) | |
| + # include like in +ActiveRecord::Base#find+; names associations that s… | |
| # @option options [Boolean] :live_data (false) | |
| # specifies whether data for the current reporting period is to be rea… | |
| # @option options [DateTime, Boolean] :end_date (false) | |
| @@ -71,6 +73,7 @@ module Saulabs | |
| @options = { | |
| :limit => options[:limit] || 100, | |
| :distinct => options[:distinct] || false, | |
| + :include => options[:include] || [], | |
| :conditions => options[:conditions] || [], | |
| :grouping => Grouping.new(options[:grouping] || :day), | |
| :live_data => options[:live_data] || false, | |
| @@ -122,14 +125,14 @@ module Saulabs | |
| def read_data(begin_at, end_at, options) | |
| conditions = setup_conditions(begin_at, end_at, options[:conditions]) | |
| - @klass.send(@aggregation, | |
| - @value_column, | |
| - :conditions => conditions, | |
| - :distinct => options[:distinct], | |
| - :group => options[:grouping].to_sql(@date_column), | |
| - :order => "#{options[:grouping].to_sql(@date_column)} ASC", | |
| - :limit => options[:limit] | |
| - ) | |
| + table_name = ActiveRecord::Base.connection.quote_table_name(@klass.t… | |
| + date_column = ActiveRecord::Base.connection.quote_column_name(@date_… | |
| + grouping = options[:grouping].to_sql("#{table_name}.#{date_column}") | |
| + order = "#{grouping} ASC" | |
| + | |
| + @klass.where(conditions).includes(options[:include]).distinct(option… | |
| + group(grouping).order(order).limit(options[:limit]). | |
| + calculate(@aggregation, @value_column) | |
| end | |
| def setup_conditions(begin_at, end_at, custom_conditions = []) | |
| @@ -153,13 +156,13 @@ module Saulabs | |
| case context | |
| when :initialize | |
| options.each_key do |k| | |
| - raise ArgumentError.new("Invalid option #{k}!") unless [:limit… | |
| + raise ArgumentError.new("Invalid option #{k}!") unless [:limit… | |
| end | |
| raise ArgumentError.new("Invalid aggregation #{options[:aggregat… | |
| 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('Options :live_data and :end_date may not bo… | |
| diff --git a/lib/saulabs/reportable/report_cache.rb b/lib/saulabs/reportable/re… | |
| @@ -20,7 +20,7 @@ module Saulabs | |
| validates_presence_of :value | |
| validates_presence_of :reporting_period | |
| - attr_accessible :model_name, :report_name, :grouping, :aggregation, :val… | |
| + # attr_accessible :model_name, :report_name, :grouping, :aggregation, :v… | |
| self.skip_time_zone_conversion_for_attributes = [:reporting_period] | |
| @@ -40,10 +40,7 @@ module Saulabs | |
| # 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 | |
| - }) | |
| + self.where(model_name: klass.name, report_name: report.to_s).delete_all | |
| end | |
| # Processes the report using the respective cache. | |
| @@ -90,21 +87,20 @@ module Saulabs | |
| private | |
| def self.prepare_result(new_data, cached_data, report, options) | |
| - new_data = new_data.map { |data| [ReportingPeriod.from_db_string(opt… | |
| - cached_data.map! { |cached| [ReportingPeriod.new(options[:grouping],… | |
| + new_data = new_data.to_a.map { |data| [ReportingPeriod.from_db_strin… | |
| + cached_data.to_a.map! { |cached| [ReportingPeriod.new(options[:group… | |
| current_reporting_period = ReportingPeriod.new(options[:grouping]) | |
| reporting_period = get_first_reporting_period(options) | |
| result = [] | |
| while reporting_period < (options[:end_date] ? ReportingPeriod.new(o… | |
| if options[:cacheable] and cached = cached_data.find { |cached| re… | |
| result << [cached[0].date_time, cached[1]] | |
| + elsif reporting_period.last_date_time.past? | |
| + new_cached = build_cached_data(report, options[:grouping], optio… | |
| + new_cached.save! | |
| + result << [reporting_period.date_time, new_cached.value] | |
| else | |
| - value = find_value(new_data, reporting_period) | |
| - if options[:cacheable] | |
| - new_cached = build_cached_data(report, options[:grouping], opt… | |
| - new_cached.save! if options[:cacheable] | |
| - end | |
| - result << [reporting_period.date_time, value] | |
| + result << [reporting_period.date_time, find_value(new_data, repo… | |
| end | |
| reporting_period = reporting_period.next | |
| end | |
| @@ -142,36 +138,31 @@ module Saulabs | |
| end | |
| def self.read_cached_data(report, options) | |
| - return [] if not options[:cacheable] | |
| - options[:conditions] ||= [] | |
| - conditions = [ | |
| - %w(model_name report_name grouping aggregation conditions).map do … | |
| - "#{self.connection.quote_column_name(column_name)} = ?" | |
| - end.join(' AND '), | |
| - report.klass.to_s, | |
| - report.name.to_s, | |
| - options[:grouping].identifier.to_s, | |
| - report.aggregation.to_s, | |
| - serialize_conditions(options[:conditions]) | |
| - ] | |
| - first_reporting_period = get_first_reporting_period(options) | |
| + conditions = build_conditions_for_reading_cached_data(report, option… | |
| + conditions.limit(options[:limit]).order('reporting_period ASC') | |
| + end | |
| + | |
| + def self.build_conditions_for_reading_cached_data(report, options) | |
| + start_date = get_first_reporting_period(options).date_time | |
| + | |
| + conditions = where('reporting_period >= ?', start_date).where( | |
| + model_name: report.klass.to_s, | |
| + report_name: report.name.to_s, | |
| + grouping: options[:grouping].identifier.to_s, | |
| + aggregation: report.aggregation.to_s, | |
| + conditions: serialize_conditions(options[:conditions] || []) | |
| + ) | |
| + | |
| if options[:end_date] | |
| - conditions.first << ' AND reporting_period BETWEEN ? AND ?' | |
| - conditions << first_reporting_period.date_time | |
| - conditions << ReportingPeriod.new(options[:grouping], options[:end… | |
| + end_date = ReportingPeriod.new(options[:grouping], options[:end_da… | |
| + conditions.where('reporting_period <= ?', end_date) | |
| else | |
| - conditions.first << ' AND reporting_period >= ?' | |
| - conditions << first_reporting_period.date_time | |
| + conditions | |
| end | |
| - self.all( | |
| - :conditions => conditions, | |
| - :limit => options[:limit], | |
| - :order => 'reporting_period ASC' | |
| - ) | |
| end | |
| def self.read_new_data(cached_data, options, &block) | |
| - return [] if !options[:live_data] && cached_data.length == options[:… | |
| + return [] if !options[:live_data] && cached_data.size == options[:li… | |
| first_reporting_period_to_read = get_first_reporting_period_to_read(… | |
| last_reporting_period_to_read = options[:end_date] ? ReportingPeriod… | |
| diff --git a/lib/saulabs/reportable/reporting_period.rb b/lib/saulabs/reportabl… | |
| @@ -70,7 +70,7 @@ module Saulabs | |
| # the reporting period for the {Saulabs::Reportable::Grouping} as pars… | |
| # | |
| def self.from_db_string(grouping, db_string) | |
| - return self.new(grouping, db_string) if db_string.is_a?(Date) | |
| + return self.new(grouping, db_string) if db_string.is_a?(Date) || db_st… | |
| parts = grouping.date_parts_from_db_string(db_string.to_s) | |
| case grouping.identifier | |
| when :hour | |
| diff --git a/reportable.gemspec b/reportable.gemspec | |
| @@ -14,7 +14,7 @@ pkg_files += Dir['spec/**/*.{rb,yml,opts}'] | |
| Gem::Specification.new do |s| | |
| s.name = %q{reportable} | |
| - s.version = '1.2.0' | |
| + s.version = '1.3.1' | |
| s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to?(… | |
| s.authors = ['Marco Otte-Witte', 'Martin Kavalar'] | |
| diff --git a/spec/classes/report_cache_spec.rb b/spec/classes/report_cache_spec… | |
| @@ -91,19 +91,6 @@ describe Saulabs::Reportable::ReportCache do | |
| end | |
| - describe '.clear_for' do | |
| - | |
| - it 'should delete all entries in the cache for the klass and report name' … | |
| - Saulabs::Reportable::ReportCache.should_receive(:delete_all).once.with(:… | |
| - :model_name => User.name, | |
| - :report_name => 'registrations' | |
| - }) | |
| - | |
| - Saulabs::Reportable::ReportCache.clear_for(User, :registrations) | |
| - end | |
| - | |
| - end | |
| - | |
| describe '.process' do | |
| before do | |
| @@ -136,7 +123,7 @@ describe Saulabs::Reportable::ReportCache do | |
| end | |
| it 'should yield the first reporting period if not all required data cou… | |
| - Saulabs::Reportable::ReportCache.stub!(:all).and_return([Saulabs::Repo… | |
| + Saulabs::Reportable::ReportCache.stub!(:read_cached_data).and_return([… | |
| Saulabs::Reportable::ReportCache.process(@report, @options) do |begin_… | |
| begin_at.should == Saulabs::Reportable::ReportingPeriod.first(@repor… | |
| @@ -152,7 +139,7 @@ describe Saulabs::Reportable::ReportCache do | |
| ) | |
| cached = Saulabs::Reportable::ReportCache.new | |
| cached.stub!(:reporting_period).and_return(reporting_period.date_time) | |
| - Saulabs::Reportable::ReportCache.stub!(:all).and_return(Array.new(@rep… | |
| + Saulabs::Reportable::ReportCache.stub!(:read_cached_data).and_return(A… | |
| Saulabs::Reportable::ReportCache.process(@report, @options) do |begin_… | |
| begin_at.should == reporting_period.date_time | |
| @@ -166,7 +153,7 @@ describe Saulabs::Reportable::ReportCache do | |
| describe 'with :live_data = false' do | |
| it 'should not yield if all required data could be retrieved from the ca… | |
| - Saulabs::Reportable::ReportCache.stub!(:all).and_return(Array.new(@rep… | |
| + Saulabs::Reportable::ReportCache.stub!(:read_cached_data).and_return(A… | |
| lambda { | |
| Saulabs::Reportable::ReportCache.process(@report, @report.options) {… | |
| @@ -174,7 +161,7 @@ describe Saulabs::Reportable::ReportCache do | |
| end | |
| it 'should yield to the block if no data could be retrieved from the cac… | |
| - Saulabs::Reportable::ReportCache.stub!(:all).and_return([]) | |
| + Saulabs::Reportable::ReportCache.stub!(:read_cached_data).and_return([… | |
| lambda { | |
| Saulabs::Reportable::ReportCache.process(@report, @report.options) {… | |
| @@ -200,7 +187,7 @@ describe Saulabs::Reportable::ReportCache do | |
| end | |
| - it 'should read existing data from the cache' do | |
| + xit 'should read existing data from the cache' do | |
| Saulabs::Reportable::ReportCache.should_receive(:all).once.with( | |
| :conditions => [ | |
| %w(model_name report_name grouping aggregation conditions).map do |c… | |
| @@ -220,7 +207,7 @@ describe Saulabs::Reportable::ReportCache do | |
| Saulabs::Reportable::ReportCache.process(@report, @report.options) { [] } | |
| end | |
| - it 'should utilize the end_date in the conditions' do | |
| + xit 'should utilize the end_date in the conditions' do | |
| end_date = Time.now - 1.send(@report.options[:grouping].identifier) | |
| Saulabs::Reportable::ReportCache.should_receive(:all).once.with( | |
| :conditions => [ | |
| @@ -242,7 +229,7 @@ describe Saulabs::Reportable::ReportCache do | |
| Saulabs::Reportable::ReportCache.process(@report, @report.options.merge(… | |
| end | |
| - it "should read existing data from the cache for the correct grouping if o… | |
| + xit "should read existing data from the cache for the correct grouping if … | |
| grouping = Saulabs::Reportable::Grouping.new(:month) | |
| Saulabs::Reportable::ReportCache.should_receive(:all).once.with( | |
| :conditions => [ | |
| diff --git a/spec/classes/report_spec.rb b/spec/classes/report_spec.rb | |
| @@ -21,7 +21,8 @@ describe Saulabs::Reportable::Report do | |
| it 'should process the data with the report cache' do | |
| Saulabs::Reportable::ReportCache.should_receive(:process).once.with( | |
| @report, | |
| - { :limit => 100, :grouping => @report.options[:grouping], :conditions … | |
| + | |
| + { :limit => 100, :grouping => @report.options[:grouping], :conditions … | |
| ) | |
| @report.run | |
| @@ -30,7 +31,7 @@ describe Saulabs::Reportable::Report do | |
| it 'should process the data with the report cache when custom conditions a… | |
| Saulabs::Reportable::ReportCache.should_receive(:process).once.with( | |
| @report, | |
| - { :limit => 100, :grouping => @report.options[:grouping], :conditions … | |
| + { :limit => 100, :grouping => @report.options[:grouping], :conditions … | |
| ) | |
| @report.run(:conditions => { :some => :condition }) | |
| @@ -47,7 +48,7 @@ describe Saulabs::Reportable::Report do | |
| Saulabs::Reportable::Grouping.should_receive(:new).once.with(:month).and… | |
| Saulabs::Reportable::ReportCache.should_receive(:process).once.with( | |
| @report, | |
| - { :limit => 100, :grouping => grouping, :conditions => [], :live_data … | |
| + { :limit => 100, :grouping => grouping, :conditions => [], :live_data … | |
| ) | |
| @report.run(:grouping => :month) | |
| @@ -581,7 +582,7 @@ describe Saulabs::Reportable::Report do | |
| describe '#read_data' do | |
| - it 'should invoke the aggregation method on the model' do | |
| + xit 'should invoke the aggregation method on the model' do | |
| @report = Saulabs::Reportable::Report.new(User, :registrations, :aggrega… | |
| User.should_receive(:count).once.and_return([]) | |
| diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb | |
| @@ -27,8 +27,14 @@ require File.join(ROOT, 'lib', 'saulabs', 'reportable.rb') | |
| # config.time_zone = 'Pacific Time (US & Canada)' | |
| # end | |
| -FileUtils.mkdir_p File.join(File.dirname(__FILE__), 'log') | |
| -ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(__F… | |
| +# FileUtils.mkdir_p File.join(File.dirname(__FILE__), 'log') | |
| +# ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.dirname(_… | |
| + | |
| +RSpec.configure do |config| | |
| + config.filter_run :focus => true | |
| + config.run_all_when_everything_filtered = true | |
| +end | |
| +ActiveRecord::Base.default_timezone = :local | |
| databases = YAML::load(IO.read(File.join(File.dirname(__FILE__), 'db', 'databa… | |
| ActiveRecord::Base.establish_connection(databases[ENV['DB'] || 'sqlite3']) |