weighted_average 0.0.6 → 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/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Rakefile +2 -47
- data/lib/weighted_average.rb +16 -9
- data/lib/weighted_average/version.rb +3 -0
- data/test/helper.rb +30 -17
- data/test/test_weighted_average.rb +56 -47
- data/weighted_average.gemspec +24 -55
- metadata +103 -93
- data/VERSION +0 -1
data/.gitignore
CHANGED
data/Gemfile
ADDED
data/Rakefile
CHANGED
@@ -1,26 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require 'rake'
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'jeweler'
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "weighted_average"
|
8
|
-
gem.summary = %Q{Perform weighted averages. Rails 3 only.}
|
9
|
-
gem.description = %Q{Perform weighted averages, even across associations. Rails 3 only because it uses ARel.}
|
10
|
-
gem.email = "seamus@abshere.net"
|
11
|
-
gem.homepage = "http://github.com/seamusabshere/weighted_average"
|
12
|
-
gem.authors = ["Seamus Abshere", "Andy Rossmeissl", "Ian Hough", "Matt Kling"]
|
13
|
-
gem.add_dependency 'activerecord', '~>3'
|
14
|
-
gem.add_dependency 'arel', '~>2'
|
15
|
-
gem.add_development_dependency 'cohort_scope', '>=0.0.2'
|
16
|
-
gem.add_development_dependency "shoulda", ">= 2.10.3"
|
17
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
-
end
|
19
|
-
Jeweler::GemcutterTasks.new
|
20
|
-
rescue LoadError
|
21
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
-
end
|
1
|
+
require "bundler/gem_tasks"
|
23
2
|
|
3
|
+
require 'rake'
|
24
4
|
require 'rake/testtask'
|
25
5
|
Rake::TestTask.new(:test) do |test|
|
26
6
|
test.libs << 'lib' << 'test'
|
@@ -28,29 +8,4 @@ Rake::TestTask.new(:test) do |test|
|
|
28
8
|
test.verbose = true
|
29
9
|
end
|
30
10
|
|
31
|
-
begin
|
32
|
-
require 'rcov/rcovtask'
|
33
|
-
Rcov::RcovTask.new do |test|
|
34
|
-
test.libs << 'test'
|
35
|
-
test.pattern = 'test/**/test_*.rb'
|
36
|
-
test.verbose = true
|
37
|
-
end
|
38
|
-
rescue LoadError
|
39
|
-
task :rcov do
|
40
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
task :test => :check_dependencies
|
45
|
-
|
46
11
|
task :default => :test
|
47
|
-
|
48
|
-
require 'rake/rdoctask'
|
49
|
-
Rake::RDocTask.new do |rdoc|
|
50
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
51
|
-
|
52
|
-
rdoc.rdoc_dir = 'rdoc'
|
53
|
-
rdoc.title = "weighted_average #{version}"
|
54
|
-
rdoc.rdoc_files.include('README*')
|
55
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
|
-
end
|
data/lib/weighted_average.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'active_support/core_ext'
|
1
2
|
require 'active_record'
|
3
|
+
require "weighted_average/version"
|
2
4
|
|
3
5
|
module WeightedAverage
|
4
6
|
# Returns a number.
|
@@ -8,12 +10,12 @@ module WeightedAverage
|
|
8
10
|
|
9
11
|
# Returns the ARel relation for a weighted average query.
|
10
12
|
def weighted_average_relation(data_column_names, options = {})
|
11
|
-
raise ArgumentError, "Only use array form if the weighting column in the foreign table is not called 'weighting'" if options[:weighted_by].is_a?(Array) and options[:weighted_by].length != 2
|
12
|
-
raise ArgumentError, "No nil values in weighted_by, please" if Array.wrap(options[:weighted_by]).any?(&:nil?)
|
13
|
+
raise ::ArgumentError, "Only use array form if the weighting column in the foreign table is not called 'weighting'" if options[:weighted_by].is_a?(::Array) and options[:weighted_by].length != 2
|
14
|
+
raise ::ArgumentError, "No nil values in weighted_by, please" if ::Array.wrap(options[:weighted_by]).any?(&:nil?)
|
13
15
|
|
14
16
|
# :airline_aircraft_seat_class
|
15
17
|
association = if options[:weighted_by].present?
|
16
|
-
options[:weighted_by].is_a?(Array) ? reflect_on_association(options[:weighted_by].first.to_sym) : reflect_on_association(options[:weighted_by].to_sym)
|
18
|
+
options[:weighted_by].is_a?(::Array) ? reflect_on_association(options[:weighted_by].first.to_sym) : reflect_on_association(options[:weighted_by].to_sym)
|
17
19
|
end
|
18
20
|
|
19
21
|
# AirlineAircraftSeatClass
|
@@ -27,9 +29,9 @@ module WeightedAverage
|
|
27
29
|
end
|
28
30
|
|
29
31
|
# `airline_aircraft_seat_classes`.`weighting`
|
30
|
-
weighted_by_column_name = if association_class and options[:weighted_by].is_a?(Array)
|
32
|
+
weighted_by_column_name = if association_class and options[:weighted_by].is_a?(::Array)
|
31
33
|
options[:weighted_by].last.to_s
|
32
|
-
elsif !association_class and (options[:weighted_by].is_a?(String) or options[:weighted_by].is_a?(Symbol))
|
34
|
+
elsif !association_class and (options[:weighted_by].is_a?(::String) or options[:weighted_by].is_a?(::Symbol))
|
33
35
|
options[:weighted_by].to_s
|
34
36
|
else
|
35
37
|
'weighting'
|
@@ -42,7 +44,7 @@ module WeightedAverage
|
|
42
44
|
end
|
43
45
|
|
44
46
|
# [ `aircraft`.`foo`, `aircraft`.`baz` ]
|
45
|
-
data_column_names = Array.wrap(data_column_names).map do |data_column_name|
|
47
|
+
data_column_names = ::Array.wrap(data_column_names).map do |data_column_name|
|
46
48
|
[ quoted_table_name, connection.quote_column_name(data_column_name) ].join '.'
|
47
49
|
end
|
48
50
|
|
@@ -50,13 +52,18 @@ module WeightedAverage
|
|
50
52
|
data_column_names.each do |data_column_name|
|
51
53
|
relation = relation.where("#{data_column_name} IS NOT NULL")
|
52
54
|
end
|
55
|
+
|
56
|
+
# avoid division by zero
|
57
|
+
relation = relation.where("#{weighted_by_column_name} > 0")
|
58
|
+
relation = relation.where("#{disaggregate_by_column_name} > 0") if disaggregate_by_column_name
|
59
|
+
|
53
60
|
# FIXME this will break on through relationships, where it has to be :aircraft => :aircraft_class
|
54
61
|
relation = relation.joins(association.name) if association_class
|
55
62
|
relation
|
56
63
|
end
|
57
64
|
end
|
58
65
|
|
59
|
-
ActiveRecord::Associations::AssociationCollection.class_eval do
|
66
|
+
(defined?(::ActiveRecord::Associations::CollectionProxy) ? ::ActiveRecord::Associations::CollectionProxy : ::ActiveRecord::Associations::AssociationCollection).class_eval do
|
60
67
|
def self.weighted_average(*args) # :nodoc:
|
61
68
|
scoped.weighted_average(*args)
|
62
69
|
end
|
@@ -65,7 +72,7 @@ ActiveRecord::Associations::AssociationCollection.class_eval do
|
|
65
72
|
scoped.weighted_average_relation(*args)
|
66
73
|
end
|
67
74
|
end
|
68
|
-
ActiveRecord::Base.class_eval do
|
75
|
+
::ActiveRecord::Base.class_eval do
|
69
76
|
def self.weighted_average(*args) # :nodoc:
|
70
77
|
scoped.weighted_average(*args)
|
71
78
|
end
|
@@ -75,4 +82,4 @@ ActiveRecord::Base.class_eval do
|
|
75
82
|
end
|
76
83
|
end
|
77
84
|
|
78
|
-
ActiveRecord::Relation.send :include, WeightedAverage
|
85
|
+
::ActiveRecord::Relation.send :include, ::WeightedAverage
|
data/test/helper.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
unless RUBY_VERSION >= '1.9'
|
5
|
-
require 'ruby-debug'
|
6
|
-
end
|
7
|
-
require 'logger'
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'minitest/spec'
|
3
|
+
require 'minitest/autorun'
|
8
4
|
require 'cohort_scope'
|
9
5
|
|
10
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
11
6
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
12
8
|
require 'weighted_average'
|
13
9
|
|
14
|
-
class
|
10
|
+
class MiniTest::Unit::TestCase
|
15
11
|
end
|
16
12
|
|
13
|
+
# require 'logger'
|
14
|
+
# ActiveRecord::Base.logger = Logger.new($stderr)
|
15
|
+
# ActiveRecord::Base.logger.level = Logger::DEBUG
|
16
|
+
|
17
17
|
$logger = Logger.new STDOUT #'test/test.log'
|
18
18
|
$logger.level = Logger::INFO
|
19
19
|
|
@@ -22,12 +22,25 @@ ActiveSupport::Notifications.subscribe do |*args|
|
|
22
22
|
$logger.debug "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
config = {
|
26
|
+
:host => '127.0.0.1',
|
27
|
+
:database => 'test_weighted_average'
|
28
|
+
}
|
29
|
+
|
30
|
+
extra = if ENV['WEIGHTED_AVERAGE_TEST_ADAPTER'] == 'postgresql'
|
31
|
+
{
|
32
|
+
:adapter => 'postgresql',
|
33
|
+
:username => `whoami`.chomp,
|
34
|
+
}
|
35
|
+
else
|
36
|
+
{
|
37
|
+
:adapter => 'mysql',
|
38
|
+
:username => 'root',
|
39
|
+
:password => 'password'
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
ActiveRecord::Base.establish_connection config.merge(extra)
|
31
44
|
|
32
45
|
ActiveSupport::Inflector.inflections do |inflect|
|
33
46
|
inflect.uncountable %w{aircraft airline_aircraft aircraft_deux AircraftDeux}
|
@@ -40,7 +53,7 @@ ActiveSupport::Inflector.inflections do |inflect|
|
|
40
53
|
end
|
41
54
|
|
42
55
|
ActiveRecord::Schema.define(:version => 20090819143429) do
|
43
|
-
create_table "segments", :force => true, :
|
56
|
+
create_table "segments", :force => true, :id => false do |t|
|
44
57
|
t.integer "aircraft_id" # should this be here?
|
45
58
|
t.integer "departures_performed"
|
46
59
|
t.integer "payload"
|
@@ -219,7 +232,7 @@ end
|
|
219
232
|
(1..10).each do |i|
|
220
233
|
a = Segment.new
|
221
234
|
a.payload = i
|
222
|
-
a.row_hash =
|
235
|
+
a.row_hash = SecureRandom.hex(10)
|
223
236
|
a.save!
|
224
237
|
end
|
225
238
|
|
@@ -1,70 +1,70 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
|
4
|
-
#
|
3
|
+
describe WeightedAverage do
|
4
|
+
# it "update all weighted averages, has_many through" do
|
5
5
|
# should_have_same_sql(
|
6
6
|
# AircraftClass.construct_update_all_weighted_averages_sql(:seats, :association => :airline_aircraft_seat_classes),
|
7
|
-
# "UPDATE
|
7
|
+
# "UPDATE aircraft_classes SET aircraft_classes.seats = (SELECT (SUM((airline_aircraft_seat_classes.seats * airline_aircraft_seat_classes.weighting) / SUM(airline_aircraft_seat_classes.weighting)) AS weighted_average FROM airline_aircraft_seat_classes LEFT OUTER JOIN aircraft ON aircraft.id = airline_aircraft_seat_classes.aircraft_id WHERE ((aircraft.aircraft_class_id = aircraft_classes.id AND airline_aircraft_seat_classes.aircraft_id = aircraft.id) AND (airline_aircraft_seat_classes.seats IS NOT NULL)) )"
|
8
8
|
# )
|
9
9
|
# end
|
10
10
|
#
|
11
|
-
#
|
11
|
+
# it "update all weighted averages" do
|
12
12
|
# should_have_same_sql(
|
13
13
|
# Aircraft.construct_update_all_weighted_averages_sql(:seats, :association => :airline_aircraft_seat_classes),
|
14
|
-
# "UPDATE
|
14
|
+
# "UPDATE aircraft SET aircraft.seats = (SELECT (SUM((airline_aircraft_seat_classes.seats * airline_aircraft_seat_classes.weighting) / SUM(airline_aircraft_seat_classes.weighting)) AS weighted_average FROM airline_aircraft_seat_classes WHERE ((airline_aircraft_seat_classes.aircraft_id = aircraft.id) AND (airline_aircraft_seat_classes.seats IS NOT NULL)) )"
|
15
15
|
# )
|
16
16
|
#
|
17
17
|
# should_have_same_sql(
|
18
18
|
# Aircraft.construct_update_all_weighted_averages_sql(:seats, :association => :airline_aircraft_seat_classes),
|
19
|
-
# "UPDATE
|
19
|
+
# "UPDATE aircraft SET aircraft.seats = (#{AirlineAircraftSeatClass.construct_weighted_average_sql(:seats, {}, :conditions => 'airline_aircraft_seat_classes.aircraft_id = aircraft.id')})"
|
20
20
|
# )
|
21
21
|
# end
|
22
22
|
#
|
23
|
-
#
|
23
|
+
# it "update all weighted averages, custom weighting" do
|
24
24
|
# should_have_same_sql(
|
25
25
|
# Aircraft.construct_update_all_weighted_averages_sql(:distance, :by => :passengers, :association => :segments),
|
26
|
-
# "UPDATE
|
26
|
+
# "UPDATE aircraft SET aircraft.distance = (SELECT (SUM((segments.distance * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM segments WHERE ((segments.aircraft_id = aircraft.id) AND (segments.distance IS NOT NULL)) )"
|
27
27
|
# )
|
28
28
|
#
|
29
29
|
# should_have_same_sql(
|
30
30
|
# Aircraft.construct_update_all_weighted_averages_sql(:distance, :by => :passengers, :association => :segments),
|
31
|
-
# "UPDATE
|
31
|
+
# "UPDATE aircraft SET aircraft.distance = (#{Segment.construct_weighted_average_sql(:distance, { :by => :passengers }, :conditions => 'segments.aircraft_id = aircraft.id' )})"
|
32
32
|
# )
|
33
33
|
# end
|
34
34
|
#
|
35
|
-
#
|
35
|
+
# it "update all weighted averages, custom weighting and disaggregator" do
|
36
36
|
# should_have_same_sql(
|
37
37
|
# Aircraft.construct_update_all_weighted_averages_sql(:payload, :by => :passengers, :association => :segments, :disaggregator => :departures_performed),
|
38
|
-
# "UPDATE
|
38
|
+
# "UPDATE aircraft SET aircraft.payload = (SELECT (SUM((segments.payload / segments.departures_performed * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM segments WHERE ((segments.aircraft_id = aircraft.id) AND (segments.payload IS NOT NULL)) )"
|
39
39
|
# )
|
40
40
|
#
|
41
41
|
# should_have_same_sql(
|
42
42
|
# Aircraft.construct_update_all_weighted_averages_sql(:payload, :by => :passengers, :association => :segments, :disaggregator => :departures_performed),
|
43
|
-
# "UPDATE
|
43
|
+
# "UPDATE aircraft SET aircraft.payload = (#{Segment.construct_weighted_average_sql(:payload, { :by => :passengers, :disaggregator => :departures_performed }, :conditions => 'segments.aircraft_id = aircraft.id')})"
|
44
44
|
# )
|
45
45
|
# end
|
46
46
|
|
47
47
|
# plain
|
48
48
|
|
49
|
-
|
49
|
+
it "does default weighting" do
|
50
50
|
should_have_same_sql(
|
51
|
-
"SELECT (SUM((
|
51
|
+
"SELECT (SUM((airline_aircraft_seat_classes.seats) * airline_aircraft_seat_classes.weighting) / SUM(airline_aircraft_seat_classes.weighting)) AS weighted_average FROM airline_aircraft_seat_classes WHERE (airline_aircraft_seat_classes.seats IS NOT NULL) AND (airline_aircraft_seat_classes.weighting > 0)",
|
52
52
|
AirlineAircraftSeatClass.weighted_average_relation('seats')
|
53
53
|
)
|
54
54
|
end
|
55
55
|
|
56
|
-
|
56
|
+
it "does custom weighting" do
|
57
57
|
should_have_same_sql(
|
58
|
-
"SELECT (SUM((
|
58
|
+
"SELECT (SUM((segments.distance) * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM segments WHERE (segments.distance IS NOT NULL) AND (segments.passengers > 0)",
|
59
59
|
Segment.weighted_average_relation('distance', :weighted_by => 'passengers')
|
60
60
|
)
|
61
61
|
end
|
62
62
|
|
63
63
|
# multiple columns
|
64
64
|
|
65
|
-
|
65
|
+
it "adds multiple columns before averaging" do
|
66
66
|
should_have_same_sql(
|
67
|
-
"SELECT (SUM((
|
67
|
+
"SELECT (SUM((airline_aircraft_seat_classes.seats + airline_aircraft_seat_classes.pitch) * airline_aircraft_seat_classes.weighting) / SUM(airline_aircraft_seat_classes.weighting)) AS weighted_average FROM airline_aircraft_seat_classes WHERE (airline_aircraft_seat_classes.seats IS NOT NULL) AND (airline_aircraft_seat_classes.pitch IS NOT NULL) AND (airline_aircraft_seat_classes.weighting > 0)",
|
68
68
|
AirlineAircraftSeatClass.weighted_average_relation(['seats', 'pitch'])
|
69
69
|
)
|
70
70
|
end
|
@@ -72,20 +72,20 @@ class TestWeightedAverage < Test::Unit::TestCase
|
|
72
72
|
# conditions
|
73
73
|
|
74
74
|
# a subquery used in Aircraft.update_all_seats
|
75
|
-
|
75
|
+
it "does default weighting with conditions" do
|
76
76
|
conditions = 'aircraft_id = 1'
|
77
77
|
|
78
78
|
should_have_same_sql(
|
79
|
-
"SELECT (SUM((
|
79
|
+
"SELECT (SUM((airline_aircraft_seat_classes.seats) * airline_aircraft_seat_classes.weighting) / SUM(airline_aircraft_seat_classes.weighting)) AS weighted_average FROM airline_aircraft_seat_classes WHERE (#{conditions}) AND (airline_aircraft_seat_classes.seats IS NOT NULL) AND (airline_aircraft_seat_classes.weighting > 0)",
|
80
80
|
AirlineAircraftSeatClass.where(conditions).weighted_average_relation('seats')
|
81
81
|
)
|
82
82
|
end
|
83
83
|
|
84
84
|
# note fake condition
|
85
|
-
|
85
|
+
it "does custom weighting with conditions" do
|
86
86
|
conditions = '456 = 456'
|
87
87
|
should_have_same_sql(
|
88
|
-
"SELECT (SUM((
|
88
|
+
"SELECT (SUM((segments.load_factor) * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM segments WHERE (#{conditions}) AND (segments.load_factor IS NOT NULL) AND (segments.passengers > 0)",
|
89
89
|
Segment.where(conditions).weighted_average_relation('load_factor', :weighted_by => 'passengers')
|
90
90
|
)
|
91
91
|
end
|
@@ -93,83 +93,83 @@ class TestWeightedAverage < Test::Unit::TestCase
|
|
93
93
|
# foreign weightings
|
94
94
|
|
95
95
|
# fake! we would never calc seats this way
|
96
|
-
|
96
|
+
it "does foreign default weighting" do
|
97
97
|
should_have_same_sql(
|
98
|
-
"SELECT (SUM((
|
98
|
+
"SELECT (SUM((aircraft.seats) * airline_aircraft_seat_classes.weighting) / SUM(airline_aircraft_seat_classes.weighting)) AS weighted_average FROM aircraft INNER JOIN airline_aircraft_seat_classes ON airline_aircraft_seat_classes.aircraft_id = aircraft.id WHERE (aircraft.seats IS NOT NULL) AND (airline_aircraft_seat_classes.weighting > 0)",
|
99
99
|
Aircraft.weighted_average_relation('seats', :weighted_by => :airline_aircraft_seat_classes)
|
100
100
|
)
|
101
101
|
end
|
102
102
|
|
103
103
|
# Aircraft#m3 fallback value
|
104
104
|
# a subquery used in Aircraft.update_all_m3s
|
105
|
-
|
105
|
+
it "does foreign custom weighting" do
|
106
106
|
should_have_same_sql(
|
107
|
-
"SELECT (SUM((
|
107
|
+
"SELECT (SUM((aircraft.m3) * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM aircraft INNER JOIN segments ON segments.bts_aircraft_type = aircraft.bts_aircraft_type WHERE (aircraft.m3 IS NOT NULL) AND (segments.passengers > 0)",
|
108
108
|
Aircraft.weighted_average_relation(:m3, :weighted_by => [:segments, :passengers])
|
109
109
|
)
|
110
110
|
end
|
111
111
|
|
112
|
-
|
112
|
+
it "does foreign custom weighting with custom join keys" do
|
113
113
|
should_have_same_sql(
|
114
|
-
"SELECT (SUM((
|
114
|
+
"SELECT (SUM((aircraft_deux.m3) * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM aircraft_deux INNER JOIN segments ON segments.bts_aircraft_type = aircraft_deux.my_bts_aircraft_type_code WHERE (aircraft_deux.m3 IS NOT NULL) AND (segments.passengers > 0)",
|
115
115
|
AircraftDeux.weighted_average_relation(:m3, :weighted_by => [:segments, :passengers])
|
116
116
|
)
|
117
117
|
end
|
118
118
|
|
119
119
|
# scoped
|
120
120
|
|
121
|
-
|
121
|
+
it "does default weighting, scoped" do
|
122
122
|
conditions = '456 = 456'
|
123
123
|
should_have_same_sql(
|
124
|
-
"SELECT (SUM((
|
124
|
+
"SELECT (SUM((airline_aircraft_seat_classes.seats) * airline_aircraft_seat_classes.weighting) / SUM(airline_aircraft_seat_classes.weighting)) AS weighted_average FROM airline_aircraft_seat_classes WHERE (#{conditions}) AND (airline_aircraft_seat_classes.seats IS NOT NULL) AND (airline_aircraft_seat_classes.weighting > 0)",
|
125
125
|
AirlineAircraftSeatClass.scoped(:conditions => conditions).weighted_average_relation(:seats)
|
126
126
|
)
|
127
127
|
end
|
128
128
|
|
129
|
-
|
129
|
+
it "does custom weighting, scoped" do
|
130
130
|
conditions = '999 = 999'
|
131
131
|
should_have_same_sql(
|
132
|
-
"SELECT (SUM((
|
132
|
+
"SELECT (SUM((segments.load_factor) * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM segments WHERE (#{conditions}) AND (segments.load_factor IS NOT NULL) AND (segments.passengers > 0)",
|
133
133
|
Segment.scoped(:conditions => conditions).weighted_average_relation(:load_factor, :weighted_by => :passengers)
|
134
134
|
)
|
135
135
|
end
|
136
136
|
|
137
137
|
# scoped foreign weightings
|
138
138
|
|
139
|
-
|
139
|
+
it "does foreign default weighting, scoped" do
|
140
140
|
conditions = '454 != 999'
|
141
141
|
should_have_same_sql(
|
142
|
-
"SELECT (SUM((
|
142
|
+
"SELECT (SUM((aircraft.seats) * airline_aircraft_seat_classes.weighting) / SUM(airline_aircraft_seat_classes.weighting)) AS weighted_average FROM aircraft INNER JOIN airline_aircraft_seat_classes ON airline_aircraft_seat_classes.aircraft_id = aircraft.id WHERE (454 != 999) AND (aircraft.seats IS NOT NULL) AND (airline_aircraft_seat_classes.weighting > 0)",
|
143
143
|
Aircraft.scoped(:conditions => conditions).weighted_average_relation(:seats, :weighted_by => :airline_aircraft_seat_classes)
|
144
144
|
)
|
145
145
|
end
|
146
146
|
|
147
|
-
|
148
|
-
conditions = '
|
147
|
+
it "does foreign custom weighting, scoped" do
|
148
|
+
conditions = 'aircraft.m3 > 1'
|
149
149
|
should_have_same_sql(
|
150
|
-
"SELECT (SUM((
|
150
|
+
"SELECT (SUM((aircraft.m3) * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM aircraft INNER JOIN segments ON segments.bts_aircraft_type = aircraft.bts_aircraft_type WHERE (aircraft.m3 > 1) AND (aircraft.m3 IS NOT NULL) AND (segments.passengers > 0)",
|
151
151
|
Aircraft.scoped(:conditions => conditions).weighted_average_relation(:m3, :weighted_by => [:segments, :passengers])
|
152
152
|
)
|
153
153
|
end
|
154
154
|
|
155
155
|
# disaggregation
|
156
156
|
|
157
|
-
|
157
|
+
it "does custom weighting with disaggregation" do
|
158
158
|
should_have_same_sql(
|
159
|
-
"SELECT (SUM((
|
159
|
+
"SELECT (SUM((segments.load_factor) / segments.departures_performed * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM segments WHERE (segments.load_factor IS NOT NULL) AND (segments.passengers > 0) AND (segments.departures_performed > 0)",
|
160
160
|
Segment.weighted_average_relation(:load_factor, :weighted_by => :passengers, :disaggregate_by => :departures_performed)
|
161
161
|
)
|
162
162
|
end
|
163
163
|
|
164
164
|
# more complicated stuff
|
165
165
|
|
166
|
-
|
166
|
+
it "construct weightings across has_many through associations (that can be used for updating all)" do
|
167
167
|
aircraft_class = AircraftClass.arel_table
|
168
168
|
aircraft = Aircraft.arel_table
|
169
169
|
segment = Segment.arel_table
|
170
170
|
|
171
171
|
should_have_same_sql(
|
172
|
-
"SELECT (SUM((
|
172
|
+
"SELECT (SUM((segments.seats) * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM segments INNER JOIN aircraft ON aircraft.bts_aircraft_type = segments.bts_aircraft_type INNER JOIN aircraft_classes ON aircraft_classes.id = aircraft.aircraft_class_id WHERE aircraft.aircraft_class_id = aircraft_classes.id AND (segments.seats IS NOT NULL) AND (segments.passengers > 0)",
|
173
173
|
Segment.joins(:aircraft => :aircraft_class).weighted_average_relation(:seats, :weighted_by => :passengers).where(aircraft[:aircraft_class_id].eq(aircraft_class[:id]))
|
174
174
|
)
|
175
175
|
end
|
@@ -177,25 +177,34 @@ class TestWeightedAverage < Test::Unit::TestCase
|
|
177
177
|
|
178
178
|
# cohorts (requires the cohort_scope gem)
|
179
179
|
|
180
|
-
|
180
|
+
it "does custom weighting, with a cohort" do
|
181
181
|
should_have_same_sql(
|
182
|
-
"SELECT (SUM((
|
182
|
+
"SELECT (SUM((segments.load_factor) * segments.passengers) / SUM(segments.passengers)) AS weighted_average FROM segments WHERE segments.payload = 5 AND (segments.load_factor IS NOT NULL) AND (segments.passengers > 0)",
|
183
183
|
Segment.big_cohort(:payload => 5).weighted_average_relation(:load_factor, :weighted_by => :passengers)
|
184
184
|
)
|
185
185
|
end
|
186
186
|
|
187
187
|
private
|
188
188
|
|
189
|
+
def database_field_quote_char
|
190
|
+
case ActiveRecord::Base.connection.adapter_name
|
191
|
+
when /mysql/i
|
192
|
+
'`'
|
193
|
+
when /postgres/i
|
194
|
+
'"'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
189
198
|
def should_have_same_sql(*args)
|
190
199
|
# make sure everything is an SQL string
|
191
200
|
args.map! { |arg| arg.is_a?(String) ? arg : arg.to_sql }
|
192
201
|
# clean up whitespace
|
193
|
-
args.map! { |arg| arg.to_s.gsub
|
202
|
+
args.map! { |arg| arg.to_s.gsub(/\s+/, ' ').gsub(database_field_quote_char, '') }
|
194
203
|
# treat the first arg as the "known good"
|
195
204
|
best = args.shift
|
205
|
+
# make sure the "best" SQL is valid
|
206
|
+
ActiveRecord::Base.connection.execute(best)
|
196
207
|
# compare everything to the known good
|
197
|
-
args.each { |arg|
|
198
|
-
# make sure the SQL is valid
|
199
|
-
assert_nothing_raised { ActiveRecord::Base.connection.execute best }
|
208
|
+
args.each { |arg| best.must_equal arg }
|
200
209
|
end
|
201
210
|
end
|
data/weighted_average.gemspec
CHANGED
@@ -1,63 +1,32 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "weighted_average/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
6
|
+
s.name = "weighted_average"
|
7
|
+
s.version = WeightedAverage::VERSION
|
8
|
+
s.authors = ["Seamus Abshere", "Andy Rossmeissl", "Ian Hough", "Matt Kling"]
|
9
|
+
s.email = ["seamus@abshere.net"]
|
10
|
+
s.homepage = "https://github.com/seamusabshere/weighted_average"
|
11
|
+
s.summary = %Q{Perform weighted averages. Rails 3 only.}
|
12
|
+
s.description = %Q{Perform weighted averages, even across associations. Rails 3 only because it uses ARel.}
|
9
13
|
|
10
|
-
s.
|
11
|
-
s.authors = ["Seamus Abshere", "Andy Rossmeissl", "Ian Hough", "Matt Kling"]
|
12
|
-
s.date = %q{2010-11-16}
|
13
|
-
s.description = %q{Perform weighted averages, even across associations. Rails 3 only because it uses ARel.}
|
14
|
-
s.email = %q{seamus@abshere.net}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
".gitignore",
|
22
|
-
"LICENSE",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION",
|
26
|
-
"lib/weighted_average.rb",
|
27
|
-
"test/helper.rb",
|
28
|
-
"test/test_weighted_average.rb",
|
29
|
-
"weighted_average.gemspec"
|
30
|
-
]
|
31
|
-
s.homepage = %q{http://github.com/seamusabshere/weighted_average}
|
32
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
-
s.require_paths = ["lib"]
|
34
|
-
s.rubygems_version = %q{1.3.7}
|
35
|
-
s.summary = %q{Perform weighted averages. Rails 3 only.}
|
36
|
-
s.test_files = [
|
37
|
-
"test/helper.rb",
|
38
|
-
"test/test_weighted_average.rb"
|
39
|
-
]
|
14
|
+
s.rubyforge_project = "weighted_average"
|
40
15
|
|
41
|
-
|
42
|
-
|
43
|
-
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
44
20
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
s.add_dependency(%q<shoulda>, [">= 2.10.3"])
|
55
|
-
end
|
56
|
-
else
|
57
|
-
s.add_dependency(%q<activerecord>, ["~> 3"])
|
58
|
-
s.add_dependency(%q<arel>, ["~> 2"])
|
59
|
-
s.add_dependency(%q<cohort_scope>, [">= 0.0.2"])
|
60
|
-
s.add_dependency(%q<shoulda>, [">= 2.10.3"])
|
61
|
-
end
|
21
|
+
s.add_runtime_dependency 'activerecord', '~>3'
|
22
|
+
s.add_runtime_dependency 'activesupport', '~>3'
|
23
|
+
s.add_runtime_dependency 'arel', '~>2'
|
24
|
+
|
25
|
+
s.add_development_dependency 'cohort_scope', '>=0.0.2'
|
26
|
+
s.add_development_dependency 'minitest'
|
27
|
+
s.add_development_dependency 'rake'
|
28
|
+
s.add_development_dependency 'mysql'
|
29
|
+
s.add_development_dependency 'pg'
|
62
30
|
end
|
63
31
|
|
32
|
+
|
metadata
CHANGED
@@ -1,15 +1,10 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: weighted_average
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 6
|
10
|
-
version: 0.0.6
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Seamus Abshere
|
14
9
|
- Andy Rossmeissl
|
15
10
|
- Ian Hough
|
@@ -17,124 +12,139 @@ authors:
|
|
17
12
|
autorequire:
|
18
13
|
bindir: bin
|
19
14
|
cert_chain: []
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
dependencies:
|
24
|
-
- !ruby/object:Gem::Dependency
|
15
|
+
date: 2011-09-23 00:00:00.000000000Z
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
25
18
|
name: activerecord
|
19
|
+
requirement: &2153895000 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '3'
|
25
|
+
type: :runtime
|
26
26
|
prerelease: false
|
27
|
-
|
27
|
+
version_requirements: *2153895000
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: activesupport
|
30
|
+
requirement: &2153894500 !ruby/object:Gem::Requirement
|
28
31
|
none: false
|
29
|
-
requirements:
|
32
|
+
requirements:
|
30
33
|
- - ~>
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
|
33
|
-
segments:
|
34
|
-
- 3
|
35
|
-
version: "3"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '3'
|
36
36
|
type: :runtime
|
37
|
-
version_requirements: *id001
|
38
|
-
- !ruby/object:Gem::Dependency
|
39
|
-
name: arel
|
40
37
|
prerelease: false
|
41
|
-
|
38
|
+
version_requirements: *2153894500
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: arel
|
41
|
+
requirement: &2153894040 !ruby/object:Gem::Requirement
|
42
42
|
none: false
|
43
|
-
requirements:
|
43
|
+
requirements:
|
44
44
|
- - ~>
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
|
47
|
-
segments:
|
48
|
-
- 2
|
49
|
-
version: "2"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2'
|
50
47
|
type: :runtime
|
51
|
-
version_requirements: *id002
|
52
|
-
- !ruby/object:Gem::Dependency
|
53
|
-
name: cohort_scope
|
54
48
|
prerelease: false
|
55
|
-
|
49
|
+
version_requirements: *2153894040
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: cohort_scope
|
52
|
+
requirement: &2153893580 !ruby/object:Gem::Requirement
|
56
53
|
none: false
|
57
|
-
requirements:
|
58
|
-
- -
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
hash: 27
|
61
|
-
segments:
|
62
|
-
- 0
|
63
|
-
- 0
|
64
|
-
- 2
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
65
57
|
version: 0.0.2
|
66
58
|
type: :development
|
67
|
-
version_requirements: *id003
|
68
|
-
- !ruby/object:Gem::Dependency
|
69
|
-
name: shoulda
|
70
59
|
prerelease: false
|
71
|
-
|
60
|
+
version_requirements: *2153893580
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: minitest
|
63
|
+
requirement: &2153893200 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
type: :development
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: *2153893200
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: rake
|
74
|
+
requirement: &2153892740 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
type: :development
|
81
|
+
prerelease: false
|
82
|
+
version_requirements: *2153892740
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mysql
|
85
|
+
requirement: &2153892320 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: *2153892320
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: pg
|
96
|
+
requirement: &2153891900 !ruby/object:Gem::Requirement
|
72
97
|
none: false
|
73
|
-
requirements:
|
74
|
-
- -
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
|
77
|
-
segments:
|
78
|
-
- 2
|
79
|
-
- 10
|
80
|
-
- 3
|
81
|
-
version: 2.10.3
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
82
102
|
type: :development
|
83
|
-
|
84
|
-
|
85
|
-
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: *2153891900
|
105
|
+
description: Perform weighted averages, even across associations. Rails 3 only because
|
106
|
+
it uses ARel.
|
107
|
+
email:
|
108
|
+
- seamus@abshere.net
|
86
109
|
executables: []
|
87
|
-
|
88
110
|
extensions: []
|
89
|
-
|
90
|
-
|
91
|
-
- LICENSE
|
92
|
-
- README.rdoc
|
93
|
-
files:
|
111
|
+
extra_rdoc_files: []
|
112
|
+
files:
|
94
113
|
- .document
|
95
114
|
- .gitignore
|
115
|
+
- Gemfile
|
96
116
|
- LICENSE
|
97
117
|
- README.rdoc
|
98
118
|
- Rakefile
|
99
|
-
- VERSION
|
100
119
|
- lib/weighted_average.rb
|
120
|
+
- lib/weighted_average/version.rb
|
101
121
|
- test/helper.rb
|
102
122
|
- test/test_weighted_average.rb
|
103
123
|
- weighted_average.gemspec
|
104
|
-
|
105
|
-
homepage: http://github.com/seamusabshere/weighted_average
|
124
|
+
homepage: https://github.com/seamusabshere/weighted_average
|
106
125
|
licenses: []
|
107
|
-
|
108
126
|
post_install_message:
|
109
|
-
rdoc_options:
|
110
|
-
|
111
|
-
require_paths:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
112
129
|
- lib
|
113
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
131
|
none: false
|
115
|
-
requirements:
|
116
|
-
- -
|
117
|
-
- !ruby/object:Gem::Version
|
118
|
-
|
119
|
-
|
120
|
-
- 0
|
121
|
-
version: "0"
|
122
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ! '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
137
|
none: false
|
124
|
-
requirements:
|
125
|
-
- -
|
126
|
-
- !ruby/object:Gem::Version
|
127
|
-
|
128
|
-
segments:
|
129
|
-
- 0
|
130
|
-
version: "0"
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
131
142
|
requirements: []
|
132
|
-
|
133
|
-
|
134
|
-
rubygems_version: 1.3.7
|
143
|
+
rubyforge_project: weighted_average
|
144
|
+
rubygems_version: 1.8.6
|
135
145
|
signing_key:
|
136
146
|
specification_version: 3
|
137
147
|
summary: Perform weighted averages. Rails 3 only.
|
138
|
-
test_files:
|
148
|
+
test_files:
|
139
149
|
- test/helper.rb
|
140
150
|
- test/test_weighted_average.rb
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.0.6
|