activerecord 5.0.0.beta2 → 5.0.0.beta3

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +83 -20
  3. data/lib/active_record/association_relation.rb +1 -1
  4. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -0
  5. data/lib/active_record/associations/collection_association.rb +12 -1
  6. data/lib/active_record/associations/collection_proxy.rb +14 -0
  7. data/lib/active_record/associations/join_dependency/join_association.rb +13 -7
  8. data/lib/active_record/associations/preloader/association.rb +1 -1
  9. data/lib/active_record/associations/singular_association.rb +1 -1
  10. data/lib/active_record/attribute_assignment.rb +0 -8
  11. data/lib/active_record/attribute_methods.rb +0 -24
  12. data/lib/active_record/attribute_methods/read.rb +5 -17
  13. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -2
  14. data/lib/active_record/attribute_methods/write.rb +0 -13
  15. data/lib/active_record/base.rb +0 -1
  16. data/lib/active_record/callbacks.rb +1 -1
  17. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  18. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  19. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -2
  20. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  21. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +4 -6
  22. data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -2
  23. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +10 -16
  24. data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
  25. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +2 -2
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -3
  27. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
  28. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -3
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -4
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +0 -1
  31. data/lib/active_record/core.rb +5 -0
  32. data/lib/active_record/gem_version.rb +1 -1
  33. data/lib/active_record/inheritance.rb +1 -1
  34. data/lib/active_record/migration/compatibility.rb +1 -1
  35. data/lib/active_record/nested_attributes.rb +14 -6
  36. data/lib/active_record/null_relation.rb +1 -1
  37. data/lib/active_record/querying.rb +3 -3
  38. data/lib/active_record/reflection.rb +53 -36
  39. data/lib/active_record/relation.rb +26 -18
  40. data/lib/active_record/relation/batches.rb +4 -4
  41. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  42. data/lib/active_record/relation/calculations.rb +2 -10
  43. data/lib/active_record/relation/delegation.rb +2 -1
  44. data/lib/active_record/relation/finder_methods.rb +55 -26
  45. data/lib/active_record/relation/predicate_builder.rb +3 -4
  46. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +10 -0
  47. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  48. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  49. data/lib/active_record/relation/query_methods.rb +11 -7
  50. data/lib/active_record/relation/spawn_methods.rb +1 -1
  51. data/lib/active_record/sanitization.rb +1 -1
  52. data/lib/active_record/schema_dumper.rb +6 -4
  53. data/lib/active_record/scoping/named.rb +10 -0
  54. data/lib/active_record/statement_cache.rb +1 -1
  55. data/lib/active_record/table_metadata.rb +5 -1
  56. data/lib/active_record/tasks/database_tasks.rb +4 -0
  57. data/lib/active_record/validations.rb +1 -1
  58. data/lib/active_record/validations/absence.rb +0 -1
  59. data/lib/active_record/validations/length.rb +0 -12
  60. data/lib/active_record/validations/presence.rb +0 -1
  61. data/lib/active_record/validations/uniqueness.rb +7 -9
  62. data/lib/rails/generators/active_record/model/model_generator.rb +9 -0
  63. data/lib/rails/generators/active_record/model/templates/application_record.rb +3 -0
  64. metadata +9 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1253c6d369c65fef25582c1db08ce74b101c9ca2
4
- data.tar.gz: 15c25783b7d09e51ce7a1cffd4195b146d3c4135
3
+ metadata.gz: 8926929a6df7248ea130b2445de91ba4e4933fb2
4
+ data.tar.gz: e25e460b0ff4541594badc944391f6a90b2d4c9f
5
5
  SHA512:
