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 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