activerecord 5.1.0.beta1 → 5.1.0.rc1

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.

Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +93 -6
  3. data/lib/active_record/associations.rb +4 -0
  4. data/lib/active_record/associations/association_scope.rb +8 -8
  5. data/lib/active_record/associations/belongs_to_association.rb +4 -0
  6. data/lib/active_record/associations/builder/belongs_to.rb +8 -1
  7. data/lib/active_record/associations/collection_proxy.rb +5 -4
  8. data/lib/active_record/associations/join_dependency.rb +1 -1
  9. data/lib/active_record/associations/join_dependency/join_association.rb +4 -23
  10. data/lib/active_record/attribute_methods/dirty.rb +3 -3
  11. data/lib/active_record/connection_adapters/abstract/quoting.rb +20 -3
  12. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +3 -7
  13. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +30 -16
  14. data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -5
  15. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +16 -80
  16. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +33 -0
  17. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  18. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  19. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +56 -96
  20. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  21. data/lib/active_record/connection_adapters/postgresql_adapter.rb +4 -12
  22. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
  23. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -51
  24. data/lib/active_record/core.rb +0 -1
  25. data/lib/active_record/gem_version.rb +1 -1
  26. data/lib/active_record/locking/optimistic.rb +2 -6
  27. data/lib/active_record/migration.rb +32 -17
  28. data/lib/active_record/null_relation.rb +1 -1
  29. data/lib/active_record/querying.rb +1 -1
  30. data/lib/active_record/railties/databases.rake +3 -19
  31. data/lib/active_record/reflection.rb +67 -16
  32. data/lib/active_record/relation.rb +0 -4
  33. data/lib/active_record/relation/calculations.rb +7 -10
  34. data/lib/active_record/relation/delegation.rb +2 -2
  35. data/lib/active_record/relation/finder_methods.rb +102 -100
  36. data/lib/active_record/relation/query_methods.rb +6 -1
  37. data/lib/active_record/result.rb +12 -1
  38. data/lib/active_record/sanitization.rb +1 -2
  39. data/lib/active_record/schema_dumper.rb +1 -1
  40. data/lib/active_record/schema_migration.rb +5 -1
  41. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -0
  42. data/lib/active_record/transactions.rb +1 -1
  43. data/lib/active_record/type/decimal_without_scale.rb +4 -0
  44. data/lib/active_record/type/serialized.rb +2 -0
  45. data/lib/rails/generators/active_record/migration.rb +1 -1
  46. metadata +8 -6
@@ -261,10 +261,6 @@ module ActiveRecord
261
261
  coder.represent_seq(nil, records)
262
262
  end
263
263
 
264
- def as_json(options = nil) #:nodoc:
265
- records.as_json(options)
266
- end
267
-
268
264
  # Returns size of the records.
269
265
  def size
270
266
  loaded? ? @records.length : count(:all)
@@ -37,11 +37,8 @@ module ActiveRecord
37
37
  # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
38
38
  # between databases. In invalid cases, an error from the database is thrown.
39
39
  def count(column_name = nil)
40
- if block_given?
41
- to_a.count { |*block_args| yield(*block_args) }
42
- else
43
- calculate(:count, column_name)
44
- end
40
+ return super() if block_given?
41
+ calculate(:count, column_name)
45
42
  end
46
43
 
47
44
  # Calculates the average value on a given column. Returns +nil+ if there's
@@ -75,8 +72,8 @@ module ActiveRecord
75
72
  # #calculate for examples with options.
76
73
  #
77
74
  # Person.sum(:age) # => 4562
78
- def sum(column_name = nil, &block)
79
- return super(&block) if block_given?
75
+ def sum(column_name = nil)
76
+ return super() if block_given?
80
77
  calculate(:sum, column_name)
81
78
  end
82
79
 
@@ -232,7 +229,7 @@ module ActiveRecord
232
229
  query_builder = build_count_subquery(spawn, column_name, distinct)
233
230
  else
234
231
  # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
235
- relation = unscope(:order)
232
+ relation = unscope(:order).distinct!(false)
236
233
 
237
234
  column = aggregate_column(column_name)
238
235
 
@@ -282,7 +279,7 @@ module ActiveRecord
282
279
  operation,
283
280
  distinct).as(aggregate_alias)
284
281
  ]
285
- select_values += select_values unless having_clause.empty?
282
+ select_values += self.select_values unless having_clause.empty?
286
283
 
287
284
  select_values.concat group_columns.map { |aliaz, field|
288
285
  if field.respond_to?(:as)
@@ -292,7 +289,7 @@ module ActiveRecord
292
289
  end
293
290
  }