6
- metadata.gz: 9ea8bb7f465a0a0f88233fb05a70d5a7ab3ae100e928e831ea7813385537718d9907981fb7dfa51beb2eaa1b4a1726d9927c72b47562504c0cb083e4f7942ef2
7
- data.tar.gz: 86360455da6ca811777f4288ac4d18051ad86c308ae7a2d53d1f527b0db19a0bf5f6e5d5c589f8b825e94886e03e51b0a854fc2137ab02929cd2309e685b67d2
6
+ metadata.gz: baf2428151cb30e7a384a1a7a63fbb9da784c9a4fe23b8900ee9dc0ba7735baed85ca24b5e1dd2a75d1625e6b6e0b6378764e1694623cf292d66cc189f0fe373
7
+ data.tar.gz: 1069aa6eb655e0ee291af703e6d49785603662274d42cc632d95d9b39e37f2facc5d79aa4a25dd24103bd7268ba40a8c5f2af8cc1ece4e7c9d437ff584313439
@@ -1,3 +1,85 @@
1
+ ## Rails 5.0.0.beta3 (February 24, 2016) ##
2
+
3
+ * Ensure that mutations of the array returned from `ActiveRecord::Relation#to_a`
4
+ do not affect the original relation, by returning a duplicate array each time.
5
+
6
+ This brings the behavior in line with `CollectionProxy#to_a`, which was
7
+ already more careful.
8
+
9
+ *Matthew Draper*
10
+
11
+ * Fixed `where` for polymorphic associations when passed an array containing different types.
12
+
13
+ Fixes #17011.
14
+
15
+ Example:
16
+
17
+ PriceEstimate.where(estimate_of: [Treasure.find(1), Car.find(2)])
18
+ # => SELECT "price_estimates".* FROM "price_estimates"
19
+ WHERE (("price_estimates"."estimate_of_type" = 'Treasure' AND "price_estimates"."estimate_of_id" = 1)
20
+ OR ("price_estimates"."estimate_of_type" = 'Car' AND "price_estimates"."estimate_of_id" = 2))
21
+
22
+ *Philippe Huibonhoa*
23
+
24
+ * Fix a bug where using `t.foreign_key` twice with the same `to_table` within
25
+ the same table definition would only create one foreign key.
26
+
27
+ *George Millo*
28
+
29
+ * Fix a regression on has many association, where calling a child from parent in child's callback
30
+ results in same child records getting added repeatedly to target.
31
+
32
+ Fixes #13387.
33
+
34
+ *Bogdan Gusiev*, *Jon Hinson*
35
+
36
+ * Rework `ActiveRecord::Relation#last`.
37
+
38
+ 1. Never perform additional SQL on loaded relation
39
+ 2. Use SQL reverse order instead of loading relation if relation doesn't have limit
40
+ 3. Deprecated relation loading when SQL order can not be automatically reversed
41
+
42
+ Topic.order("title").load.last(3)
43
+ # before: SELECT ...
44
+ # after: No SQL
45
+
46
+ Topic.order("title").last
47
+ # before: SELECT * FROM `topics`
48
+ # after: SELECT * FROM `topics` ORDER BY `topics`.`title` DESC LIMIT 1
49
+
50
+ Topic.order("coalesce(author, title)").last
51
+ # before: SELECT * FROM `topics`
52
+ # after: Deprecation Warning for irreversible order
53
+
54
+ *Bogdan Gusiev*
55
+
56
+
57
+ * Allow `joins` to be unscoped.
58
+
59
+ Fixes #13775.
60
+
61
+ *Takashi Kokubun*
62
+
63
+ * Add ActiveRecord `#second_to_last` and `#third_to_last` methods.
64
+
65
+ *Brian Christian*
66
+
67
+ * Added `numeric` helper into migrations.
68
+
69
+ Example:
70
+
71
+ create_table(:numeric_types) do |t|
72
+ t.numeric :numeric_type, precision: 10, scale: 2
73
+ end
74
+
75
+ *Mehmet Emin İNAÇ*
76
+
77
+ * Bumped the minimum supported version of PostgreSQL to >= 9.1.
78
+ Both PG 9.0 and 8.4 are past their end of life date:
79
+ http://www.postgresql.org/support/versioning/
80
+
81
+ *Remo Mueller*
82
+
1
83
  ## Rails 5.0.0.beta2 (February 01, 2016) ##
2
84
 
3
85
  * `ActiveRecord::Relation#reverse_order` throws `ActiveRecord::IrreversibleOrderError`
@@ -596,7 +678,7 @@
596
678
 
597
679
  *Ben Murphy*, *Matthew Draper*
