statistics 0.1.1 → 1.0.0
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/VERSION +1 -1
- data/lib/statistics.rb +40 -40
- data/statistics.gemspec +2 -2
- metadata +2 -2
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
1.0.0
|
data/lib/statistics.rb
CHANGED
|
@@ -2,39 +2,39 @@ module Statistics
|
|
|
2
2
|
class << self
|
|
3
3
|
def included(base)
|
|
4
4
|
base.extend(HasStats)
|
|
5
|
-
end
|
|
5
|
+
end
|
|
6
6
|
|
|
7
7
|
def default_filters(filters)
|
|
8
8
|
ActiveRecord::Base.instance_eval { @filter_all_on = filters }
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def supported_calculations
|
|
12
|
-
[:average, :count, :maximum, :minimum, :sum]
|
|
12
|
+
[:average, :count, :maximum, :minimum, :sum]
|
|
13
13
|
end
|
|
14
|
-
end
|
|
14
|
+
end
|
|
15
15
|
|
|
16
16
|
# This extension provides the ability to define statistics for reporting purposes
|
|
17
17
|
module HasStats
|
|
18
18
|
|
|
19
19
|
# OPTIONS:
|
|
20
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
|
|
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
23
|
# operation by (+:all+ should be used if no scopes are to be applied)
|
|
24
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
|
|
25
|
+
#* +filter_on+ - A hash with keys that represent filters. The with values in the has are rules
|
|
26
26
|
# on how to generate the query for the correspond filter.
|
|
27
27
|
#* +cached_for+ - A duration for how long to cache this specific statistic
|
|
28
28
|
#
|
|
29
|
-
# Additional options can also be passed in that would normally be passed to an ActiveRecord
|
|
29
|
+
# Additional options can also be passed in that would normally be passed to an ActiveRecord
|
|
30
30
|
# +calculate+ call, like +conditions+, +joins+, etc
|
|
31
31
|
#
|
|
32
32
|
# EXAMPLE:
|
|
33
33
|
#
|
|
34
34
|
# class MockModel < ActiveRecord::Base
|
|
35
|
-
#
|
|
35
|
+
#
|
|
36
36
|
# named_scope :my_scope, :conditions => 'value > 5'
|
|
37
|
-
#
|
|
37
|
+
#
|
|
38
38
|
# define_statistic "Basic Count", :count => :all
|
|
39
39
|
# define_statistic "Basic Sum", :sum => :all, :column_name => 'amount'
|
|
40
40
|
# define_statistic "Chained Scope Count", :count => [:all, :my_scope]
|
|
@@ -44,18 +44,18 @@ module Statistics
|
|
|
44
44
|
# end
|
|
45
45
|
def define_statistic(name, options)
|
|
46
46
|
method_name = name.to_s.gsub(" ", "").underscore + "_stat"
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
@statistics ||= {}
|
|
49
49
|
@filter_all_on ||= ActiveRecord::Base.instance_eval { @filter_all_on }
|
|
50
50
|
@statistics[name] = method_name
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
options = { :column_name => :id }.merge(options)
|
|
53
53
|
|
|
54
54
|
calculation = options.keys.find {|opt| Statistics::supported_calculations.include?(opt)}
|
|
55
55
|
calculation ||= :count
|
|
56
56
|
|
|
57
57
|
# We must use the metaclass here to metaprogrammatically define a class method
|
|
58
|
-
(class<<self; self; end).instance_eval do
|
|
58
|
+
(class<<self; self; end).instance_eval do
|
|
59
59
|
define_method(method_name) do |filters|
|
|
60
60
|
# check the cache before running a query for the stat
|
|
61
61
|
cached_val = Rails.cache.read("#{self.name}#{method_name}#{filters}") if options[:cache_for]
|
|
@@ -64,37 +64,37 @@ module Statistics
|
|
|
64
64
|
scoped_options = Marshal.load(Marshal.dump(options))
|
|
65
65
|
|
|
66
66
|
filters.each do |key, value|
|
|
67
|
-
|
|
67
|
+
unless value.nil?
|
|
68
68
|
sql = ((@filter_all_on || {}).merge(scoped_options[:filter_on] || {}))[key].gsub("?", "'#{value}'")
|
|
69
69
|
sql = sql.gsub("%t", "#{table_name}")
|
|
70
70
|
sql_frag = send(:sanitize_sql_for_conditions, sql)
|
|
71
|
-
case
|
|
72
|
-
when sql_frag.nil?
|
|
73
|
-
when scoped_options[:conditions].nil?
|
|
74
|
-
when scoped_options[:conditions].is_a?(Array)
|
|
75
|
-
when scoped_options[:conditions].is_a?(String)
|
|
71
|
+
case
|
|
72
|
+
when sql_frag.nil? then nil
|
|
73
|
+
when scoped_options[:conditions].nil? then scoped_options[:conditions] = sql_frag
|
|
74
|
+
when scoped_options[:conditions].is_a?(Array) then scoped_options[:conditions][0].concat(" AND #{sql_frag}")
|
|
75
|
+
when scoped_options[:conditions].is_a?(String) then scoped_options[:conditions].concat(" AND #{sql_frag}")
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
end if filters.is_a?(Hash)
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
base = self
|
|
81
81
|
# chain named scopes
|
|
82
82
|
scopes = Array(scoped_options[calculation])
|
|
83
83
|
scopes.each do |scope|
|
|
84
84
|
base = base.send(scope)
|
|
85
|
-
end if scopes != [:all]
|
|
85
|
+
end if scopes != [:all]
|
|
86
86
|
stat_value = base.send(calculation, scoped_options[:column_name], sql_options(scoped_options))
|
|
87
87
|
|
|
88
88
|
# cache stat value
|
|
89
|
-
Rails.cache.write("#{self.name}#{method_name}#{filters}", stat_value, :expires_in => options[:cache_for]) if options[:cache_for]
|
|
89
|
+
Rails.cache.write("#{self.name}#{method_name}#{filters}", stat_value, :expires_in => options[:cache_for]) if options[:cache_for]
|
|
90
90
|
|
|
91
91
|
stat_value
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
# Defines a statistic using a block that has access to all other defined statistics
|
|
97
|
-
#
|
|
97
|
+
#
|
|
98
98
|
# EXAMPLE:
|
|
99
99
|
# class MockModel < ActiveRecord::Base
|
|
100
100
|
# define_statistic "Basic Count", :count => :all
|
|
@@ -107,20 +107,20 @@ module Statistics
|
|
|
107
107
|
|
|
108
108
|
@statistics ||= {}
|
|
109
109
|
@statistics[name] = method_name
|
|
110
|
-
|
|
111
|
-
(class<<self; self; end).instance_eval do
|
|
110
|
+
|
|
111
|
+
(class<<self; self; end).instance_eval do
|
|
112
112
|
define_method(method_name) do |filters|
|
|
113
113
|
@filters = filters
|
|
114
114
|
yield
|
|
115
115
|
end
|
|
116
116
|
end
|
|
117
117
|
end
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
# returns an array containing the names/keys of all defined statistics
|
|
120
120
|
def statistics_keys
|
|
121
121
|
@statistics.keys
|
|
122
122
|
end
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
# Calculates all the statistics defined for this AR class and returns a hash with the values.
|
|
125
125
|
# There is an optional parameter that is a hash of all values you want to filter by.
|
|
126
126
|
#
|
|
@@ -133,7 +133,7 @@ module Statistics
|
|
|
133
133
|
stats_hash
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
# returns a single statistic based on the +stat_name+ paramater passed in and
|
|
138
138
|
# similarly to the +statistics+ method, it also can take filters.
|
|
139
139
|
#
|
|
@@ -143,7 +143,7 @@ module Statistics
|
|
|
143
143
|
def get_stat(stat_name, filters = {})
|
|
144
144
|
send(@statistics[stat_name], filters) if @statistics[stat_name]
|
|
145
145
|
end
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
# to keep things DRY anything that all statistics need to be filterable by can be defined
|
|
148
148
|
# seperatly using this method
|
|
149
149
|
#
|
|
@@ -162,19 +162,19 @@ module Statistics
|
|
|
162
162
|
|
|
163
163
|
private
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
def defined_stats(name)
|
|
166
|
+
get_stat(name, @filters)
|
|
167
|
+
end
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
172
177
|
end
|
|
173
|
-
options.delete(:column_name)
|
|
174
|
-
options.delete(:filter_on)
|
|
175
|
-
options.delete(:cache_for)
|
|
176
|
-
options
|
|
177
|
-
end
|
|
178
178
|
end
|
|
179
179
|
end
|
|
180
180
|
|
data/statistics.gemspec
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = %q{statistics}
|
|
8
|
-
s.version = "0.
|
|
8
|
+
s.version = "1.0.0"
|
|
9
9
|
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
11
|
s.authors = ["Alexandru Catighera"]
|
|
12
|
-
s.date = %q{2010-
|
|
12
|
+
s.date = %q{2010-09-10}
|
|
13
13
|
s.email = %q{acatighera@gmail.com}
|
|
14
14
|
s.extra_rdoc_files = [
|
|
15
15
|
"README.markdown"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: statistics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexandru Catighera
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2010-
|
|
12
|
+
date: 2010-09-10 00:00:00 -04:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|