weighted_average 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.0.4
@@ -1,76 +1,74 @@
1
- require 'active_support'
2
1
  require 'active_record'
2
+ require 'active_support'
3
+ require 'active_support/version'
4
+ %w{
5
+ active_support/core_ext/module
6
+ }.each do |active_support_3_requirement|
7
+ require active_support_3_requirement
8
+ end if ActiveSupport::VERSION::MAJOR == 3
3
9
 
4
- module ActiveRecord
5
- module WeightedAverage
6
- # Returns a number
7
- def weighted_average(*args)
8
- connection.select_value(weighted_average_relation(*args).to_sql, 'weighted_average').to_f
9
- end
10
-
11
- # Returns the ARel relation
12
- def weighted_average_relation(column_names, options = {})
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?)
15
-
16
- # aircraft['seats'] or (aircraft['seats'] + aircraft['payload'])
17
- columns = Array.wrap(column_names).map { |column_name| arel_table[column_name.to_s] }
18
-
19
- # :airline_aircraft_seat_class
20
- association = if options[:weighted_by].present?
21
- options[:weighted_by].is_a?(Array) ? reflect_on_association(options[:weighted_by].first.to_sym) : reflect_on_association(options[:weighted_by].to_sym)
22
- end
23
-
24
- # AirlineAircraftSeatClass
25
- association_class = association.klass if association
26
-
27
- # AirlineAircraftSeatClass.arel_table
28
- foreign_arel_table = association_class.arel_table if association_class
10
+ module WeightedAverage
11
+ # Returns a number
12
+ def weighted_average(*args)
13
+ connection.select_value(weighted_average_relation(*args).to_sql, 'weighted_average').to_f
14
+ end
29
15
 
30
- # set up join ON
31
- join_on = if association_class
32
- raise ArgumentError, "#{association.primary_key_name} isn't a column in the #{association_class.table_name} table" unless association_class.column_names.include?(association.primary_key_name)
33
- join_key = association.options[:primary_key].present? ? association.options[:primary_key] : primary_key
34
- foreign_arel_table[association.primary_key_name].eq arel_table[join_key]
35
- end
36
-
37
- # 'weighting'
38
- weighted_by_column = if association_class and options[:weighted_by].is_a?(Array)
39
- options[:weighted_by].last.to_s
40
- elsif !association_class and (options[:weighted_by].is_a?(String) or options[:weighted_by].is_a?(Symbol))
41
- options[:weighted_by].to_s
42
- else
43
- 'weighting'
44
- end
45
-
46
- # [foreign_]arel_table['weighting']
47
- weighted_by = if foreign_arel_table
48
- foreign_arel_table[weighted_by_column]
49
- else
50
- arel_table[weighted_by_column]
51
- end
16
+ # Returns the ARel relation
17
+ def weighted_average_relation(column_names, options = {})
18
+ 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
19
+ raise ArgumentError, "No nil values in weighted_by, please" if Array.wrap(options[:weighted_by]).any?(&:nil?)
20
+
21
+ # aircraft['seats'] or (aircraft['seats'] + aircraft['payload'])
22
+ columns = Array.wrap(column_names).map { |column_name| arel_table[column_name.to_s] }
52
23
 
53
- disaggregate_by = if options[:disaggregate_by].present?
54
- raise ArgumentError, "Disaggregating by a foreign table isn't supported right now" if options[:disaggregate_by].is_a?(Array)
55
- arel_table[options[:disaggregate_by].to_s]
56
- end
24
+ # :airline_aircraft_seat_class
25
+ association = if options[:weighted_by].present?
26
+ options[:weighted_by].is_a?(Array) ? reflect_on_association(options[:weighted_by].first.to_sym) : reflect_on_association(options[:weighted_by].to_sym)
27
+ end
28
+
29
+ # AirlineAircraftSeatClass
30
+ association_class = association.klass if association
31
+
32
+ # AirlineAircraftSeatClass.arel_table
33
+ foreign_arel_table = association_class.arel_table if association_class
34
+
35
+ # 'weighting'
36
+ weighted_by_column = if association_class and options[:weighted_by].is_a?(Array)
37
+ options[:weighted_by].last.to_s
38
+ elsif !association_class and (options[:weighted_by].is_a?(String) or options[:weighted_by].is_a?(Symbol))
39
+ options[:weighted_by].to_s
40
+ else
41
+ 'weighting'
42
+ end
43
+
44
+ # [foreign_]arel_table['weighting']
45
+ weighted_by = if foreign_arel_table
46
+ foreign_arel_table[weighted_by_column]
47
+ else
48
+ arel_table[weighted_by_column]
49
+ end
57
50
 