598
680
 
599
- * `bin/rake db:migrate` uses
681
+ * `bin/rails db:migrate` uses
600
682
  `ActiveRecord::Tasks::DatabaseTasks.migrations_paths` instead of
601
683
  `Migrator.migrations_paths`.
602
684
 
@@ -998,13 +1080,6 @@
998
1080
 
999
1081
  *Alex Coomans*
1000
1082
 
1001
- * Dump indexes in `create_table` instead of `add_index`.
1002
-
1003
- If the adapter supports indexes in `create_table`, generated SQL is
1004
- slightly more efficient.
1005
-
1006
- *Ryuta Kamizono*
1007
-
1008
1083
  * Correctly dump `:options` on `create_table` for MySQL.
1009
1084
 
1010
1085
  *Ryuta Kamizono*
@@ -1379,18 +1454,6 @@
1379
1454
 
1380
1455
  *Chris Sinjakli*
1381
1456
 
1382
- * Validation errors would be raised for parent records when an association
1383
- was saved when the parent had `validate: false`. It should not be the
1384
- responsibility of the model to validate an associated object unless the
1385
- object was created or modified by the parent.
1386
-
1387
- This fixes the issue by skipping validations if the parent record is
1388
- persisted, not changed, and not marked for destruction.
1389
-
1390
- Fixes #17621.
1391
-
1392
- *Eileen M. Uchitelle*, *Aaron Patterson*
1393
-
1394
1457
  * Fix n+1 query problem when eager loading nil associations (fixes #18312)
1395
1458
 
1396
1459
  *Sammy Larbi*
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  end
11
11
 
12
12
  def ==(other)
13
- other == to_a
13
+ other == records
14
14
  end
15
15
 
16
16
  def build(*args, &block)
@@ -76,6 +76,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
76
76
  left_model.retrieve_connection
77
77
  end
78
78
 
79
+ def self.primary_key
80
+ false
81
+ end
79
82
  }
80
83
 
81
84
  join_model.name = "HABTM_#{association_name.to_s.camelize}"
@@ -72,7 +72,10 @@ module ActiveRecord
72
72
  pk_type = reflection.primary_key_type
73
73
  ids = Array(ids).reject(&:blank?)
74
74
  ids.map! { |i| pk_type.cast(i) }
75
- replace(klass.find(ids).index_by(&:id).values_at(*ids))
75
+ records = klass.where(reflection.association_primary_key => ids).index_by do |r|
76
+ r.send(reflection.association_primary_key)
77
+ end.values_at(*ids)
78
+ replace(records)
76
79
  end
77
80
 
78
81
  def reset
@@ -133,6 +136,14 @@ module ActiveRecord
133
136
  first_nth_or_last(:forty_two, *args)
134
137
  end
135
138
 
139
+ def third_to_last(*args)
140
+ first_nth_or_last(:third_to_last, *args)
141
+ end
142
+
143
+ def second_to_last(*args)
144
+ first_nth_or_last(:second_to_last, *args)
145
+ end
146
+
136
147
  def last(*args)
137
148
  first_nth_or_last(:last, *args)
138
149
  end
@@ -197,6 +197,16 @@ module ActiveRecord
197
197
  @association.forty_two(*args)
198
198
  end
199
199
 
200
+ # Same as #first except returns only the third-to-last record.
201
+ def third_to_last(*args)
202
+ @association.third_to_last(*args)
203
+ end
204
+
205
+ # Same as #first except returns only the second-to-last record.
206
+ def second_to_last(*args)
207
+ @association.second_to_last(*args)
208
+ end
209
+
200
210
  # Returns the last record, or the last +n+ records, from the collection.
201
211
  # If the collection is empty, the first form returns +nil+, and the second
202
212
  # form returns an empty array.
@@ -969,6 +979,10 @@ module ActiveRecord
969
979
  end
970
980
  alias_method :to_a, :to_ary
971
981
 
982
+ def records # :nodoc:
983
+ load_target
984
+ end
985
+
972
986
  # Adds one or more +records+ to the collection by setting their foreign keys
973
987
  # to the association's primary key. Returns +self+, so several appends may be
