activerecord 3.2.14.rc2 → 3.2.14

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

@@ -1,452 +0,0 @@
1
- require 'active_support/core_ext/hash/indifferent_access'
2
- require 'active_support/core_ext/object/duplicable'
3
- require 'thread'
4
-
5
- module ActiveRecord
6
- module Core
7
- extend ActiveSupport::Concern
8
-
9
- included do
10
- ##
11
- # :singleton-method:
12
- #
13
- # Accepts a logger conforming to the interface of Log4r which is then
14
- # passed on to any new database connections made and which can be
15
- # retrieved on both a class and instance level by calling +logger+.
16
- mattr_accessor :logger, instance_writer: false
17
-
18
- ##
19
- # :singleton-method:
20
- # Contains the database configuration - as is typically stored in config/database.yml -
21
- # as a Hash.
22
- #
23
- # For example, the following database.yml...
24
- #
25
- # development:
26
- # adapter: sqlite3
27
- # database: db/development.sqlite3
28
- #
29
- # production:
30
- # adapter: sqlite3
31
- # database: db/production.sqlite3
32
- #
33
- # ...would result in ActiveRecord::Base.configurations to look like this:
34
- #
35
- # {
36
- # 'development' => {
37
- # 'adapter' => 'sqlite3',
38
- # 'database' => 'db/development.sqlite3'
39
- # },
40
- # 'production' => {
41
- # 'adapter' => 'sqlite3',
42
- # 'database' => 'db/production.sqlite3'
43
- # }
44
- # }
45
- mattr_accessor :configurations, instance_writer: false
46
- self.configurations = {}
47
-
48
- ##
49
- # :singleton-method:
50
- # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
51
- # dates and times from the database. This is set to :utc by default.
52
- mattr_accessor :default_timezone, instance_writer: false
53
- self.default_timezone = :utc
54
-
55
- ##
56
- # :singleton-method:
57
- # Specifies the format to use when dumping the database schema with Rails'
58
- # Rakefile. If :sql, the schema is dumped as (potentially database-
59
- # specific) SQL statements. If :ruby, the schema is dumped as an
60
- # ActiveRecord::Schema file which can be loaded into any database that
61
- # supports migrations. Use :ruby if you want to have different database
62
- # adapters for, e.g., your development and test environments.
63
- mattr_accessor :schema_format, instance_writer: false
64
- self.schema_format = :ruby
65
-
66
- ##
67
- # :singleton-method:
68
- # Specify whether or not to use timestamps for migration versions
69
- mattr_accessor :timestamped_migrations, instance_writer: false
70
- self.timestamped_migrations = true
71
-
72
- <<<<<<< HEAD
73
- ##
74
- # :singleton-method:
75
- # Disable implicit join references. This feature was deprecated with Rails 4.
76
- # If you don't make use of implicit references but still see deprecation warnings
77
- # you can disable the feature entirely. This will be the default with Rails 4.1.
78
- mattr_accessor :disable_implicit_join_references, instance_writer: false
79
- self.disable_implicit_join_references = false
80
-
81
- class_attribute :connection_handler, instance_writer: false
82
- self.connection_handler = ConnectionAdapters::ConnectionHandler.new
83
- =======
84
- class_attribute :default_connection_handler, instance_writer: false
85
-
86
- def self.connection_handler
87
- Thread.current[:active_record_connection_handler] || self.default_connection_handler
88
- end
89
-
90
- def self.connection_handler=(handler)
91
- Thread.current[:active_record_connection_handler] = handler
92
- end
93
-
94
- self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
95
- >>>>>>> SamSaffron/master
96
- end
97
-
98
- module ClassMethods
99
- def inherited(child_class) #:nodoc:
100
- child_class.initialize_generated_modules
101
- super
102
- end
103
-
104
- def initialize_generated_modules
105
- @attribute_methods_mutex = Mutex.new
106
-
107
- # force attribute methods to be higher in inheritance hierarchy than other generated methods
108
- generated_attribute_methods.const_set(:AttrNames, Module.new {
109
- def self.const_missing(name)
110
- const_set(name, [name.to_s.sub(/ATTR_/, '')].pack('h*').freeze)
111
- end
112
- })
113
-
114
- generated_feature_methods
115
- end
116
-
117
- def generated_feature_methods
118
- @generated_feature_methods ||= begin
119
- mod = const_set(:GeneratedFeatureMethods, Module.new)
120
- include mod
121
- mod
122
- end
123
- end
124
-
125
- # Returns a string like 'Post(id:integer, title:string, body:text)'
126
- def inspect
127
- if self == Base
128
- super
129
- elsif abstract_class?
130
- "#{super}(abstract)"
131
- elsif table_exists?
132
- attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
133
- "#{super}(#{attr_list})"
134
- else
135
- "#{super}(Table doesn't exist)"
136
- end
137
- end
138
-
139
- # Overwrite the default class equality method to provide support for association proxies.
140
- def ===(object)
141
- object.is_a?(self)
142
- end
143
-
144
- # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
145
- #
146
- # class Post < ActiveRecord::Base
147
- # scope :published_and_commented, published.and(self.arel_table[:comments_count].gt(0))
148
- # end
149
- def arel_table
150
- @arel_table ||= Arel::Table.new(table_name, arel_engine)
151
- end
152
-
153
- # Returns the Arel engine.
154
- def arel_engine
155
- @arel_engine ||= begin
156
- if Base == self || connection_handler.retrieve_connection_pool(self)
157
- self
158
- else
159
- superclass.arel_engine
160
- end
161
- end
162
- end
163
-
164
- private
165
-
166
- def relation #:nodoc:
167
- relation = Relation.new(self, arel_table)
168
-
169
- if finder_needs_type_condition?
170
- relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
171
- else
172
- relation
173
- end
174
- end
175
- end
176
-
177
- # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
178
- # attributes but not yet saved (pass a hash with key names matching the associated table column names).
179
- # In both instances, valid attribute keys are determined by the column names of the associated table --
180
- # hence you can't have attributes that aren't part of the table columns.
181
- #
182
- # ==== Example:
183
- # # Instantiates a single new object
184
- # User.new(first_name: 'Jamie')
185
- def initialize(attributes = nil)
186
- defaults = self.class.column_defaults.dup
187
- defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
188
-
189
- @attributes = self.class.initialize_attributes(defaults)
190
- @columns_hash = self.class.column_types.dup
191
-
192
- init_internals
193
- ensure_proper_type
194
- populate_with_current_scope_attributes
195
-
196
- assign_attributes(attributes) if attributes
197
-
198
- yield self if block_given?
199
- run_callbacks :initialize unless _initialize_callbacks.empty?
200
- end
201
-
202
- # Initialize an empty model object from +coder+. +coder+ must contain
203
- # the attributes necessary for initializing an empty model object. For
204
- # example:
205
- #
206
- # class Post < ActiveRecord::Base
207
- # end
208
- #
209
- # post = Post.allocate
210
- # post.init_with('attributes' => { 'title' => 'hello world' })
211
- # post.title # => 'hello world'
212
- def init_with(coder)
213
- @attributes = self.class.initialize_attributes(coder['attributes'])
214
- @columns_hash = self.class.column_types.merge(coder['column_types'] || {})
215
-
216
- init_internals
217
-
218
- @new_record = false
219
-
220
- run_callbacks :find
221
- run_callbacks :initialize
222
-
223
- self
224
- end
225
-
226
- ##
227
- # :method: clone
228
- # Identical to Ruby's clone method. This is a "shallow" copy. Be warned that your attributes are not copied.
229
- # That means that modifying attributes of the clone will modify the original, since they will both point to the
230
- # same attributes hash. If you need a copy of your attributes hash, please use the #dup method.
231
- #
232
- # user = User.first
233
- # new_user = user.clone
234
- # user.name # => "Bob"
235
- # new_user.name = "Joe"
236
- # user.name # => "Joe"
237
- #
238
- # user.object_id == new_user.object_id # => false
239
- # user.name.object_id == new_user.name.object_id # => true
240
- #
241
- # user.name.object_id == user.dup.name.object_id # => false
242
-
243
- ##
244
- # :method: dup
245
- # Duped objects have no id assigned and are treated as new records. Note
246
- # that this is a "shallow" copy as it copies the object's attributes
247
- # only, not its associations. The extent of a "deep" copy is application
248
- # specific and is therefore left to the application to implement according
249
- # to its need.
250
- # The dup method does not preserve the timestamps (created|updated)_(at|on).
251
-
252
- ##
253
- def initialize_dup(other) # :nodoc:
254
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
255
- self.class.initialize_attributes(cloned_attributes, :serialized => false)
256
-
257
- @attributes = cloned_attributes
258
- @attributes[self.class.primary_key] = nil
259
-
260
- run_callbacks(:initialize) unless _initialize_callbacks.empty?
261
-
262
- @changed_attributes = {}
263
- self.class.column_defaults.each do |attr, orig_value|
264
- @changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
265
- end
266
-
267
- @aggregation_cache = {}
268
- @association_cache = {}
269
- @attributes_cache = {}
270
-
271
- @new_record = true
272
-
273
- ensure_proper_type
274
- super
275
- end
276
-
277
- # Populate +coder+ with attributes about this record that should be
278
- # serialized. The structure of +coder+ defined in this method is
279
- # guaranteed to match the structure of +coder+ passed to the +init_with+
280
- # method.
281
- #
282
- # Example:
283
- #
284
- # class Post < ActiveRecord::Base
285
- # end
286
- # coder = {}
287
- # Post.new.encode_with(coder)
288
- # coder # => {"attributes" => {"id" => nil, ... }}
289
- def encode_with(coder)
290
- coder['attributes'] = attributes
291
- end
292
-
293
- # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
294
- # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
295
- #
296
- # Note that new records are different from any other record by definition, unless the
297
- # other record is the receiver itself. Besides, if you fetch existing records with
298
- # +select+ and leave the ID out, you're on your own, this predicate will return false.
299
- #
300
- # Note also that destroying a record preserves its ID in the model instance, so deleted
301
- # models are still comparable.
302
- def ==(comparison_object)
303
- super ||
304
- comparison_object.instance_of?(self.class) &&
305
- id.present? &&
306
- comparison_object.id == id
307
- end
308
- alias :eql? :==
309
-
310
- # Delegates to id in order to allow two records of the same type and id to work with something like:
311
- # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
312
- def hash
313
- id.hash
314
- end
315
-
316
- # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
317
- def freeze
318
- @attributes.freeze
319
- self
320
- end
321
-
322
- # Returns +true+ if the attributes hash has been frozen.
323
- def frozen?
324
- @attributes.frozen?
325
- end
326
-
327
- # Allows sort on objects
328
- def <=>(other_object)
329
- if other_object.is_a?(self.class)
330
- self.to_key <=> other_object.to_key
331
- end
332
- end
333
-
334
- # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
335
- # attributes will be marked as read only since they cannot be saved.
336
- def readonly?
337
- @readonly
338
- end
339
-
340
- # Marks this record as read only.
341
- def readonly!
342
- @readonly = true
343
- end
344
-
345
- # Returns the connection currently associated with the class. This can
346
- # also be used to "borrow" the connection to do database work that isn't
347
- # easily done without going straight to SQL.
348
- def connection
349
- ActiveSupport::Deprecation.warn("#connection is deprecated in favour of accessing it via the class")
350
- self.class.connection
351
- end
352
-
353
- # Returns the contents of the record as a nicely formatted string.
354
- def inspect
355
- inspection = if @attributes
356
- self.class.column_names.collect { |name|
357
- if has_attribute?(name)
358
- "#{name}: #{attribute_for_inspect(name)}"
359
- end
360
- }.compact.join(", ")
361
- else
362
- "not initialized"
363
- end
364
- "#<#{self.class} #{inspection}>"
365
- end
366
-
367
- # Returns a hash of the given methods with their names as keys and returned values as values.
368
- def slice(*methods)
369
- Hash[methods.map { |method| [method, public_send(method)] }].with_indifferent_access
370
- end
371
-
372
- def set_transaction_state(state) # :nodoc:
373
- @transaction_state = state
374
- end
375
-
376
- def has_transactional_callbacks? # :nodoc:
377
- !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_create_callbacks.empty?
378
- end
379
-
380
- private
381
-
382
- # Updates the attributes on this particular ActiveRecord object so that
383
- # if it is associated with a transaction, then the state of the AR object
384
- # will be updated to reflect the current state of the transaction
385
- #
386
- # The @transaction_state variable stores the states of the associated
387
- # transaction. This relies on the fact that a transaction can only be in
388
- # one rollback or commit (otherwise a list of states would be required)
389
- # Each AR object inside of a transaction carries that transaction's
390
- # TransactionState.
391
- #
392
- # This method checks to see if the ActiveRecord object's state reflects
393
- # the TransactionState, and rolls back or commits the ActiveRecord object
394
- # as appropriate.
395
- #
396
- # Since ActiveRecord objects can be inside multiple transactions, this
397
- # method recursively goes through the parent of the TransactionState and
398
- # checks if the ActiveRecord object reflects the state of the object.
399
- def sync_with_transaction_state
400
- update_attributes_from_transaction_state(@transaction_state, 0)
401
- end
402
-
403
- def update_attributes_from_transaction_state(transaction_state, depth)
404
- if transaction_state && !has_transactional_callbacks?
405
- unless @reflects_state[depth]
406
- if transaction_state.committed?
407
- committed!
408
- elsif transaction_state.rolledback?
409
- rolledback!
410
- end
411
- @reflects_state[depth] = true
412
- end
413
-
414
- if transaction_state.parent && !@reflects_state[depth+1]
415
- update_attributes_from_transaction_state(transaction_state.parent, depth+1)
416
- end
417
- end
418
- end
419
-
420
- # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
421
- # of the array, and then rescues from the possible NoMethodError. If those elements are
422
- # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
423
- # which significantly impacts upon performance.
424
- #
425
- # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
426
- #
427
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
428
- def to_ary # :nodoc:
429
- nil
430
- end
431
-
432
- def init_internals
433
- pk = self.class.primary_key
434
- @attributes[pk] = nil unless @attributes.key?(pk)
435
-
436
- @aggregation_cache = {}
437
- @association_cache = {}
438
- @attributes_cache = {}
439
- @previously_changed = {}
440
- @changed_attributes = {}
441
- @readonly = false
442
- @destroyed = false
443
- @marked_for_destruction = false
444
- @destroyed_by_association = nil
445
- @new_record = true
446
- @txn = nil
447
- @_start_transaction_state = {}
448
- @transaction_state = nil
449
- @reflects_state = [false]
450
- end
451
- end
452
- end
@@ -1,378 +0,0 @@
1
- require 'active_support/core_ext/object/blank'
2
- require 'active_support/core_ext/object/try'
3
-
4
- module ActiveRecord
5
- module Calculations
6
- # Count operates using three different approaches.
7
- #
8
- # * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
9
- # * Count using column: By passing a column name to count, it will return a count of all the
10
- # rows for the model with supplied column present.
11
- # * Count using options will find the row count matched by the options used.
12
- #
13
- # The third approach, count using options, accepts an option hash as the only parameter. The options are:
14
- #
15
- # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
16
- # See conditions in the intro to ActiveRecord::Base.
17
- # * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id"
18
- # (rarely needed) or named associations in the same form used for the <tt>:include</tt> option, which will
19
- # perform an INNER JOIN on the associated table(s). If the value is a string, then the records
20
- # will be returned read-only since they will have attributes that do not correspond to the table's columns.
21
- # Pass <tt>:readonly => false</tt> to override.
22
- # * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs.
23
- # The symbols named refer to already defined associations. When using named associations, count
24
- # returns the number of DISTINCT items for the model you're counting.
25
- # See eager loading under Associations.
26
- # * <tt>:order</tt>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
27
- # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
28
- # * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example,
29
- # want to do a join but not include the joined columns.
30
- # * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as
31
- # SELECT COUNT(DISTINCT posts.id) ...
32
- # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an
33
- # alternate table name (or even the name of a database view).
34
- #
35
- # Examples for counting all:
36
- # Person.count # returns the total count of all people
37
- #
38
- # Examples for counting by column:
39
- # Person.count(:age) # returns the total count of all people whose age is present in database
40
- #
41
- # Examples for count with options:
42
- # Person.count(:conditions => "age > 26")
43
- #
44
- # # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
45
- # Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job)
46
- #
47
- # # finds the number of rows matching the conditions and joins.
48
- # Person.count(:conditions => "age > 26 AND job.salary > 60000",
49
- # :joins => "LEFT JOIN jobs on jobs.person_id = person.id")
50
- #
51
- # Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
52
- # Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
53
- #
54
- # Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.
55
- # Use Person.count instead.
56
- def count(column_name = nil, options = {})
57
- column_name, options = nil, column_name if column_name.is_a?(Hash)
58
- calculate(:count, column_name, options)
59
- end
60
-
61
- # Calculates the average value on a given column. Returns +nil+ if there's
62
- # no row. See +calculate+ for examples with options.
63
- #
64
- # Person.average('age') # => 35.8
65
- def average(column_name, options = {})
66
- calculate(:average, column_name, options)
67
- end
68
-
69
- # Calculates the minimum value on a given column. The value is returned
70
- # with the same data type of the column, or +nil+ if there's no row. See
71
- # +calculate+ for examples with options.
72
- #
73
- # Person.minimum('age') # => 7
74
- def minimum(column_name, options = {})
75
- calculate(:minimum, column_name, options)
76
- end
77
-
78
- # Calculates the maximum value on a given column. The value is returned
79
- # with the same data type of the column, or +nil+ if there's no row. See
80
- # +calculate+ for examples with options.
81
- #
82
- # Person.maximum('age') # => 93
83
- def maximum(column_name, options = {})
84
- calculate(:maximum, column_name, options)
85
- end
86
-
87
- # Calculates the sum of values on a given column. The value is returned
88
- # with the same data type of the column, 0 if there's no row. See
89
- # +calculate+ for examples with options.
90
- #
91
- # Person.sum('age') # => 4562
92
- def sum(*args)
93
- if block_given?
94
- self.to_a.sum(*args) {|*block_args| yield(*block_args)}
95
- else
96
- calculate(:sum, *args)
97
- end
98
- end
99
-
100
- # This calculates aggregate values in the given column. Methods for count, sum, average,
101
- # minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
102
- # <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
103
- #
104
- # There are two basic forms of output:
105
- # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
106
- # for AVG, and the given column's type for everything else.
107
- # * Grouped values: This returns an ordered hash of the values and groups them by the
108
- # <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
109
- #
110
- # values = Person.maximum(:age, :group => 'last_name')
111
- # puts values["Drake"]
112
- # => 43
113
- #
114
- # drake = Family.find_by_last_name('Drake')
115
- # values = Person.maximum(:age, :group => :family) # Person belongs_to :family
116
- # puts values[drake]
117
- # => 43
118
- #
119
- # values.each do |family, max_age|
120
- # ...
121
- # end
122
- #
123
- # Options:
124
- # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
125
- # See conditions in the intro to ActiveRecord::Base.
126
- # * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
127
- # the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
128
- # * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
129
- # (Rarely needed).
130
- # The records will be returned read-only since they will have attributes that do not correspond to the
131
- # table's columns.
132
- # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
133
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
134
- # * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example
135
- # want to do a join, but not include the joined columns.
136
- # * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as
137
- # SELECT COUNT(DISTINCT posts.id) ...
138
- #
139
- # Examples:
140
- # Person.calculate(:count, :all) # The same as Person.count
141
- # Person.average(:age) # SELECT AVG(age) FROM people...
142
- # Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for
143
- # # everyone with a last name other than 'Drake'
144
- #
145
- # # Selects the minimum age for any family without any minors
146
- # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name)
147
- #
148
- # Person.sum("2 * age")
149
- def calculate(operation, column_name, options = {})
150
- if options.except(:distinct).present?
151
- apply_finder_options(options.except(:distinct)).calculate(operation, column_name, :distinct => options[:distinct])
152
- else
153
- relation = with_default_scope
154
-
155
- if relation.equal?(self)
156
- if eager_loading? || (includes_values.present? && references_eager_loaded_tables?)
157
- construct_relation_for_association_calculations.calculate(operation, column_name, options)
158
- else
159
- perform_calculation(operation, column_name, options)
160
- end
161
- else
162
- relation.calculate(operation, column_name, options)
163
- end
164
- end
165
- rescue ThrowResult
166
- 0
167
- end
168
-
169
- # This method is designed to perform select by a single column as direct SQL query
170
- # Returns <tt>Array</tt> with values of the specified column name
171
- # The values has same data type as column.
172
- #
173
- # Examples:
174
- #
175
- # Person.pluck(:id) # SELECT people.id FROM people
176
- # Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
177
- # Person.where(:confirmed => true).limit(5).pluck(:id)
178
- #
179
- def pluck(column_name)
180
- <<<<<<< HEAD
181
- if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
182
- column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
183
- else
184
- column_name = column_name.to_s
185
- end
186
-
187
- relation = clone
188
- relation.select_values = [column_name]
189
- klass.connection.select_all(relation.arel).map! do |attributes|
190
- =======
191
- column_name = column_name.to_s
192
- klass.connection.select_all(select(column_name).arel).map! do |attributes|
193
- >>>>>>> parent of 7240202... Merge pull request #8209 from senny/backport_8176
194
- klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
195
- end
196
- end
197
-
198
- private
199
-
200
- def perform_calculation(operation, column_name, options = {})
201
- operation = operation.to_s.downcase
202
-
203
- # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
204
- distinct = options[:distinct] || self.uniq_value
205
-
206
- if operation == "count"
207
- column_name ||= (select_for_count || :all)
208
-
209
- unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
210
- distinct = true
211
- end
212
-
213
- column_name = primary_key if column_name == :all && distinct
214
-
215
- distinct = nil if column_name =~ /\s*DISTINCT\s+/i
216
- end
217
-
218
- if @group_values.any?
219
- execute_grouped_calculation(operation, column_name, distinct)
220
- else
221
- execute_simple_calculation(operation, column_name, distinct)
222
- end
223
- end
224
-
225
- def aggregate_column(column_name)
226
- if @klass.column_names.include?(column_name.to_s)
227
- Arel::Attribute.new(@klass.unscoped.table, column_name)
228
- else
229
- Arel.sql(column_name == :all ? "*" : column_name.to_s)
230
- end
231
- end
232
-
233
- def operation_over_aggregate_column(column, operation, distinct)
234
- operation == 'count' ? column.count(distinct) : column.send(operation)
235
- end
236
-
237
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
238
- # Postgresql doesn't like ORDER BY when there are no GROUP BY
239
- relation = reorder(nil)
240
-
241
- if operation == "count" && (relation.limit_value || relation.offset_value)
242
- # Shortcut when limit is zero.
243
- return 0 if relation.limit_value == 0
244
-
245
- query_builder = build_count_subquery(relation, column_name, distinct)
246
- else
247
- column = aggregate_column(column_name)
248
-
249
- select_value = operation_over_aggregate_column(column, operation, distinct)
250
-
251
- relation.select_values = [select_value]
252
-
253
- query_builder = relation.arel
254
- end
255
-
256
- type_cast_calculated_value(@klass.connection.select_value(query_builder), column_for(column_name), operation)
257
- end
258
-
259
- def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
260
- group_attrs = @group_values
261
-
262
- if group_attrs.first.respond_to?(:to_sym)
263
- association = @klass.reflect_on_association(group_attrs.first.to_sym)
264
- associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
265
- group_fields = Array(associated ? association.foreign_key : group_attrs)
266
- else
267
- group_fields = group_attrs
268
- end
269
-
270
- group_aliases = group_fields.map { |field| column_alias_for(field) }
271
- group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
272
- [aliaz, column_for(field)]
273
- }
274
-
275
- group = @klass.connection.adapter_name == 'FrontBase' ? group_aliases : group_fields
276
-
277
- if operation == 'count' && column_name == :all
278
- aggregate_alias = 'count_all'
279
- else
280
- aggregate_alias = column_alias_for(operation, column_name)
281
- end
282
-
283
- select_values = [
284
- operation_over_aggregate_column(
285
- aggregate_column(column_name),
286
- operation,
287
- distinct).as(aggregate_alias)
288
- ]
289
- select_values += @select_values unless @having_values.empty?
290
-
291
- select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
292
- if field.respond_to?(:as)
293
- field.as(aliaz)
294
- else
295
- "#{field} AS #{aliaz}"
296
- end
297
- }
298
-
299
- relation = except(:group).group(group)
300
- relation.select_values = select_values
301
-
302
- calculated_data = @klass.connection.select_all(relation)
303
-
304
- if association
305
- key_ids = calculated_data.collect { |row| row[group_aliases.first] }
306
- key_records = association.klass.base_class.find(key_ids)
307
- key_records = Hash[key_records.map { |r| [r.id, r] }]
308
- end
309
-
310
- ActiveSupport::OrderedHash[calculated_data.map do |row|
311
- key = group_columns.map { |aliaz, column|
312
- type_cast_calculated_value(row[aliaz], column)
313
- }
314
- key = key.first if key.size == 1
315
- key = key_records[key] if associated
316
- [key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
317
- end]
318
- end
319
-
320
- # Converts the given keys to the value that the database adapter returns as
321
- # a usable column name:
322
- #
323
- # column_alias_for("users.id") # => "users_id"
324
- # column_alias_for("sum(id)") # => "sum_id"
325
- # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
326
- # column_alias_for("count(*)") # => "count_all"
327
- # column_alias_for("count", "id") # => "count_id"
328
- def column_alias_for(*keys)
329
- keys.map! {|k| k.respond_to?(:to_sql) ? k.to_sql : k}
330
- table_name = keys.join(' ')
331
- table_name.downcase!
332
- table_name.gsub!(/\*/, 'all')
333
- table_name.gsub!(/\W+/, ' ')
334
- table_name.strip!
335
- table_name.gsub!(/ +/, '_')
336
-
337
- @klass.connection.table_alias_for(table_name)
338
- end
339
-
340
- def column_for(field)
341
- field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
342
- @klass.columns.detect { |c| c.name.to_s == field_name }
343
- end
344
-
345
- def type_cast_calculated_value(value, column, operation = nil)
346
- case operation
347
- when 'count' then value.to_i
348
- when 'sum' then type_cast_using_column(value || '0', column)
349
- when 'average' then value.respond_to?(:to_d) ? value.to_d : value
350
- else type_cast_using_column(value, column)
351
- end
352
- end
353
-
354
- def type_cast_using_column(value, column)
355
- column ? column.type_cast(value) : value
356
- end
357
-
358
- def select_for_count
359
- if @select_values.present?
360
- select = @select_values.join(", ")
361
- select if select !~ /(,|\*)/
362
- end
363
- end
364
-
365
- def build_count_subquery(relation, column_name, distinct)
366
- column_alias = Arel.sql('count_column')
367
- subquery_alias = Arel.sql('subquery_for_count')
368
-
369
- aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
370
- relation.select_values = [aliased_column]
371
- subquery = relation.arel.as(subquery_alias)
372
-
373
- sm = Arel::SelectManager.new relation.engine
374
- select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
375
- sm.project(select_value).from(subquery)
376
- end
377
- end
378
- end