weighted_average 0.0.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
22
  test/test.log
23
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/Rakefile CHANGED
@@ -1,26 +1,6 @@
1
- require 'rubygems'
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
@@ -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
@@ -0,0 +1,3 @@
1
+ module WeightedAverage
2
+ VERSION = '1.0.0'
3
+ end
data/test/helper.rb CHANGED
@@ -1,19 +1,19 @@
1
- require 'rubygems'
2
- require 'test/unit'
3
- require 'shoulda'
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 Test::Unit::TestCase
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
- ActiveRecord::Base.establish_connection(
26
- 'adapter' => 'mysql',
27
- 'database' => 'weighted_average_test',
28
- 'username' => 'root',
29
- 'password' => 'password'
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, :options => 'ENGINE=InnoDB default charset=utf8', :id => false do |t|
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 = ActiveSupport::SecureRandom.hex(10)
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
- class TestWeightedAverage < Test::Unit::TestCase
4
- # should "update all weighted averages, has_many through" do
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 `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)) )"
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
- # should "update all weighted averages" do
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 `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)) )"
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 `aircraft` SET `aircraft`.seats = (#{AirlineAircraftSeatClass.construct_weighted_average_sql(:seats, {}, :conditions => '`airline_aircraft_seat_classes`.aircraft_id = `aircraft`.id')})"
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
- # should "update all weighted averages, custom weighting" do
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 `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)) )"
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 `aircraft` SET `aircraft`.distance = (#{Segment.construct_weighted_average_sql(:distance, { :by => :passengers }, :conditions => '`segments`.aircraft_id = `aircraft`.id' )})"
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
- # should "update all weighted averages, custom weighting and disaggregator" do
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 `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)) )"
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 `aircraft` SET `aircraft`.payload = (#{Segment.construct_weighted_average_sql(:payload, { :by => :passengers, :disaggregator => :departures_performed }, :conditions => '`segments`.aircraft_id = `aircraft`.id')})"
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
- should "do default weighting" do
49
+ it "does default weighting" do
50
50
  should_have_same_sql(
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)",
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
- should "do custom weighting" do
56
+ it "does custom weighting" do
57
57
  should_have_same_sql(
58
- "SELECT (SUM((`segments`.`distance`) * `segments`.`passengers`) / SUM(`segments`.`passengers`)) AS weighted_average FROM `segments` WHERE (`segments`.`distance` IS NOT NULL)",
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
- should "add multiple columns before averaging" do
65
+ it "adds multiple columns before averaging" do
66
66
  should_have_same_sql(
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)",
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
- should "do default weighting with conditions" do
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((`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)",
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
- should "do custom weighting with conditions" do
85
+ it "does custom weighting with conditions" do
86
86
  conditions = '456 = 456'
87
87
  should_have_same_sql(
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)",
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
- should "do foreign default weighting" do
96
+ it "does foreign default weighting" do
97
97
  should_have_same_sql(
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)",
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
- should "do foreign custom weighting" do
105
+ it "does foreign custom weighting" do
106
106
  should_have_same_sql(
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)",
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
- should "do foreign custom weighting with custom join keys" do
112
+ it "does foreign custom weighting with custom join keys" do
113
113
  should_have_same_sql(
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)",
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
- should "do default weighting, scoped" do
121
+ it "does default weighting, scoped" do
122
122
  conditions = '456 = 456'
123
123
  should_have_same_sql(
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)",
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
- should "do custom weighting, scoped" do
129
+ it "does custom weighting, scoped" do
130
130
  conditions = '999 = 999'
131
131
  should_have_same_sql(
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)",
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
- should "do foreign default weighting, scoped" do
139
+ it "does foreign default weighting, scoped" do
140
140
  conditions = '454 != 999'
141
141
  should_have_same_sql(
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)",
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
- should "do foreign custom weighting, scoped" do
148
- conditions = '`aircraft`.`m3` > 1'
147
+ it "does foreign custom weighting, scoped" do
148
+ conditions = 'aircraft.m3 > 1'
149
149
  should_have_same_sql(
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)",
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
- should "do custom weighting with disaggregation" do
157
+ it "does custom weighting with disaggregation" do
158
158
  should_have_same_sql(
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)",
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
- should "construct weightings across has_many through associations (that can be used for updating all)" do
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((`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 (`segments`.`seats` IS NOT NULL) AND (`aircraft`.`aircraft_class_id` = `aircraft_classes`.`id`)",
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
- should "do custom weighting, with a cohort" do
180
+ it "does custom weighting, with a cohort" do
181
181
  should_have_same_sql(
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)",
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 /\s+/, ' ' }
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| assert_equal best, 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
@@ -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 = %q{weighted_average}
8
- s.version = "0.0.6"
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.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
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
- if s.respond_to? :specification_version then
42
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
- s.specification_version = 3
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
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
- s.add_runtime_dependency(%q<activerecord>, ["~> 3"])
47
- s.add_runtime_dependency(%q<arel>, ["~> 2"])
48
- s.add_development_dependency(%q<cohort_scope>, [">= 0.0.2"])
49
- s.add_development_dependency(%q<shoulda>, [">= 2.10.3"])
50
- else
51
- s.add_dependency(%q<activerecord>, ["~> 3"])
52
- s.add_dependency(%q<arel>, ["~> 2"])
53
- s.add_dependency(%q<cohort_scope>, [">= 0.0.2"])
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
- hash: 19
5
- prerelease: false
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
- date: 2010-11-16 00:00:00 -06:00
22
- default_executable:
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
- requirement: &id001 !ruby/object:Gem::Requirement
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
- hash: 5
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
- requirement: &id002 !ruby/object:Gem::Requirement
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
- hash: 7
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
- requirement: &id003 !ruby/object:Gem::Requirement
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
- requirement: &id004 !ruby/object:Gem::Requirement
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
- hash: 33
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
- version_requirements: *id004
84
- description: Perform weighted averages, even across associations. Rails 3 only because it uses ARel.
85
- email: seamus@abshere.net
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
- extra_rdoc_files:
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
- has_rdoc: true
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
- - --charset=UTF-8
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
- hash: 3
119
- segments:
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
- hash: 3
128
- segments:
129
- - 0
130
- version: "0"
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
131
142
  requirements: []
132
-
133
- rubyforge_project:
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