974
988
  # chained together.
@@ -54,12 +54,18 @@ module ActiveRecord
54
54
  end
55
55
  scope_chain_index += 1
56
56
 
57
- relation = ActiveRecord::Relation.create(
58
- klass,
59
- table,
60
- predicate_builder,
61
- )
62
- scope_chain_items.concat [klass.send(:build_default_scope, relation)].compact
57
+ klass_scope =
58
+ if klass.current_scope
59
+ klass.current_scope.clone
60
+ else
61
+ relation = ActiveRecord::Relation.create(
62
+ klass,
63
+ table,
64
+ predicate_builder,
65
+ )
66
+ klass.send(:build_default_scope, relation)
67
+ end
68
+ scope_chain_items.concat [klass_scope].compact
63
69
 
64
70
  rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
65
71
  left.merge right
@@ -75,7 +81,7 @@ module ActiveRecord
75
81
  column = klass.columns_hash[reflection.type.to_s]
76
82
 
77
83
  binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
78
- constraint = constraint.and table[reflection.type].eq(Arel::Nodes::BindParam.new)
84
+ constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
79
85
  end
80
86
 
81
87
  joins << table.create_join(table, table.create_on(constraint), join_type)
@@ -47,7 +47,7 @@ module ActiveRecord
47
47
  # This is overridden by HABTM as the condition should be on the foreign_key column in
48
48
  # the join table
49
49
  def association_key
50
- table[association_key_name]
50
+ klass.arel_attribute(association_key_name, table)
51
51
  end
52
52
 
53
53
  # The name of the key on the model which declares the association
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  end
46
46
 
47
47
  def get_records
48
- return scope.limit(1).to_a if skip_statement_cache?
48
+ return scope.limit(1).records if skip_statement_cache?
49
49
 
50
50
  conn = klass.connection
51
51
  sc = reflection.association_scope_cache(conn, owner) do
@@ -29,14 +29,6 @@ module ActiveRecord
29
29
  assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
30
30
  end
31
31
 
32
- # Tries to assign given value to given attribute.
33
- # In case of an error, re-raises with the ActiveRecord constant.
34
- def _assign_attribute(k, v) # :nodoc:
35
- super
36
- rescue ActiveModel::UnknownAttributeError
37
- raise UnknownAttributeError.new(self, k)
38
- end
39
-
40
32
  # Assign any deferred nested attributes after the base attributes have been set.
41
33
  def assign_nested_parameter_attributes(pairs)
42
34
  pairs.each { |k, v| _assign_attribute(k, v) }
@@ -34,30 +34,6 @@ module ActiveRecord
34
34
 
35
35
  BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
36
36
 
37
- class AttributeMethodCache
38
- def initialize
39
- @module = Module.new
40
- @method_cache = Concurrent::Map.new
41
- end
42
-
43
- def [](name)
44
- @method_cache.compute_if_absent(name) do
45
- safe_name = name.unpack('h*'.freeze).first
46
- temp_method = "__temp__#{safe_name}"
47
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
48
- @module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
49
- @module.instance_method temp_method
50
- end
51
- end
52
-
53
- private
54
-
55
- # Override this method in the subclasses for method body.
56
- def method_body(method_name, const_name)
57
- raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
58
- end
59
- end
60
-
61
37
  class GeneratedAttributeMethods < Module; end # :nodoc:
62
38
 
63
39
  module ClassMethods
@@ -1,8 +1,11 @@
1
1
  module ActiveRecord
2
2
  module AttributeMethods
3
3
  module Read
4
- ReaderMethodCache = Class.new(AttributeMethodCache) {
5
- private
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ protected
8
+
6
9
  # We want to generate the methods via module_eval rather than
7
10
  # define_method, because define_method is slower on dispatch.
8
11
  # Evaluating many similar methods may use more memory as the instruction
@@ -21,21 +24,6 @@ module ActiveRecord
21
24
  # to allocate an object on each call to the attribute method.
22
25
  # Making it frozen means that it doesn't get duped when used to
23
26
  # key the @attributes in read_attribute.
24
- def method_body(method_name, const_name)
25
- <<-EOMETHOD
26
- def #{method_name}
27
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
28
- _read_attribute(name) { |n| missing_attribute(n, caller) }
29
- end
30
- EOMETHOD
31
- end
32
- }.new
33
-
34
- extend ActiveSupport::Concern
35
-
36
- module ClassMethods
37
- protected
38
-
39
27
  def define_method_attribute(name)
