statistics 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ *~
2
+ *.swp
3
+ *.swo
4
+ .DS_Store
5
+ debug.log
6
+ coverage
7
+ coverage.data
8
+ pkg/
@@ -0,0 +1 @@
1
+ Version 0.1, 27.05.09 - initial release
@@ -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.
@@ -0,0 +1,124 @@
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
+
111
+ define_statistic :active_users_count, :count => [:not_admins, :accounts_with_posts]
112
+ end
113
+
114
+ #### Accepts all ActiveRecord::Calculations options
115
+
116
+ The `:conditions` and `:joins` options are all particularly useful
117
+
118
+ class Account < ActiveRecord::Base
119
+ has_many :posts
120
+
121
+ define_statistic :active_users_count, :count => :all, :joins => :posts, :conditions => "account_type = 'non-admin'"
122
+ end
123
+
124
+ ###### Copyright (c) 2009 Alexandru Catighera, released under MIT license
@@ -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')
@@ -0,0 +1,181 @@
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
+ #
38
+ # define_statistic "Basic Count", :count => :all
39
+ # define_statistic "Basic Sum", :sum => :all, :column_name => 'amount'
40
+ # define_statistic "Chained Scope Count", :count => [:all, :my_scope]
41
+ # define_statistic "Default Filter", :count => :all
42
+ # define_statistic "Custom Filter", :count => :all, :filter_on => { :channel => 'channel = ?', :start_date => 'DATE(created_at) > ?' }
43
+ # define_statistic "Cached", :count => :all, :filter_on => { :channel => 'channel = ?', :blah => 'blah = ?' }, :cache_for => 1.second
44
+ # end
45
+ def define_statistic(name, options)
46
+ method_name = name.to_s.gsub(" ", "").underscore + "_stat"
47
+
48
+ @statistics ||= {}
49
+ @filter_all_on ||= ActiveRecord::Base.instance_eval { @filter_all_on }
50
+ @statistics[name] = method_name
51
+
52
+ options = { :column_name => :id }.merge(options)
53
+
54
+ calculation = options.keys.find {|opt| Statistics::supported_calculations.include?(opt)}
55
+ calculation ||= :count
56
+
57
+ # We must use the metaclass here to metaprogrammatically define a class method
58
+ (class<<self; self; end).instance_eval do
59
+ define_method(method_name) do |filters|
60
+ # check the cache before running a query for the stat
61
+ cached_val = Rails.cache.read("#{self.name}#{method_name}#{filters}") if options[:cache_for]
62
+ return cached_val unless cached_val.nil?
63
+
64
+ scoped_options = Marshal.load(Marshal.dump(options))
65
+
66
+ filters.each do |key, value|
67
+ if value
68
+ sql = ((@filter_all_on || {}).merge(scoped_options[:filter_on] || {}))[key].gsub("?", "'#{value}'")
69
+ sql = sql.gsub("%t", "#{table_name}")
70
+ sql_frag = send(:sanitize_sql_for_conditions, sql)
71
+ case
72
+ when sql_frag.nil? : nil
73
+ when scoped_options[:conditions].nil? : scoped_options[:conditions] = sql_frag
74
+ when scoped_options[:conditions].is_a?(Array) : scoped_options[:conditions][0].concat(" AND #{sql_frag}")
75
+ when scoped_options[:conditions].is_a?(String) : scoped_options[:conditions].concat(" AND #{sql_frag}")
76
+ end
77
+ end
78
+ end if filters.is_a?(Hash)
79
+
80
+ base = self
81
+ # chain named scopes
82
+ scopes = Array(scoped_options[calculation])
83
+ scopes.each do |scope|
84
+ base = base.send(scope)
85
+ end if scopes != [:all]
86
+ stat_value = base.send(calculation, scoped_options[:column_name], sql_options(scoped_options))
87
+
88
+ # cache stat value
89
+ Rails.cache.write("#{self.name}#{method_name}#{filters}", stat_value, :expires_in => options[:cache_for]) if options[:cache_for]
90
+
91
+ stat_value
92
+ end
93
+ end
94
+ end
95
+
96
+ # Defines a statistic using a block that has access to all other defined statistics
97
+ #
98
+ # EXAMPLE:
99
+ # class MockModel < ActiveRecord::Base
100
+ # define_statistic "Basic Count", :count => :all
101
+ # define_statistic "Basic Sum", :sum => :all, :column_name => 'amount'
102
+ # define_calculated_statistic "Total Profit"
103
+ # defined_stats('Basic Sum') * defined_stats('Basic Count')
104
+ # end
105
+ def define_calculated_statistic(name, &block)
106
+ method_name = name.to_s.gsub(" ", "").underscore + "_stat"
107
+
108
+ @statistics ||= {}
109
+ @statistics[name] = method_name
110
+
111
+ (class<<self; self; end).instance_eval do
112
+ define_method(method_name) do |filters|
113
+ @filters = filters
114
+ yield
115
+ end
116
+ end
117
+ end
118
+
119
+ # returns an array containing the names/keys of all defined statistics
120
+ def statistics_keys
121
+ @statistics.keys
122
+ end
123
+
124
+ # Calculates all the statistics defined for this AR class and returns a hash with the values.
125
+ # There is an optional parameter that is a hash of all values you want to filter by.
126
+ #
127
+ # EXAMPLE:
128
+ # MockModel.statistics
129
+ # MockModel.statistics(:user_type => 'registered', :user_status => 'active')
130
+ def statistics(filters = {}, except = nil)
131
+ (@statistics || {}).inject({}) do |stats_hash, stat|
132
+ stats_hash[stat.first] = send(stat.last, filters) if stat.last != except
133
+ stats_hash
134
+ end
135
+ end
136
+
137
+ # returns a single statistic based on the +stat_name+ paramater passed in and
138
+ # similarly to the +statistics+ method, it also can take filters.
139
+ #
140
+ # EXAMPLE:
141
+ # MockModel.get_stat('Basic Count')
142
+ # MockModel.get_stat('Basic Count', :user_type => 'registered', :user_status => 'active')
143
+ def get_stat(stat_name, filters = {})
144
+ send(@statistics[stat_name], filters) if @statistics[stat_name]
145
+ end
146
+
147
+ # to keep things DRY anything that all statistics need to be filterable by can be defined
148
+ # seperatly using this method
149
+ #
150
+ # EXAMPLE:
151
+ #
152
+ # class MockModel < ActiveRecord::Base
153
+ # define_statistic "Basic Count", :count => :all
154
+ # define_statistic "Basic Sum", :sum => :all, :column_name => 'amount'
155
+ #
156
+ # filter_all_stats_on(:user_id, "user_id = ?")
157
+ # end
158
+ def filter_all_stats_on(name, cond)
159
+ @filter_all_on ||= {}
160
+ @filter_all_on[name] = cond
161
+ end
162
+
163
+ private
164
+
165
+ def defined_stats(name)
166
+ get_stat(name, @filters)
167
+ end
168
+
169
+ def sql_options(options)
170
+ Statistics::supported_calculations.each do |deletable|
171
+ options.delete(deletable)
172
+ end
173
+ options.delete(:column_name)
174
+ options.delete(:filter_on)
175
+ options.delete(:cache_for)
176
+ options
177
+ end
178
+ end
179
+ end
180
+
181
+ 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{statistics}
8
+ s.version = "0.1.1"
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{2010-02-18}
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/acatighera/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,151 @@
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 "Default Filter", :count => :all
32
+ define_statistic "Custom Filter", :count => :all, :filter_on => { :channel => 'channel = ?', :start_date => 'DATE(created_at) > ?', :blah => 'blah = ?' }
33
+ define_statistic "Cached", :count => :all, :filter_on => { :channel => 'channel = ?', :blah => 'blah = ?' }, :cache_for => 1.second
34
+
35
+ define_calculated_statistic "Total Amount" do
36
+ defined_stats('Basic Sum') * defined_stats('Basic Count')
37
+ end
38
+
39
+ filter_all_stats_on(:user_id, "user_id = ?")
40
+ end
41
+
42
+ def test_basic
43
+ BasicModel.expects(:basic_num_stat).returns(1)
44
+ assert_equal({ :basic_num => 1 }, BasicModel.statistics)
45
+ end
46
+
47
+ def test_statistics
48
+ MockModel.expects(:basic_count_stat).returns(2)
49
+ MockModel.expects(:symbol_count_stat).returns(2)
50
+ MockModel.expects(:basic_sum_stat).returns(27)
51
+ MockModel.expects(:chained_scope_count_stat).returns(4)
52
+ MockModel.expects(:default_filter_stat).returns(5)
53
+ MockModel.expects(:custom_filter_stat).returns(3)
54
+ MockModel.expects(:cached_stat).returns(9)
55
+ MockModel.expects(:total_amount_stat).returns(54)
56
+
57
+ ["Basic Count",
58
+ :symbol_count,
59
+ "Basic Sum",
60
+ "Chained Scope Count",
61
+ "Default Filter",
62
+ "Custom Filter",
63
+ "Cached",
64
+ "Total Amount"].each do |key|
65
+ assert MockModel.statistics_keys.include?(key)
66
+ end
67
+
68
+ assert_equal({ "Basic Count" => 2,
69
+ :symbol_count => 2,
70
+ "Basic Sum" => 27,
71
+ "Chained Scope Count" => 4,
72
+ "Default Filter" => 5,
73
+ "Custom Filter" => 3,
74
+ "Cached" => 9,
75
+ "Total Amount" => 54 }, MockModel.statistics)
76
+ end
77
+
78
+ def test_get_stat
79
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(3)
80
+ assert_equal 3, MockModel.get_stat("Basic Count")
81
+
82
+ MockModel.expects(:calculate).with(:count, :id, { :conditions => "user_id = '54321'"}).returns(4)
83
+ assert_equal 4, MockModel.get_stat("Basic Count", :user_id => 54321)
84
+ end
85
+
86
+ def test_basic_stat
87
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(3)
88
+ assert_equal 3, MockModel.basic_count_stat({})
89
+
90
+ MockModel.expects(:calculate).with(:sum, 'amount', {}).returns(31)
91
+ assert_equal 31, MockModel.basic_sum_stat({})
92
+ end
93
+
94
+ def test_chained_scope_stat
95
+ MockModel.expects(:all).returns(MockModel)
96
+ MockModel.expects(:named_scope).returns(MockModel)
97
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(5)
98
+ assert_equal 5, MockModel.chained_scope_count_stat({})
99
+ end
100
+
101
+ def test_calculated_stat
102
+ MockModel.expects(:basic_count_stat).returns(3)
103
+ MockModel.expects(:basic_sum_stat).returns(33)
104
+
105
+ assert_equal 99, MockModel.total_amount_stat({})
106
+
107
+ MockModel.expects(:basic_count_stat).with(:user_id => 5).returns(2)
108
+ MockModel.expects(:basic_sum_stat).with(:user_id => 5).returns(25)
109
+
110
+ assert_equal 50, MockModel.total_amount_stat({:user_id => 5})
111
+
112
+ MockModel.expects(:basic_count_stat).with(:user_id => 6).returns(3)
113
+ MockModel.expects(:basic_sum_stat).with(:user_id => 6).returns(60)
114
+
115
+ assert_equal 180, MockModel.total_amount_stat({:user_id => 6})
116
+ end
117
+
118
+ def test_default_filter_stat
119
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(8)
120
+ assert_equal 8, MockModel.default_filter_stat({})
121
+
122
+ MockModel.expects(:calculate).with(:count, :id, { :conditions => "user_id = '12345'" }).returns(2)
123
+ assert_equal 2, MockModel.default_filter_stat( :user_id => '12345' )
124
+ end
125
+
126
+ def test_custom_filter_stat
127
+ MockModel.expects(:calculate).with(:count, :id, {}).returns(6)
128
+ assert_equal 6, MockModel.custom_filter_stat({})
129
+
130
+ MockModel.expects(:calculate).with() do |param1, param2, param3|
131
+ param1 == :count &&
132
+ param2 == :id &&
133
+ (param3 == { :conditions => "channel = 'chan5' AND DATE(created_at) > '#{Date.today.to_s(:db)}'" } ||
134
+ param3 == { :conditions => "DATE(created_at) > '#{Date.today.to_s(:db)}' AND channel = 'chan5'" } )
135
+ end.returns(3)
136
+ assert_equal 3, MockModel.custom_filter_stat(:channel => 'chan5', :start_date => Date.today.to_s(:db))
137
+ end
138
+
139
+ def test_cached_stat
140
+ MockModel.expects(:calculate).returns(6)
141
+ assert_equal 6, MockModel.cached_stat({:channel => 'chan5'})
142
+
143
+ MockModel.stubs(:calculate).returns(8)
144
+ assert_equal 6, MockModel.cached_stat({:channel => 'chan5'})
145
+ assert_equal 8, MockModel.cached_stat({})
146
+
147
+ sleep(1)
148
+ assert_equal 8, MockModel.cached_stat({:channel => 'chan5'})
149
+ end
150
+
151
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: statistics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexandru Catighera
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-18 00:00:00 -05: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/acatighera/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