294
291
 
295
- relation = except(:group)
292
+ relation = except(:group).distinct!(false)
296
293
  relation.group_values = group_fields
297
294
  relation.select_values = select_values
298
295
 
@@ -36,9 +36,9 @@ module ActiveRecord
36
36
  # may vary depending on the klass of a relation, so we create a subclass of Relation
37
37
  # for each different klass, and the delegations are compiled into that subclass only.
38
38
 
39
- delegate :to_xml, :encode_with, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
39
+ delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
40
40
  :[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
41
- :to_sentence, :to_formatted_s,
41
+ :to_sentence, :to_formatted_s, :as_json,
42
42
  :shuffle, :split, :index, to: :records
43
43
 
44
44
  delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
@@ -147,7 +147,7 @@ module ActiveRecord
147
147
  def last(limit = nil)
148
148
  return find_last(limit) if loaded? || limit_value
149
149
 
150
- result = limit(limit || 1)
150
+ result = limit(limit)
151
151
  result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
152
152
  result = result.reverse_order!
153
153
 
@@ -430,140 +430,142 @@ module ActiveRecord
430
430
  reflections.none?(&:collection?)
431
431
  end
432
432
 
433
- private
433
+ def find_with_ids(*ids)
434
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
434
435
 
435
- def find_with_ids(*ids)
436
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
436
+ expects_array = ids.first.kind_of?(Array)
437
+ return ids.first if expects_array && ids.first.empty?
437
438
 
438
- expects_array = ids.first.kind_of?(Array)
439
- return ids.first if expects_array && ids.first.empty?
439
+ ids = ids.flatten.compact.uniq
440
440
 
441
- ids = ids.flatten.compact.uniq
442
-
443
- case ids.size
444
- when 0
445
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
446
- when 1
447
- result = find_one(ids.first)
448
- expects_array ? [ result ] : result
449
- else
450
- find_some(ids)
451
- end
452
- rescue ::RangeError
453
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
441
+ case ids.size
442
+ when 0
443
+ raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
444
+ when 1
445
+ result = find_one(ids.first)
446
+ expects_array ? [ result ] : result
447
+ else
448
+ find_some(ids)
454
449
  end
450
+ rescue ::RangeError
451
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
452
+ end
455
453
 
456
- def find_one(id)
457
- if ActiveRecord::Base === id
458
- raise ArgumentError, <<-MSG.squish
459
- You are passing an instance of ActiveRecord::Base to `find`.
460
- Please pass the id of the object by calling `.id`.
461
- MSG
462
- end
463
-
464
- relation = where(primary_key => id)
465
- record = relation.take
466
-
467
- raise_record_not_found_exception!(id, 0, 1) unless record
468
-
469
- record
454
+ def find_one(id)
455
+ if ActiveRecord::Base === id
456
+ raise ArgumentError, <<-MSG.squish
457
+ You are passing an instance of ActiveRecord::Base to `find`.
458
+ Please pass the id of the object by calling `.id`.
459
+ MSG
470
460
  end
471
461
 
472
- def find_some(ids)
473
- return find_some_ordered(ids) unless order_values.present?
462
+ relation = where(primary_key => id)
463
+ record = relation.take
474
464
 
475
- result = where(primary_key => ids).to_a
465
+ raise_record_not_found_exception!(id, 0, 1) unless record
476
466
 
477
- expected_size =
478
- if limit_value && ids.size > limit_value
479
- limit_value
480
- else
481
- ids.size
482
- end
467
+ record
468
+ end
483
469
 
484
- # 11 ids with limit 3, offset 9 should give 2 results.
485
- if offset_value && (ids.size - offset_value < expected_size)
486
- expected_size = ids.size - offset_value
487
- end
470
+ def find_some(ids)
471
+ return find_some_ordered(ids) unless order_values.present?
488
472
 
489
- if result.size == expected_size
490
- result
473
+ result = where(primary_key => ids).to_a
474
+
475
+ expected_size =
476
+ if limit_value && ids.size > limit_value
477
+ limit_value
491
478
  else
492
- raise_record_not_found_exception!(ids, result.size, expected_size)
479
+ ids.size
493
480
  end
481
+
482
+ # 11 ids with limit 3, offset 9 should give 2 results.
483
+ if offset_value && (ids.size - offset_value < expected_size)
484
+ expected_size = ids.size - offset_value
494
485
  end
495
486
 
496
- def find_some_ordered(ids)
497
- ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
487
+ if result.size == expected_size
488
+ result
489
+ else
490
+ raise_record_not_found_exception!(ids, result.size, expected_size)
491
+ end
492
+ end
498
493
 
499
- result = except(:limit, :offset).where(primary_key => ids).records
494
+ def find_some_ordered(ids)
495
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
500
496
 
501
- if result.size == ids.size
502
- pk_type = @klass.type_for_attribute(primary_key)
497
+ result = except(:limit, :offset).where(primary_key => ids).records
503
498
 
504
- records_by_id = result.index_by(&:id)
505
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
506
- else
507
- raise_record_not_found_exception!(ids, result.size, ids.size)
508
- end
509
- end
499
+ if result.size == ids.size
500
+ pk_type = @klass.type_for_attribute(primary_key)
510
501
 
511
- def find_take
512
- if loaded?
513
- records.first
514
- else
515
- @take ||= limit(1).records.first
516
- end
502
+ records_by_id = result.index_by(&:id)
503
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
504
+ else
505
+ raise_record_not_found_exception!(ids, result.size, ids.size)
517
506
  end
507
+ end
518
508
 
519
- def find_take_with_limit(limit)
520
- if loaded?
521
- records.take(limit)
522
- else
523
- limit(limit).to_a
524
- end
509
+ def find_take
510
+ if loaded?
511
+ records.first
512
+ else
513
+ @take ||= limit(1).records.first
525
514
  end
515
+ end
526
516
 
527
- def find_nth(index)
528
- @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
517
+ def find_take_with_limit(limit)
518
+ if loaded?
519
+ records.take(limit)
520
+ else
521
+ limit(limit).to_a
529
522
  end
523
+ end
524
+
525
+ def find_nth(index)
526
+ @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
527
+ end
530
528
 
531
- def find_nth_with_limit(index, limit)
532
- if loaded?
533
- records[index, limit] || []
529
+ def find_nth_with_limit(index, limit)
530
+ if loaded?
531
+ records[index, limit] || []
532
+ else
533
+ relation = if order_values.empty? && primary_key
534
+ order(arel_attribute(primary_key).asc)
534
535
  else
535
- relation = if order_values.empty? && primary_key
536
- order(arel_attribute(primary_key).asc)
537
- else
538
- self
539
- end
536
+ self
537
+ end
540
538
 
539
+ if limit_value.nil? || index < limit_value
541
540
  relation = relation.offset(offset_index + index) unless index.zero?
542
541
  relation.limit(limit).to_a
542
+ else
543
+ []
543
544
  end
544
545
  end
546
+ end
545
547
 
546
- def find_nth_from_last(index)
547
- if loaded?
548
- records[-index]
548
+ def find_nth_from_last(index)
549
+ if loaded?
550
+ records[-index]
551
+ else
552
+ relation = if order_values.empty? && primary_key
553
+ order(arel_attribute(primary_key).asc)
549
554
  else
550
- relation = if order_values.empty? && primary_key
551
- order(arel_attribute(primary_key).asc)
552
- else
553
- self
554
- end
555
-
556
- relation.to_a[-index]
557
- # TODO: can be made more performant on large result sets by
558
- # for instance, last(index)[-index] (which would require
559
- # refactoring the last(n) finder method to make test suite pass),
560
- # or by using a combination of reverse_order, limit, and offset,
561
- # e.g., reverse_order.offset(index-1).first
555
+ self
562
556
  end
563
- end
564
557
 
565
- def find_last(limit)
566
- limit ? records.last(limit) : records.last
558
+ relation.to_a[-index]
559
+ # TODO: can be made more performant on large result sets by
560
+ # for instance, last(index)[-index] (which would require
561
+ # refactoring the last(n) finder method to make test suite pass),
562
+ # or by using a combination of reverse_order, limit, and offset,
563
+ # e.g., reverse_order.offset(index-1).first
567
564
  end
565
+ end
566
+
567
+ def find_last(limit)
568
+ limit ? records.last(limit) : records.last
569
+ end
568
570
  end
569
571
  end
@@ -1130,7 +1130,12 @@ module ActiveRecord
1130
1130
  arel_attribute(arg).asc
1131
1131
  when Hash
1132
1132
  arg.map { |field, dir|
1133
- arel_attribute(field).send(dir.downcase)
1133
+ case field
1134
+ when Arel::Nodes::SqlLiteral
1135
+ field.send(dir.downcase)
1136
+ else
1137
+ arel_attribute(field).send(dir.downcase)
1138
+ end
1134
1139
  }
1135
1140
  else
1136
1141
  arg
@@ -41,10 +41,15 @@ module ActiveRecord
41
41
  @column_types = column_types
42
42
  end
43
43
 
44
+ # Returns the number of elements in the rows array.
44
45
  def length
45
46
  @rows.length
46
47
  end
47
48
 
49
+ # Calls the given block once for each element in row collection, passing
50
+ # row as parameter.
51
+ #
52
+ # Returns an +Enumerator+ if no block is given.
48
53
  def each
49
54
  if block_given?
50
55
  hash_rows.each { |row| yield row }
@@ -53,6 +58,7 @@ module ActiveRecord
53
58
  end
54
59
  end
55
60
 
61
+ # Returns an array of hashes representing each row record.
56
62
  def to_hash
57
63
  hash_rows
58
64
  end
@@ -60,11 +66,12 @@ module ActiveRecord
60
66
  alias :map! :map
61
67
  alias :collect! :map
62
68
 
63
- # Returns true if there are no records.
69
+ # Returns true if there are no records, otherwise false.
64
70
  def empty?
65
71
  rows.empty?
66
72
  end
67
73
 
74
+ # Returns an array of hashes representing each row record.
68
75
  def to_ary
69
76
  hash_rows
70
77
  end
@@ -73,11 +80,15 @@ module ActiveRecord
73
80
  hash_rows[idx]
74
81
  end
75
82
 
83
+ # Returns the first record from the rows collection.
84
+ # If the rows collection is empty, returns +nil+.
76
85
  def first
77
86
  return nil if @rows.empty?
78
87
  Hash[@columns.zip(@rows.first)]
79
88
  end
80
89
 
90
+ # Returns the last record from the rows collection.
91
+ # If the rows collection is empty, returns +nil+.
81
92
  def last
82
93
  return nil if @rows.empty?
83
94
  Hash[@columns.zip(@rows.last)]
@@ -1,4 +1,3 @@
1
-
2
1
  module ActiveRecord
3
2
  module Sanitization
4
3
  extend ActiveSupport::Concern
@@ -207,9 +206,9 @@ module ActiveRecord
207
206
  end
208
207
  end
209
208
 
210
- # TODO: Deprecate this
211
209
  def quoted_id # :nodoc:
212
210
  self.class.connection.quote(@attributes[self.class.primary_key].value_for_database)
213
211
  end
212
+ deprecate :quoted_id
214
213
  end
215
214
  end
@@ -85,7 +85,7 @@ HEADER
85
85
  end
86
86
 
87
87
  def tables(stream)
88
- sorted_tables = @connection.data_sources.sort - @connection.views
88
+ sorted_tables = @connection.tables.sort
89
89
 
90
90
  sorted_tables.each do |table_name|
91
91
  table(table_name, stream) unless ignored?(table_name)
@@ -39,7 +39,11 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  def normalized_versions
42
- pluck(:version).map { |v| normalize_migration_number v }
42
+ all_versions.map { |v| normalize_migration_number v }
43
+ end
44
+
45
+ def all_versions
46
+ order(:version).pluck(:version)
43
47
  end
44
48
  end
45
49
 
@@ -1,8 +1,11 @@
1
+ require "tempfile"
2
+
1
3
  module ActiveRecord
2
4
  module Tasks # :nodoc:
3
5
  class PostgreSQLDatabaseTasks # :nodoc:
4
6
  DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
5
7
  ON_ERROR_STOP_1 = "ON_ERROR_STOP=1".freeze
8
+ SQL_COMMENT_BEGIN = "--".freeze
6
9
 
7
10
  delegate :connection, :establish_connection, :clear_active_connections!,
8
11
  to: ActiveRecord::Base
@@ -65,6 +68,7 @@ module ActiveRecord
65
68
  end
66
69
  args << configuration["database"]
67
70
  run_cmd("pg_dump", args, "dumping")
71
+ remove_sql_header_comments(filename)
68
72
  File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
69
73
  end
70
74
 
@@ -110,6 +114,22 @@ module ActiveRecord
110
114
  msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
111
115
  msg
112
116
  end
117
+
118
+ def remove_sql_header_comments(filename)
119
+ removing_comments = true
120
+ tempfile = Tempfile.open("uncommented_structure.sql")
121
+ begin
122
+ File.foreach(filename) do |line|
123
+ unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
124
+ tempfile << line
125
+ removing_comments = false
126
+ end
127
+ end
128
+ ensure
129
+ tempfile.close
130
+ end
131
+ FileUtils.mv(tempfile.path, filename)
132
+ end
113
133
  end
114
134
  end
115
135
  end