58
- # FIXME
59
- # projecting "12345" so that we don't get any other fields back
60
- if foreign_arel_table
61
- foreign_arel_table = foreign_arel_table.project('12345')
62
- end
51
+ disaggregate_by = if options[:disaggregate_by].present?
52
+ raise ArgumentError, "Disaggregating by a foreign table isn't supported right now" if options[:disaggregate_by].is_a?(Array)
53
+ arel_table[options[:disaggregate_by].to_s]
54
+ end
63
55
 
64
- relation = select("(SUM((#{columns.map { |column| column.to_sql }.join(' + ')}) #{"/ #{disaggregate_by.to_sql} " if disaggregate_by}* #{weighted_by.to_sql}) / SUM(#{weighted_by.to_sql})) AS weighted_average")
65
- columns.each do |column|
66
- relation = relation.where("#{column.to_sql} IS NOT NULL")
67
- end
68
- relation = relation.outer_join(foreign_arel_table).on(join_on) if foreign_arel_table
69
- relation
56
+ relation = select("(SUM((#{columns.map { |column| column.to_sql }.join(' + ')}) #{"/ #{disaggregate_by.to_sql} " if disaggregate_by}* #{weighted_by.to_sql}) / SUM(#{weighted_by.to_sql})) AS weighted_average")
57
+ columns.each do |column|
58
+ relation = relation.where("#{column.to_sql} IS NOT NULL")
70
59
  end
60
+ # FIXME this will break on through relationships, where it has to be :aircraft => :aircraft_class
61
+ relation = relation.joins(association.name) if association_class
62
+ relation
71
63
  end
72
64
  end
73
65
 
66
+ ActiveRecord::Associations::AssociationCollection.class_eval do
67
+ delegate :weighted_average, :weighted_average_relation, :to => :scoped
68
+ end
74
69
  ActiveRecord::Base.class_eval do
75
- extend ActiveRecord::WeightedAverage
76
- end
70
+ class << self
71
+ delegate :weighted_average, :weighted_average_relation, :to => :scoped
72
+ end
73
+ end
74
+ ActiveRecord::Relation.send :include, WeightedAverage
data/test/helper.rb CHANGED
@@ -13,6 +13,7 @@ class Test::Unit::TestCase
13
13
  end
14
14
 
15
15
  $logger = Logger.new STDOUT #'test/test.log'
16
+ $logger.level = Logger::INFO
16
17
 
17
18
  ActiveSupport::Notifications.subscribe do |*args|
18
19
  event = ActiveSupport::Notifications::Event.new(*args)
@@ -209,6 +210,8 @@ class Segment < ActiveRecord::Base
209
210
  validates_presence_of :row_hash
210
211
  extend CohortScope
211
212
  self.minimum_cohort_size = 1
213
+ belongs_to :aircraft, :foreign_key => 'bts_aircraft_type', :primary_key => 'bts_aircraft_type'
214
+ has_one :aircraft_class, :through => :aircraft
212
215
  end
213
216
 
214
217
  (1..10).each do |i|
@@ -223,30 +226,23 @@ end
223
226
  # Segment.create! :payload => 6, :row_hash => 'dsiauhiluashdliufhalsidfhuailsd'
224
227
 
225
228
  class AirlineAircraftSeatClass < ActiveRecord::Base
226
- # include CohortScope
227
-
228
- # belongs_to :airline, :class_name => 'Airline', :foreign_key => 'airline_id'
229
- belongs_to :aircraft, :class_name => 'Aircraft', :foreign_key => 'aircraft_id'
230
- # belongs_to :seat_class, :class_name => 'SeatClass', :foreign_key => 'seat_class_id'
231
- has_one :aircraft_class, :class_name => 'AircraftClass', :through => :aircraft
229
+ belongs_to :aircraft
230
+ has_one :aircraft_class, :through => :aircraft
232
231
  end
233
232
 
234
233
 
235
234
  class Aircraft < ActiveRecord::Base
236
- belongs_to :aircraft_class, :class_name => 'AircraftClass', :foreign_key => 'aircraft_class_id'
237
- # belongs_to :manufacturer, :class_name => 'Manufacturer', :foreign_key => 'manufacturer_id'
238
- # has_many :airline_aircraft, :class_name => 'AirlineAircraft'
239
- # has_many :seat_classes, :class_name => 'AircraftSeatClass'
240
- has_many :segments, :class_name => "Segment"
241
- has_many :airline_aircraft_seat_classes, :class_name => 'AirlineAircraftSeatClass'
235
+ has_many :segments, :foreign_key => 'bts_aircraft_type', :primary_key => 'bts_aircraft_type'
236
+ belongs_to :aircraft_class
237
+ has_many :airline_aircraft_seat_classes
242
238
  end
243
239
 
244
240
  class AircraftClass < ActiveRecord::Base
245
- has_many :aircraft, :class_name => 'Aircraft'
241
+ has_many :aircraft
246
242
  has_many :airline_aircraft_seat_classes, :through => :aircraft
247
243
  end
248
244
 
249
245
  class AircraftDeux < ActiveRecord::Base
250
246
  set_primary_key 'icao_code'
251
- has_many :segments, :class_name => "Segment", :primary_key => 'my_bts_aircraft_type_code', :foreign_key => 'bts_aircraft_type'
247
+ has_many :segments, :primary_key => 'my_bts_aircraft_type_code', :foreign_key => 'bts_aircraft_type'
252
248
  end
@@ -95,23 +95,23 @@ class TestWeightedAverage < Test::Unit::TestCase
95
95
  # fake! we would never calc seats this way
96
96
  should "do 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, 12345 FROM `aircraft` LEFT OUTER 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)",
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
  should "do foreign custom weighting" do
106
106
  should_have_same_sql(
107
- "SELECT (SUM((`aircraft`.`m3`) * `segments`.`passengers`) / SUM(`segments`.`passengers`)) AS weighted_average, 12345 FROM `aircraft` LEFT OUTER JOIN `segments` ON `segments`.`aircraft_id` = `aircraft`.`id` 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)",
108
108
  Aircraft.weighted_average_relation(:m3, :weighted_by => [:segments, :passengers])
109
109
  )
110
110
  end
111
111
 
112
112
  should "do 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, 12345 FROM `aircraft_deux` LEFT OUTER 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)",
115
115
  AircraftDeux.weighted_average_relation(:m3, :weighted_by => [:segments, :passengers])
116
116
  )
117
117
  end
@@ -139,7 +139,7 @@ class TestWeightedAverage < Test::Unit::TestCase
139
139
  should "do 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, 12345 FROM `aircraft` LEFT OUTER JOIN `airline_aircraft_seat_classes` ON `airline_aircraft_seat_classes`.`aircraft_id` = `aircraft`.`id` WHERE (#{conditions}) 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)",
143
143
  Aircraft.scoped(:conditions => conditions).weighted_average_relation(:seats, :weighted_by => :airline_aircraft_seat_classes)
144
144
  )
