fnm-statistics 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *~
2
+ *.swp
3
+ *.swo
4
+ .DS_Store
5
+ debug.log
6
+ coverage
7
+ coverage.data
8
+ pkg/
9
+ spec/database.yml
10
+ spec/*.log
data/CHANGES.markdown ADDED
@@ -0,0 +1,2 @@
1
+ Version 0.1, 27.05.09 - initial release
2
+ Version 0.1.2, 12.02.10 - MySQL avg fix
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009 Alexandru Catighera
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,126 @@
1
+ # Statistics
2
+
3
+ This ActiverRecord plugin allows you to easily define and pull statistics for AR models. This plugin was built with reporting in mind.
4
+
5
+ ## Installation
6
+ gem install statistics
7
+ OR
8
+ script/plugin install git://github.com/acatighera/statistics.git
9
+
10
+ ## Examples
11
+ #### Defining statistics is similar to defining named scopes. Strings and symbols both work as names.
12
+
13
+ class Account < ActiveRecord::Base
14
+ define_statistic :user_count, :count => :all
15
+ define_statistic :average_age, :average => :all, :column_name => 'age'
16
+ define_statistic 'subscriber count', :count => :all, :conditions => "subscription_opt_in = 1"
17
+ end
18
+
19
+ class Donations < ActiveRecord::Base
20
+ define_statistic :total_donations, :sum => :all, :column_name => "amount"
21
+ end
22
+
23
+ #### Actually pulling the numbers is simple:
24
+
25
+ #####for all stats
26
+
27
+ Account.statistics # returns { :user_count => 120, :average_age => 28, 'subscriber count' => 74 }
28
+
29
+ #####for a single stat
30
+
31
+ Account.get_stat(:user_count) # returns 120
32
+
33
+ ### Here are some additional benefits of using this plugin:
34
+
35
+ #### Easily Filter
36
+
37
+ Note: I found filtering to be an important part of reporting (ie. filtering by date). All filters are optional so even if you define them you don’t have to use them when pulling data. Using the `filter_all_stats_on` method and `:joins` options you can make things filterable by the same things which I found to be extremely useful.
38
+
39
+ class Account < ActiveRecord::Base
40
+ define_statistic :user_count, :count => :all, :filter_on => { :state => 'state = ?', :created_after => 'DATE(created_at) > ?'}
41
+ define_statistic :subscriber_count, :count => :all, :conditions => "subscription_opt_in = true"
42
+
43
+ filter_all_stats_on(:account_type, "account_type = ?")
44
+ end
45
+
46
+ Account.statistics(:account_type => 'non-admin')
47
+ Account.get_stat(:user_count, :account_type => 'non-admin', :created_after => ‘2009-01-01’, :state => 'NY')
48
+
49
+ # NOTE: filters are optional (ie. no filters will be applied if none are passed in)
50
+ Account.get_stat(:user_count)
51
+
52
+ #### Caching
53
+
54
+ This is a new feature that uses `Rails.cache`. You can cache certain statistics for a specified amount of time (see below). By default caching is disabled if you do not pass in the `:cache_for` option. It is also important to note that caching is scoped by filters, there is no way around this since different filters produce different values.
55
+ class Account < ActiveRecord::Base
56
+ define_statistic :user_count, :count => :all, :cache_for => 30.minutes, :filter_on { :state => 'state = ?' }
57
+ end
58
+
59
+ Account.statistics(:state => 'NY') # This call generates a SQL query
60
+
61
+ Account.statistics(:state => 'NY') # This call and subsequent calls for the next 30 minutes will use the cached value
62
+
63
+ Account.statistics(:state => 'PA') # This call generates a SQL query because the user count for NY and PA could be different (and probably is)
64
+
65
+ Note: If you want Rails.cache to work properly, you need to use mem_cache_store in your rails enviroment file (ie. `config.cache_store = :mem_cache_store` in your enviroment.rb file).
66
+
67
+ #### Standardized
68
+
69
+ All ActiveRecord classes now respond to `statistics` and `get_stat` methods
70
+
71
+ all_stats = []
72
+ [ Account, Post, Comment ].each do |ar|
73
+ all_stats << ar.statistics
74
+ end
75
+
76
+ #### Calculated statistics (DRY)
77
+
78
+ You can define calculated metrics in order to perform mathematical calculations on one or more defined statistics.
79
+
80
+ class Account < ActiveRecord::Base
81
+ has_many :donations
82
+
83
+ define_statistic :user_count, :count => :all
84
+ define_statistic :total_donations, :sum => :all, :column_name => 'donations.amount', :joins => :donations
85
+
86
+ define_calculated_statistic :average_donation_per_user do
87
+ defined_stats(:total_donations) / defined_stats(:user_count)
88
+ end
89
+
90
+ filter_all_stats_on(:account_type, "account_type = ?")
91
+ filter_all_stats_on(:state, "state = ?")
92
+ filter_all_stats_on(:created_after, "DATE(created_at) > ?")
93
+ end
94
+
95
+
96
+ Pulling stats for calculated metrics is the same as for regular statistics. They also work with filters like regular statistics!
97
+
98
+ Account.get_stat(:average_donation_per_user, :account_type => 'non-admin', :state => 'NY')
99
+ Account.get_stat(:average_donation_per_user, :created_after => '2009-01-01')
100
+
101
+ #### Reuse scopes you already have defined
102
+
103
+ You can reuse the code you have written to do reporting.
104
+
105
+ class Account < ActiveRecord::Base
106
+ has_many :posts
107
+
108
+ named_scope :not_admins, :conditions => “account_type = ‘non-admin’”
109
+ named_scope :accounts_with_posts, :joins => :posts
110
+ named_scope :with_account_type, lambda{|types| types = [types].compact.flatten; types.size > 0 && {:conditions => ['account_type in (?)', types]} || {}}
111
+
112
+ define_statistic :active_users_count, :count => [:not_admins, :accounts_with_posts]
113
+ define_statistic :active_staff_and_admin_count, :count => [:accounts_with_posts, [:with_account_type, 'staff', 'admin']]
114
+ end
115
+
116
+ #### Accepts all ActiveRecord::Calculations options
117
+
118
+ The `:conditions` and `:joins` options are all particularly useful
119
+
120
+ class Account < ActiveRecord::Base
121
+ has_many :posts
122
+
123
+ define_statistic :active_users_count, :count => :all, :joins => :posts, :conditions => "account_type = 'non-admin'"
124
+ end
125
+
126
+ ###### Copyright (c) 2009 Alexandru Catighera, released under MIT license
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "statistics"
5
+ gemspec.summary = "An ActiveRecord gem that makes it easier to do reporting."
6
+ gemspec.email = "acatighera@gmail.com"
7
+ gemspec.homepage = "http://github.com/acatighera/statistics"
8
+ gemspec.authors = ["Alexandru Catighera"]
9
+ end
10
+ rescue LoadError
11
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
12
+ end
13
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'statistics')
data/lib/statistics.rb ADDED
@@ -0,0 +1,187 @@
1
+ module Statistics
2
+ class << self
3
+ def included(base)
4
+ base.extend(HasStats)
5
+ end
6
+
7
+ def default_filters(filters)
8
+ ActiveRecord::Base.instance_eval { @filter_all_on = filters }
9
+ end
10
+
11
+ def supported_calculations
12
+ [:average, :count, :maximum, :minimum, :sum]
13
+ end
14
+ end
15
+
16
+ # This extension provides the ability to define statistics for reporting purposes
17
+ module HasStats
18
+
19
+ # OPTIONS:
20
+ #
21
+ #* +average+, +count+, +sum+, +maximum+, +minimum+ - Only one of these keys is passed, which
22
+ # one depends on the type of operation. The value is an array of named scopes to scope the
23
+ # operation by (+:all+ should be used if no scopes are to be applied)
24
+ #* +column_name+ - The SQL column to perform the operation on (default: +id+)
25
+ #* +filter_on+ - A hash with keys that represent filters. The with values in the has are rules
26
+ # on how to generate the query for the correspond filter.
27
+ #* +cached_for+ - A duration for how long to cache this specific statistic
28
+ #
29
+ # Additional options can also be passed in that would normally be passed to an ActiveRecord
30
+ # +calculate+ call, like +conditions+, +joins+, etc
31
+ #
32
+ # EXAMPLE:
33
+ #
34
+ # class MockModel < ActiveRecord::Base
35
+ #
36
+ # named_scope :my_scope, :conditions => 'value > 5'
37
+ # named_scope :my_scope_with_lambda, lambda {|val| {:conditions => ['value > ?', val]}}
38
+ #
39
+ # define_statistic "Basic Count", :count => :all
40
+ # define_statistic "Basic Sum", :sum => :all, :column_name => 'amount'
41
+ # define_statistic "Chained Scope Count", :count => [:all, :my_scope]
42
+ # define_statistic "Chained Scope Count with Lambda", :count => [:all, [:my_scope_with_lambda, 3]]
43
+ # define_statistic "Default Filter", :count => :all
44
+ # define_statistic "Custom Filter", :count => :all, :filter_on => { :channel => 'channel = ?', :start_date => 'DATE(created_at) > ?' }
45
+ # define_statistic "Cached", :count => :all, :filter_on => { :channel => 'channel = ?', :blah => 'blah = ?' }, :cache_for => 1.second
46
+ # end
47
+ def define_statistic(name, options)
48
+ method_name = name.to_s.gsub(" ", "").underscore + "_stat"
49
+
50
+ @statistics ||= {}
51
+ @filter_all_on ||= ActiveRecord::Base.instance_eval { @filter_all_on }
52
+ @statistics[name] = method_name
53
+
54
+ options = { :column_name => :id }.merge(options)
55
+
56
+ calculation = options.keys.find {|opt| Statistics::supported_calculations.include?(opt)}
57
+ # replace :average with :avg to work with mysql
58
+ calculation = :avg if calculation == :average
59
+ calculation ||= :count
60
+
61
+ # We must use the metaclass here to metaprogrammatically define a class method
62
+ (class<<self; self; end).instance_eval do
63
+ define_method(method_name) do |filters|
64
+ # check the cache before running a query for the stat
65
+ cached_val = Rails.cache.read("#{self.name}#{method_name}#{filters}") if options[:cache_for]
66
+ return cached_val unless cached_val.nil?
67
+
68
+ scoped_options = Marshal.load(Marshal.dump(options))
69
+
70
+ filters.each do |key, value|
71
+ if value
72
+ sql = ((@filter_all_on || {}).merge(scoped_options[:filter_on] || {}))[key].gsub("?", "'#{value}'")
73
+ sql = sql.gsub("%t", "#{table_name}")
74
+ sql_frag = send(:sanitize_sql_for_conditions, sql)
75
+ case
76
+ when sql_frag.nil? : nil
77
+ when scoped_options[:conditions].nil? : scoped_options[:conditions] = sql_frag
78
+ when scoped_options[:conditions].is_a?(Array) : scoped_options[:conditions][0].concat(" AND #{sql_frag}")
79
+ when scoped_options[:conditions].is_a?(String) : scoped_options[:conditions].concat(" AND #{sql_frag}")
80
+ end
81
+ end
82
+ end if filters.is_a?(Hash)
83
+
84
+ base = self
85
+ # chain named scopes
86
+ scopes = Array(scoped_options[calculation])
87
+ scopes.each do |scope|
88
+ scope = [scope].flatten
89
+
90
+ base = base.send(scope[0], scope[1...scope.size])
91
+ end if scopes != [:all]
92
+ stat_value = base.calculate(calculation, scoped_options[:column_name], sql_options(scoped_options))
93
+
94
+ # cache stat value
95
+ Rails.cache.write("#{self.name}#{method_name}#{filters}", stat_value, :expires_in => options[:cache_for]) if options[:cache_for]
96
+
97
+ stat_value
98
+ end
99
+ end
100
+ end
101
+
102
+ # Defines a statistic using a block that has access to all other defined statistics
103
+ #
104
+ # EXAMPLE:
105
+ # class MockModel < ActiveRecord::Base
106
+ # define_statistic "Basic Count", :count => :all
107
+ # define_statistic "Basic Sum", :sum => :all, :column_name => 'amount'
108
+ # define_calculated_statistic "Total Profit"
109
+ # defined_stats('Basic Sum') * defined_stats('Basic Count')
110
+ # end
111
+ def define_calculated_statistic(name, &block)
112
+ method_name = name.to_s.gsub(" ", "").underscore + "_stat"
113
+
114
+ @statistics ||= {}
115
+ @statistics[name] = method_name
116
+
117
+ (class<<self; self; end).instance_eval do
118
+ define_method(method_name) do |filters|
119
+ @filters = filters
120
+ yield
121
+ end
122
+ end
123
+ end
124
+
125
+ # returns an array containing the names/keys of all defined statistics
126
+ def statistics_keys
127
+ @statistics.keys
128
+ end
129
+
130
+ # Calculates all the statistics defined for this AR class and returns a hash with the values.
131
+ # There is an optional parameter that is a hash of all values you want to filter by.
132
+ #
133
+ # EXAMPLE:
134
+ # MockModel.statistics
135
+ # MockModel.statistics(:user_type => 'registered', :user_status => 'active')
136
+ def statistics(filters = {}, except = nil)
137
+ (@statistics || {}).inject({}) do |stats_hash, stat|
138
+ stats_hash[stat.first] = send(stat.last, filters) if stat.last != except
139
+ stats_hash
140
+ end
141
+ end
142
+
143
+ # returns a single statistic based on the +stat_name+ paramater passed in and
144
+ # similarly to the +statistics+ method, it also can take filters.
145
+ #
146
+ # EXAMPLE:
147
+ # MockModel.get_stat('Basic Count')
148
+ # MockModel.get_stat('Basic Count', :user_type => 'registered', :user_status => 'active')
149
+ def get_stat(stat_name, filters = {})
150
+ send(@statistics[stat_name], filters) if @statistics[stat_name]
151
+ end
152
+
153
+ # to keep things DRY anything that all statistics need to be filterable by can be defined
154
+ # seperatly using this method
155
+ #
156
+ # EXAMPLE:
157
+ #
158
+ # class MockModel < ActiveRecord::Base
159
+ # define_statistic "Basic Count", :count => :all
160
+ # define_statistic "Basic Sum", :sum => :all, :column_name => 'amount'
161
+ #
162
+ # filter_all_stats_on(:user_id, "user_id = ?")
163
+ # end
164
+ def filter_all_stats_on(name, cond)
165
+ @filter_all_on ||= {}
166
+ @filter_all_on[name] = cond
167
+ end
168
+
169
+ private
170
+
171
+ def defined_stats(name)
172
+ get_stat(name, @filters)
173
+ end
174
+
175
+ def sql_options(options)
176
+ Statistics::supported_calculations.each do |deletable|
177
+ options.delete(deletable)
178
+ end
179
+ options.delete(:column_name)
180
+ options.delete(:filter_on)
181
+ options.delete(:cache_for)
182
+ options
183
+ end
184
+ end
185
+ end
186
+
187
+ ActiveRecord::Base.send(:include, Statistics)
@@ -0,0 +1,48 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{fnm-statistics}
8
+ s.version = "0.1.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Alexandru Catighera"]
12
+ s.date = %q{2009-12-16}
13
+ s.email = %q{acatighera@gmail.com}
14
+ s.extra_rdoc_files = [
15
+ "README.markdown"
16
+ ]
17
+ s.files = [
18
+ ".gitignore",
19
+ "CHANGES.markdown",
20
+ "MIT-LICENSE",
21
+ "README.markdown",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "init.rb",
25
+ "lib/statistics.rb",
26
+ "statistics.gemspec",
27
+ "test/statistics_test.rb"
28
+ ]
29
+ s.homepage = %q{http://github.com/paulspringett/statistics}
30
+ s.rdoc_options = ["--charset=UTF-8"]
31
+ s.require_paths = ["lib"]
32
+ s.rubygems_version = %q{1.3.5}
33
+ s.summary = %q{An ActiveRecord gem that makes it easier to do reporting.}
34
+ s.test_files = [
35
+ "test/statistics_test.rb"
36
+ ]
37
+
38
+ if s.respond_to? :specification_version then
39
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
43
+ else
44
+ end
45
+ else
46
+ end
47
+ end
48
+
@@ -0,0 +1,155 @@
1
+ require 'test/unit'
2
+
3
+ require 'rubygems'
4
+ gem 'activerecord', '>= 1.15.4.7794'
5
+ gem 'mocha', '>= 0.9.0'
6
+ require 'active_record'
7
+ require 'active_support'
8
+ require 'mocha'
9
+
10
+ require "#{File.dirname(__FILE__)}/../init"
11
+
12
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
13
+
14
+ class Rails
15
+ def self.cache
16
+ ActiveSupport::Cache::MemCacheStore.new
17
+ end
18
+ end
19
+
20
+ class StatisticsTest < Test::Unit::TestCase
21
+
22
+ class BasicModel < ActiveRecord::Base
23
+ define_statistic :basic_num, :count => :all
24
+ end
25
+
26
+ class MockModel < ActiveRecord::Base
27
+ define_statistic "Basic Count", :count => :all
28
+ define_statistic :symbol_count, :count => :all
29
+ define_statistic "Basic Sum", :sum => :all, :column_name => 'amount'
30
+ define_statistic "Chained Scope Count", :count => [:all, :named_scope]
31
+ define_statistic "Chained Scope with Lambda", :count => [:all, [:named_scope_with_lambda, 4]]
32
+ define_statistic "Default Filter", :count => :all
33
+ define_statistic "Custom Filter", :count => :all, :filter_on => { :channel => 'channel = ?', :start_date => 'DATE(created_at) > ?', :blah => 'blah = ?' }
34
+ define_statistic "Cached", :count => :all, :filter_on => { :channel => 'channel = ?', :blah => 'blah = ?' }, :cache_for => 1.second
35
+
36
+ define_calculated_statistic "Total Amount" do
37
+ defined_stats('Basic Sum') * defined_stats('Basic Count')
38
+ end
39
+
40
+ filter_all_stats_on(:user_id, "user_id = ?")
41
+ end
42
+
43
+ def test_basic
44
+ BasicModel.expects(:basic_num_stat).returns(1)
45
+ assert_equal({ :basic_num => 1 }, BasicModel.statistics)
46
+ end
47
+
48
+ def test_statistics
49
+ MockModel.expects(:basic_count_stat).returns(2)
50
+ MockModel.expects(:symbol_count_stat).returns(2)
51
+ MockModel.expects(:basic_sum_stat).returns(27)
52
+ MockModel.expects(:chained_scope_count_stat).returns(4)
53
+ MockModel.expects(:chained_scope_with_lambda_stat).return(8)
54
+ MockModel.expects(:default_filter_stat).returns(5)
55
+ MockModel.expects(:custom_filter_stat).returns(3)
56
+ MockModel.expects(:cached_stat).returns(9)
57
+ MockModel.expects(:total_amount_stat).returns(54)
58
+
59
+ ["Basic Count",
60
+ :symbol_count,
61
+ "Basic Sum",
62
+ "Chained Scope Count",
63
+ "Chained Scope with Lambda",
64
+ "Default Filter",
65
+ "Custom Filter",
66
+ "Cached",
67
+ "Total Amount"].each do |key|
68
+ assert MockModel.statistics_keys.include?(key)
69
+ end
70
+
71
+ assert_equal({ "Basic Count" => 2,
72
+ :symbol_count => 2,
73
+ "Basic Sum" => 27,
74
+ "Chained Scope Count" => 4,
75
+ "Chained Scope with Lambda" => 8,
76
+ "Default Filter" => 5,
77
+ "Custom Filter" => 3,
78
+ "Cached" => 9,
79
+ "Total Amount" => 54 }, MockModel.statistics)
80
+ end
81
+
82
+ def test_get_stat
83
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(3)
84
+ assert_equal 3, MockModel.get_stat("Basic Count")
85
+
86
+ MockModel.expects(:calculate).with(:count, :id, { :conditions => "user_id = '54321'"}).returns(4)
87
+ assert_equal 4, MockModel.get_stat("Basic Count", :user_id => 54321)
88
+ end
89
+
90
+ def test_basic_stat
91
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(3)
92
+ assert_equal 3, MockModel.basic_count_stat({})
93
+
94
+ MockModel.expects(:calculate).with(:sum, 'amount', {}).returns(31)
95
+ assert_equal 31, MockModel.basic_sum_stat({})
96
+ end
97
+
98
+ def test_chained_scope_stat
99
+ MockModel.expects(:all).returns(MockModel)
100
+ MockModel.expects(:named_scope).returns(MockModel)
101
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(5)
102
+ assert_equal 5, MockModel.chained_scope_count_stat({})
103
+ end
104
+
105
+ def test_calculated_stat
106
+ MockModel.expects(:basic_count_stat).returns(3)
107
+ MockModel.expects(:basic_sum_stat).returns(33)
108
+
109
+ assert_equal 99, MockModel.total_amount_stat({})
110
+
111
+ MockModel.expects(:basic_count_stat).with(:user_id => 5).returns(2)
112
+ MockModel.expects(:basic_sum_stat).with(:user_id => 5).returns(25)
113
+
114
+ assert_equal 50, MockModel.total_amount_stat({:user_id => 5})
115
+
116
+ MockModel.expects(:basic_count_stat).with(:user_id => 6).returns(3)
117
+ MockModel.expects(:basic_sum_stat).with(:user_id => 6).returns(60)
118
+
119
+ assert_equal 180, MockModel.total_amount_stat({:user_id => 6})
120
+ end
121
+
122
+ def test_default_filter_stat
123
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(8)
124
+ assert_equal 8, MockModel.default_filter_stat({})
125
+
126
+ MockModel.expects(:calculate).with(:count, :id, { :conditions => "user_id = '12345'" }).returns(2)
127
+ assert_equal 2, MockModel.default_filter_stat( :user_id => '12345' )
128
+ end
129
+
130
+ def test_custom_filter_stat
131
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(6)
132
+ assert_equal 6, MockModel.custom_filter_stat({})
133
+
134
+ MockModel.expects(:calculate).with() do |param1, param2, param3|
135
+ param1 == :count &&
136
+ param2 == :id &&
137
+ (param3 == { :conditions => "channel = 'chan5' AND DATE(created_at) > '#{Date.today.to_s(:db)}'" } ||
138
+ param3 == { :conditions => "DATE(created_at) > '#{Date.today.to_s(:db)}' AND channel = 'chan5'" } )
139
+ end.returns(3)
140
+ assert_equal 3, MockModel.custom_filter_stat(:channel => 'chan5', :start_date => Date.today.to_s(:db))
141
+ end
142
+
143
+ def test_cached_stat
144
+ MockModel.expects(:calculate).returns(6)
145
+ assert_equal 6, MockModel.cached_stat({:channel => 'chan5'})
146
+
147
+ MockModel.stubs(:calculate).returns(8)
148
+ assert_equal 6, MockModel.cached_stat({:channel => 'chan5'})
149
+ assert_equal 8, MockModel.cached_stat({})
150
+
151
+ sleep(1)
152
+ assert_equal 8, MockModel.cached_stat({:channel => 'chan5'})
153
+ end
154
+
155
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fnm-statistics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Alexandru Catighera
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-16 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: acatighera@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ files:
25
+ - .gitignore
26
+ - CHANGES.markdown
27
+ - MIT-LICENSE
28
+ - README.markdown
29
+ - Rakefile
30
+ - VERSION
31
+ - init.rb
32
+ - lib/statistics.rb
33
+ - statistics.gemspec
34
+ - test/statistics_test.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/paulspringett/statistics
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.5
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: An ActiveRecord gem that makes it easier to do reporting.
63
+ test_files:
64
+ - test/statistics_test.rb