40
28
  safe_name = name.unpack('h*'.freeze).first
41
29
  temp_method = "__temp__#{safe_name}"
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  nil
21
21
  end
22
22
  else
23
- map(super) { |t| cast(t) }
23
+ map_avoiding_infinite_recursion(super) { |v| cast(v) }
24
24
  end
25
25
  end
26
26
 
@@ -34,13 +34,23 @@ module ActiveRecord
34
34
  elsif value.is_a?(::Float)
35
35
  value
36
36
  else
37
- map(value) { |v| convert_time_to_time_zone(v) }
37
+ map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
38
38
  end
39
39
  end
40
40
 
41
41
  def set_time_zone_without_conversion(value)
42
42
  ::Time.zone.local_to_utc(value).in_time_zone
43
43
  end
44
+
45
+ def map_avoiding_infinite_recursion(value)
46
+ map(value) do |v|
47
+ if value.equal?(v)
48
+ nil
49
+ else
50
+ yield(v)
51
+ end
52
+ end
53
+ end
44
54
  end
45
55
 
46
56
  extend ActiveSupport::Concern
@@ -1,19 +1,6 @@
1
1
  module ActiveRecord
2
2
  module AttributeMethods
3
3
  module Write
4
- WriterMethodCache = Class.new(AttributeMethodCache) {
5
- private
6
-
7
- def method_body(method_name, const_name)
8
- <<-EOMETHOD
9
- def #{method_name}(value)
10
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
11
- write_attribute(name, value)
12
- end
13
- EOMETHOD
14
- end
15
- }.new
16
-
17
4
  extend ActiveSupport::Concern
18
5
 
19
6
  included do
@@ -13,7 +13,6 @@ require 'active_support/core_ext/kernel/singleton_class'
13
13
  require 'active_support/core_ext/module/introspection'
14
14
  require 'active_support/core_ext/object/duplicable'
15
15
  require 'active_support/core_ext/class/subclasses'
16
- require 'arel'
17
16
  require 'active_record/attribute_decorators'
18
17
  require 'active_record/errors'
19
18
  require 'active_record/log_subscriber'
@@ -179,7 +179,7 @@ module ActiveRecord
179
179
  #
180
180
  # If the +before_validation+ callback throws +:abort+, the process will be
181
181
  # aborted and {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] will return +false+.
182
- # If {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] is called it will raise a ActiveRecord::RecordInvalid exception.
182
+ # If {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] is called it will raise an ActiveRecord::RecordInvalid exception.
183
183
  # Nothing will be appended to the errors object.
184
184
  #
185
185
  # == Canceling callbacks
@@ -27,10 +27,10 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  # Returns an ActiveRecord::Result instance.
30
- def select_all(arel, name = nil, binds = [])
30
+ def select_all(arel, name = nil, binds = [], preparable: nil)
31
31
  arel, binds = binds_from_relation arel, binds
32
32
  sql = to_sql(arel, binds)
33
- if arel.is_a?(String)
33
+ if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
34
34
  preparable = false
35
35
  else
36
36
  preparable = visitor.preparable
@@ -61,11 +61,11 @@ module ActiveRecord
61
61
  @query_cache.clear
62
62
  end
63
63
 
64
- def select_all(arel, name = nil, binds = [])
64
+ def select_all(arel, name = nil, binds = [], preparable: nil)
65
65
  if @query_cache_enabled && !locked?(arel)
66
66
  arel, binds = binds_from_relation arel, binds
67
67
  sql = to_sql(arel, binds)
68
- cache_sql(sql, binds) { super(sql, name, binds) }
68
+ cache_sql(sql, binds) { super(sql, name, binds, preparable: visitor.preparable) }
69
69
  else
70
70
  super
71
71
  end