145
145
  end
@@ -147,7 +147,7 @@ class TestWeightedAverage < Test::Unit::TestCase
147
147
  should "do foreign custom weighting, scoped" do
148
148
  conditions = '`aircraft`.`m3` > 1'
149
149
  should_have_same_sql(
150
- "SELECT (SUM((`aircraft`.`m3`) * `segments`.`passengers`) / SUM(`segments`.`passengers`)) AS weighted_average, 12345 FROM `aircraft` LEFT OUTER JOIN `segments` ON `segments`.`aircraft_id` = `aircraft`.`id` WHERE (#{conditions}) 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)",
151
151
  Aircraft.scoped(:conditions => conditions).weighted_average_relation(:m3, :weighted_by => [:segments, :passengers])
152
152
  )
153
153
  end
@@ -160,6 +160,20 @@ class TestWeightedAverage < Test::Unit::TestCase
160
160
  Segment.weighted_average_relation(:load_factor, :weighted_by => :passengers, :disaggregate_by => :departures_performed)
161
161
  )
162
162
  end
163
+
164
+ # more complicated stuff
165
+
166
+ should "construct weightings across has_many through associations (that can be used for updating all)" do
167
+ aircraft_class = AircraftClass.arel_table
168
+ aircraft = Aircraft.arel_table
169
+ segment = Segment.arel_table
170
+
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`)",
173
+ Segment.joins(:aircraft => :aircraft_class).weighted_average_relation(:seats, :weighted_by => :passengers).where(aircraft[:aircraft_class_id].eq(aircraft_class[:id]))
174
+ )
175
+ end
176
+
163
177
 
164
178
  # cohorts (requires the cohort_scope gem)
165
179
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 3
9
- version: 0.0.3
8
+ - 4
9
+ version: 0.0.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Seamus Abshere
@@ -17,7 +17,7 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2010-05-17 00:00:00 -04:00
20
+ date: 2010-05-18 00:00:00 -04:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency