activerecord 5.0.7 → 5.1.7

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 (219) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -2080
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record/aggregations.rb +244 -244
  8. data/lib/active_record/association_relation.rb +5 -5
  9. data/lib/active_record/associations/alias_tracker.rb +10 -11
  10. data/lib/active_record/associations/association.rb +23 -5
  11. data/lib/active_record/associations/association_scope.rb +95 -81
  12. data/lib/active_record/associations/belongs_to_association.rb +7 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +30 -16
  14. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  16. data/lib/active_record/associations/collection_association.rb +36 -205
  17. data/lib/active_record/associations/collection_proxy.rb +132 -63
  18. data/lib/active_record/associations/has_many_association.rb +10 -19
  19. data/lib/active_record/associations/has_many_through_association.rb +12 -4
  20. data/lib/active_record/associations/has_one_association.rb +24 -28
  21. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  22. data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
  23. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  24. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +121 -118
  26. data/lib/active_record/associations/preloader/association.rb +64 -64
  27. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  28. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  29. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  30. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  31. data/lib/active_record/associations/preloader/through_association.rb +41 -41
  32. data/lib/active_record/associations/preloader.rb +94 -94
  33. data/lib/active_record/associations/singular_association.rb +8 -25
  34. data/lib/active_record/associations/through_association.rb +2 -5
  35. data/lib/active_record/associations.rb +1591 -1562
  36. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute_assignment.rb +61 -61
  39. data/lib/active_record/attribute_decorators.rb +35 -13
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +229 -46
  42. data/lib/active_record/attribute_methods/primary_key.rb +74 -73
  43. data/lib/active_record/attribute_methods/read.rb +39 -35
  44. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  46. data/lib/active_record/attribute_methods/write.rb +30 -33
  47. data/lib/active_record/attribute_methods.rb +56 -65
  48. data/lib/active_record/attribute_mutation_tracker.rb +63 -11
  49. data/lib/active_record/attribute_set/builder.rb +27 -33
  50. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  51. data/lib/active_record/attribute_set.rb +9 -6
  52. data/lib/active_record/attributes.rb +22 -22
  53. data/lib/active_record/autosave_association.rb +18 -13
  54. data/lib/active_record/base.rb +24 -22
  55. data/lib/active_record/callbacks.rb +56 -14
  56. data/lib/active_record/coders/yaml_column.rb +9 -11
  57. data/lib/active_record/collection_cache_key.rb +3 -4
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
  70. data/lib/active_record/connection_adapters/column.rb +26 -4
  71. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  72. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  91. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
  92. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  99. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
  116. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
  117. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  118. data/lib/active_record/connection_handling.rb +14 -26
  119. data/lib/active_record/core.rb +109 -93
  120. data/lib/active_record/counter_cache.rb +60 -13
  121. data/lib/active_record/define_callbacks.rb +20 -0
  122. data/lib/active_record/dynamic_matchers.rb +80 -79
  123. data/lib/active_record/enum.rb +8 -6
  124. data/lib/active_record/errors.rb +64 -15
  125. data/lib/active_record/explain.rb +1 -2
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +7 -4
  128. data/lib/active_record/fixture_set/file.rb +11 -8
  129. data/lib/active_record/fixtures.rb +66 -53
  130. data/lib/active_record/gem_version.rb +1 -1
  131. data/lib/active_record/inheritance.rb +93 -79
  132. data/lib/active_record/integration.rb +7 -7
  133. data/lib/active_record/internal_metadata.rb +3 -16
  134. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  135. data/lib/active_record/locking/optimistic.rb +69 -74
  136. data/lib/active_record/locking/pessimistic.rb +10 -1
  137. data/lib/active_record/log_subscriber.rb +23 -28
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +100 -47
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/migration.rb +153 -155
  142. data/lib/active_record/model_schema.rb +94 -107
  143. data/lib/active_record/nested_attributes.rb +200 -199
  144. data/lib/active_record/null_relation.rb +11 -34
  145. data/lib/active_record/persistence.rb +65 -50
  146. data/lib/active_record/query_cache.rb +2 -6
  147. data/lib/active_record/querying.rb +3 -4
  148. data/lib/active_record/railtie.rb +16 -17
  149. data/lib/active_record/railties/controller_runtime.rb +6 -2
  150. data/lib/active_record/railties/databases.rake +105 -133
  151. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  152. data/lib/active_record/readonly_attributes.rb +2 -2
  153. data/lib/active_record/reflection.rb +154 -108
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  155. data/lib/active_record/relation/batches.rb +80 -51
  156. data/lib/active_record/relation/calculations.rb +169 -162
  157. data/lib/active_record/relation/delegation.rb +32 -31
  158. data/lib/active_record/relation/finder_methods.rb +197 -231
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  161. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  162. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  163. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  166. data/lib/active_record/relation/predicate_builder.rb +92 -89
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +255 -293
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +80 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/relation.rb +93 -119
  174. data/lib/active_record/result.rb +41 -32
  175. data/lib/active_record/runtime_registry.rb +3 -3
  176. data/lib/active_record/sanitization.rb +176 -192
  177. data/lib/active_record/schema.rb +3 -3
  178. data/lib/active_record/schema_dumper.rb +15 -38
  179. data/lib/active_record/schema_migration.rb +8 -4
  180. data/lib/active_record/scoping/default.rb +90 -90
  181. data/lib/active_record/scoping/named.rb +11 -11
  182. data/lib/active_record/scoping.rb +6 -6
  183. data/lib/active_record/secure_token.rb +2 -2
  184. data/lib/active_record/statement_cache.rb +13 -15
  185. data/lib/active_record/store.rb +31 -32
  186. data/lib/active_record/suppressor.rb +2 -1
  187. data/lib/active_record/table_metadata.rb +9 -5
  188. data/lib/active_record/tasks/database_tasks.rb +65 -55
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  192. data/lib/active_record/timestamp.rb +46 -25
  193. data/lib/active_record/touch_later.rb +1 -2
  194. data/lib/active_record/transactions.rb +97 -109
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +13 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/internal/abstract_json.rb +4 -0
  199. data/lib/active_record/type/serialized.rb +14 -8
  200. data/lib/active_record/type/text.rb +9 -0
  201. data/lib/active_record/type/time.rb +0 -1
  202. data/lib/active_record/type/type_map.rb +11 -15
  203. data/lib/active_record/type/unsigned_integer.rb +15 -0
  204. data/lib/active_record/type.rb +17 -13
  205. data/lib/active_record/type_caster/connection.rb +8 -6
  206. data/lib/active_record/type_caster/map.rb +3 -1
  207. data/lib/active_record/type_caster.rb +2 -2
  208. data/lib/active_record/validations/associated.rb +1 -1
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +8 -39
  211. data/lib/active_record/validations.rb +4 -4
  212. data/lib/active_record/version.rb +1 -1
  213. data/lib/active_record.rb +20 -20
  214. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  215. data/lib/rails/generators/active_record/migration.rb +1 -1
  216. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  217. data/lib/rails/generators/active_record.rb +4 -4
  218. metadata +24 -13
  219. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,5 +1,3 @@
1
- require 'active_support/core_ext/string/strip'
2
-
3
1
  module ActiveRecord
4
2
  module AttributeMethods
5
3
  module TimeZoneConversion
@@ -26,31 +24,31 @@ module ActiveRecord
26
24
 
27
25
  private
28
26
 
29
- def convert_time_to_time_zone(value)
30
- return if value.nil?
27
+ def convert_time_to_time_zone(value)
28
+ return if value.nil?
31
29
 
32
- if value.acts_like?(:time)
33
- value.in_time_zone
34
- elsif value.is_a?(::Float)
35
- value
36
- else
37
- map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
30
+ if value.acts_like?(:time)
31
+ value.in_time_zone
32
+ elsif value.is_a?(::Float)
33
+ value
34
+ else
35
+ map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
36
+ end
38
37
  end
39
- end
40
38
 
41
- def set_time_zone_without_conversion(value)
42
- ::Time.zone.local_to_utc(value).in_time_zone if value
43
- end
39
+ def set_time_zone_without_conversion(value)
40
+ ::Time.zone.local_to_utc(value).try(:in_time_zone) if value
41
+ end
44
42
 
45
- def map_avoiding_infinite_recursion(value)
46
- map(value) do |v|
47
- if value.equal?(v)
48
- nil
49
- else
50
- yield(v)
43
+ def map_avoiding_infinite_recursion(value)
44
+ map(value) do |v|
45
+ if value.equal?(v)
46
+ nil
47
+ else
48
+ yield(v)
49
+ end
51
50
  end
52
51
  end
53
- end
54
52
  end
55
53
 
56
54
  extend ActiveSupport::Concern
@@ -63,53 +61,32 @@ module ActiveRecord
63
61
  self.skip_time_zone_conversion_for_attributes = []
64
62
 
65
63
  class_attribute :time_zone_aware_types, instance_writer: false
66
- self.time_zone_aware_types = [:datetime, :not_explicitly_configured]
64
+ self.time_zone_aware_types = [:datetime, :time]
67
65
  end
68
66
 
69
67
  module ClassMethods
70
68
  private
71
69
 
72
- def inherited(subclass)
73
- # We need to apply this decorator here, rather than on module inclusion. The closure
74
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
75
- # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
76
- # `skip_time_zone_conversion_for_attributes` would not be picked up.
77
- subclass.class_eval do
78
- matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
79
- decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
80
- TimeZoneConverter.new(type)
70
+ def inherited(subclass)
71
+ super
72
+ # We need to apply this decorator here, rather than on module inclusion. The closure
73
+ # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
74
+ # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
75
+ # `skip_time_zone_conversion_for_attributes` would not be picked up.
76
+ subclass.class_eval do
77
+ matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
78
+ decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
79
+ TimeZoneConverter.new(type)
80
+ end
81
81
  end
82
82
  end
83
- super
84
- end
85
-
86
- def create_time_zone_conversion_attribute?(name, cast_type)
87
- enabled_for_column = time_zone_aware_attributes &&
88
- !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
89
- result = enabled_for_column &&
90
- time_zone_aware_types.include?(cast_type.type)
91
-
92
- if enabled_for_column &&
93
- !result &&
94
- cast_type.type == :time &&
95
- time_zone_aware_types.include?(:not_explicitly_configured)
96
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
97
- Time columns will become time zone aware in Rails 5.1. This
98
- still causes `String`s to be parsed as if they were in `Time.zone`,
99
- and `Time`s to be converted to `Time.zone`.
100
83
 
101
- To keep the old behavior, you must add the following to your initializer:
84
+ def create_time_zone_conversion_attribute?(name, cast_type)
85
+ enabled_for_column = time_zone_aware_attributes &&
86
+ !skip_time_zone_conversion_for_attributes.include?(name.to_sym)
102
87
 
103
- config.active_record.time_zone_aware_types = [:datetime]
104
-
105
- To use the new behavior, add the following:
106
-
107
- config.active_record.time_zone_aware_types = [:datetime, :time]
108
- MESSAGE
88
+ enabled_for_column && time_zone_aware_types.include?(cast_type.type)
109
89
  end
110
-
111
- result
112
- end
113
90
  end
114
91
  end
115
92
  end
@@ -8,52 +8,49 @@ module ActiveRecord
8
8
  end
9
9
 
10
10
  module ClassMethods
11
- protected
12
-
13
- def define_method_attribute=(name)
14
- safe_name = name.unpack('h*'.freeze).first
15
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
16
-
17
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
18
- def __temp__#{safe_name}=(value)
19
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
20
- write_attribute(name, value)
21
- end
22
- alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
23
- undef_method :__temp__#{safe_name}=
24
- STR
25
- end
11
+ private
12
+
13
+ def define_method_attribute=(name)
14
+ safe_name = name.unpack("h*".freeze).first
15
+ ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
16
+
17
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
18
+ def __temp__#{safe_name}=(value)
19
+ name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
20
+ write_attribute(name, value)
21
+ end
22
+ alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
23
+ undef_method :__temp__#{safe_name}=
24
+ STR
25
+ end
26
26
  end
27
27
 
28
28
  # Updates the attribute identified by <tt>attr_name</tt> with the
29
29
  # specified +value+. Empty strings for Integer and Float columns are
30
30
  # turned into +nil+.
31
31
  def write_attribute(attr_name, value)
32
- write_attribute_with_type_cast(attr_name, value, true)
32
+ name = if self.class.attribute_alias?(attr_name)
33
+ self.class.attribute_alias(attr_name).to_s
34
+ else
35
+ attr_name.to_s
36
+ end
37
+
38
+ name = self.class.primary_key if name == "id".freeze && self.class.primary_key
39
+ @attributes.write_from_user(name, value)
40
+ value
33
41
  end
34
42
 
35
43
  def raw_write_attribute(attr_name, value) # :nodoc:
36
- write_attribute_with_type_cast(attr_name, value, false)
44
+ name = attr_name.to_s
45
+ @attributes.write_cast_value(name, value)
46
+ value
37
47
  end
38
48
 
39
49
  private
40
- # Handle *= for method_missing.
41
- def attribute=(attribute_name, value)
42
- write_attribute(attribute_name, value)
43
- end
44
-
45
- def write_attribute_with_type_cast(attr_name, value, should_type_cast)
46
- attr_name = attr_name.to_s
47
- attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
48
-
49
- if should_type_cast
50
- @attributes.write_from_user(attr_name, value)
51
- else
52
- @attributes.write_cast_value(attr_name, value)
50
+ # Handle *= for method_missing.
51
+ def attribute=(attribute_name, value)
52
+ write_attribute(attribute_name, value)
53
53
  end
54
-
55
- value
56
- end
57
54
  end
58
55
  end
59
56
  end
@@ -1,7 +1,7 @@
1
- require 'active_support/core_ext/enumerable'
2
- require 'active_support/core_ext/string/filters'
3
- require 'mutex_m'
4
- require 'concurrent/map'
1
+ require "active_support/core_ext/enumerable"
2
+ require "active_support/core_ext/string/filters"
3
+ require "mutex_m"
4
+ require "concurrent/map"
5
5
 
6
6
  module ActiveRecord
7
7
  # = Active Record Attribute Methods
@@ -148,7 +148,7 @@ module ActiveRecord
148
148
  # Person.attribute_method?(:age=) # => true
149
149
  # Person.attribute_method?(:nothing) # => false
150
150
  def attribute_method?(attribute)
151
- super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
151
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, "")))
152
152
  end
153
153
 
154
154
  # Returns an array of column names as strings if it's not an abstract class and
@@ -161,10 +161,10 @@ module ActiveRecord
161
161
  # # => ["id", "created_at", "updated_at", "name", "age"]
162
162
  def attribute_names
163
163
  @attribute_names ||= if !abstract_class? && table_exists?
164
- attribute_types.keys
165
- else
166
- []
167
- end
164
+ attribute_types.keys
165
+ else
166
+ []
167
+ end
168
168
  end
169
169
 
170
170
  # Returns true if the given attribute exists, otherwise false.
@@ -209,13 +209,13 @@ module ActiveRecord
209
209
  # end
210
210
  #
211
211
  # person = Person.new
212
- # person.respond_to(:name) # => true
213
- # person.respond_to(:name=) # => true
214
- # person.respond_to(:name?) # => true
215
- # person.respond_to('age') # => true
216
- # person.respond_to('age=') # => true
217
- # person.respond_to('age?') # => true
218
- # person.respond_to(:nothing) # => false
212
+ # person.respond_to?(:name) # => true
213
+ # person.respond_to?(:name=) # => true
214
+ # person.respond_to?(:name?) # => true
215
+ # person.respond_to?('age') # => true
216
+ # person.respond_to?('age=') # => true
217
+ # person.respond_to?('age?') # => true
218
+ # person.respond_to?(:nothing) # => false
219
219
  def respond_to?(name, include_private = false)
220
220
  return false unless super
221
221
 
@@ -330,8 +330,6 @@ module ActiveRecord
330
330
  #
331
331
  # Note: +:id+ is always present.
332
332
  #
333
- # Alias for the #read_attribute method.
334
- #
335
333
  # class Person < ActiveRecord::Base
336
334
  # belongs_to :organization
337
335
  # end
@@ -396,65 +394,58 @@ module ActiveRecord
396
394
 
397
395
  protected
398
396
 
399
- def clone_attribute_value(reader_method, attribute_name) # :nodoc:
400
- value = send(reader_method, attribute_name)
401
- value.duplicable? ? value.clone : value
402
- rescue TypeError, NoMethodError
403
- value
404
- end
405
-
406
- def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
407
- arel_attributes_with_values(attributes_for_create(attribute_names))
408
- end
397
+ def attribute_method?(attr_name) # :nodoc:
398
+ # We check defined? because Syck calls respond_to? before actually calling initialize.
399
+ defined?(@attributes) && @attributes.key?(attr_name)
400
+ end
409
401
 
410
- def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
411
- arel_attributes_with_values(attributes_for_update(attribute_names))
412
- end
402
+ private
413
403
 
414
- def attribute_method?(attr_name) # :nodoc:
415
- # We check defined? because Syck calls respond_to? before actually calling initialize.
416
- defined?(@attributes) && @attributes.key?(attr_name)
417
- end
404
+ def arel_attributes_with_values_for_create(attribute_names)
405
+ arel_attributes_with_values(attributes_for_create(attribute_names))
406
+ end
418
407
 
419
- private
408
+ def arel_attributes_with_values_for_update(attribute_names)
409
+ arel_attributes_with_values(attributes_for_update(attribute_names))
410
+ end
420
411
 
421
- # Returns a Hash of the Arel::Attributes and attribute values that have been
422
- # typecasted for use in an Arel insert/update method.
423
- def arel_attributes_with_values(attribute_names)
424
- attrs = {}
425
- arel_table = self.class.arel_table
412
+ # Returns a Hash of the Arel::Attributes and attribute values that have been
413
+ # typecasted for use in an Arel insert/update method.
414
+ def arel_attributes_with_values(attribute_names)
415
+ attrs = {}
416
+ arel_table = self.class.arel_table
426
417
 
427
- attribute_names.each do |name|
428
- attrs[arel_table[name]] = typecasted_attribute_value(name)
418
+ attribute_names.each do |name|
419
+ attrs[arel_table[name]] = typecasted_attribute_value(name)
420
+ end
421
+ attrs
429
422
  end
430
- attrs
431
- end
432
423
 
433
- # Filters the primary keys and readonly attributes from the attribute names.
434
- def attributes_for_update(attribute_names)
435
- attribute_names.reject do |name|
436
- readonly_attribute?(name)
424
+ # Filters the primary keys and readonly attributes from the attribute names.
425
+ def attributes_for_update(attribute_names)
426
+ attribute_names.reject do |name|
427
+ readonly_attribute?(name)
428
+ end
437
429
  end
438
- end
439
430
 
440
- # Filters out the primary keys, from the attribute names, when the primary
441
- # key is to be generated (e.g. the id attribute has no value).
442
- def attributes_for_create(attribute_names)
443
- attribute_names.reject do |name|
444
- pk_attribute?(name) && id.nil?
431
+ # Filters out the primary keys, from the attribute names, when the primary
432
+ # key is to be generated (e.g. the id attribute has no value).
433
+ def attributes_for_create(attribute_names)
434
+ attribute_names.reject do |name|
435
+ pk_attribute?(name) && id.nil?
436
+ end
445
437
  end
446
- end
447
438
 
448
- def readonly_attribute?(name)
449
- self.class.readonly_attributes.include?(name)
450
- end
439
+ def readonly_attribute?(name)
440
+ self.class.readonly_attributes.include?(name)
441
+ end
451
442
 
452
- def pk_attribute?(name)
453
- name == self.class.primary_key
454
- end
443
+ def pk_attribute?(name)
444
+ name == self.class.primary_key
445
+ end
455
446
 
456
- def typecasted_attribute_value(name)
457
- _read_attribute(name)
458
- end
447
+ def typecasted_attribute_value(name)
448
+ _read_attribute(name)
449
+ end
459
450
  end
460
451
  end
@@ -1,7 +1,15 @@
1
1
  module ActiveRecord
2
2
  class AttributeMutationTracker # :nodoc:
3
+ OPTION_NOT_GIVEN = Object.new
4
+
3
5
  def initialize(attributes)
4
6
  @attributes = attributes
7
+ @forced_changes = Set.new
8
+ @deprecated_forced_changes = Set.new
9
+ end
10
+
11
+ def changed_attribute_names
12
+ attr_names.select { |attr_name| changed?(attr_name) }
5
13
  end
6
14
 
7
15
  def changed_values
@@ -14,48 +22,89 @@ module ActiveRecord
14
22
 
15
23
  def changes
16
24
  attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
17
- if changed?(attr_name)
18
- result[attr_name] = [attributes[attr_name].original_value, attributes.fetch_value(attr_name)]
25
+ change = change_to_attribute(attr_name)
26
+ if change
27
+ result.merge!(attr_name => change)
19
28
  end
20
29
  end
21
30
  end
22
31
 
23
- def changed?(attr_name)
32
+ def change_to_attribute(attr_name)
24
33
  attr_name = attr_name.to_s
25
- attributes[attr_name].changed?
34
+ if changed?(attr_name)
35
+ [attributes[attr_name].original_value, attributes.fetch_value(attr_name)]
36
+ end
37
+ end
38
+
39
+ def any_changes?
40
+ attr_names.any? { |attr| changed?(attr) } || deprecated_forced_changes.any?
41
+ end
42
+
43
+ def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN)
44
+ attr_name = attr_name.to_s
45
+ forced_changes.include?(attr_name) ||
46
+ attributes[attr_name].changed? &&
47
+ (OPTION_NOT_GIVEN == from || attributes[attr_name].original_value == from) &&
48
+ (OPTION_NOT_GIVEN == to || attributes[attr_name].value == to)
26
49
  end
27
50
 
28
51
  def changed_in_place?(attr_name)
29
- attributes[attr_name].changed_in_place?
52
+ attributes[attr_name.to_s].changed_in_place?
30
53
  end
31
54
 
32
55
  def forget_change(attr_name)
33
56
  attr_name = attr_name.to_s
34
57
  attributes[attr_name] = attributes[attr_name].forgetting_assignment
58
+ forced_changes.delete(attr_name)
59
+ end
60
+
61
+ def original_value(attr_name)
62
+ attributes[attr_name.to_s].original_value
35
63
  end
36
64
 
65
+ def force_change(attr_name)
66
+ forced_changes << attr_name.to_s
67
+ end
68
+
69
+ def deprecated_force_change(attr_name)
70
+ deprecated_forced_changes << attr_name.to_s
71
+ end
72
+
73
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
74
+ # Workaround for Ruby 2.2 "private attribute?" warning.
37
75
  protected
38
76
 
39
- attr_reader :attributes
77
+ attr_reader :attributes, :forced_changes, :deprecated_forced_changes
40
78
 
41
79
  private
42
80
 
43
- def attr_names
44
- attributes.keys
45
- end
81
+ def attr_names
82
+ attributes.keys
83
+ end
46
84
  end
47
85
 
48
86
  class NullMutationTracker # :nodoc:
49
87
  include Singleton
50
88
 
51
- def changed_values
89
+ def changed_attribute_names(*)
90
+ []
91
+ end
92
+
93
+ def changed_values(*)
52
94
  {}
53
95
  end
54
96
 
55
- def changes
97
+ def changes(*)
56
98
  {}
57
99
  end
58
100
 
101
+ def change_to_attribute(attr_name)
102
+ end
103
+
104
+ def any_changes?(*)
105
+ false
106
+ end
107
+
59
108
  def changed?(*)
60
109
  false
61
110
  end
@@ -66,5 +115,8 @@ module ActiveRecord
66
115
 
67
116
  def forget_change(*)
68
117
  end
118
+
119
+ def original_value(*)
120
+ end
69
121
  end
70
122
  end
@@ -1,4 +1,4 @@
1
- require 'active_record/attribute'
1
+ require "active_record/attribute"
2
2
 
3
3
  module ActiveRecord
4
4
  class AttributeSet # :nodoc:
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  class LazyAttributeHash # :nodoc:
21
- delegate :transform_values, :each_key, :fetch, :except, to: :materialize
21
+ delegate :transform_values, :each_key, :each_value, :fetch, :except, to: :materialize
22
22
 
23
23
  def initialize(types, values, additional_types, default_attributes, delegate_hash = {})
24
24
  @types = types
@@ -87,46 +87,40 @@ module ActiveRecord
87
87
  end
88
88
  end
89
89
 
90
- def encode_with(coder)
91
- coder["delegate_hash"] = materialize
92
- end
93
-
94
- def init_with(coder)
95
- marshal_load(coder["delegate_hash"])
96
- end
97
-
90
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
91
+ # Workaround for Ruby 2.2 "private attribute?" warning.
98
92
  protected
99
93
 
100
- attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
94
+ attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
101
95
 
102
- def materialize
103
- unless @materialized
104
- values.each_key { |key| self[key] }
105
- types.each_key { |key| self[key] }
106
- unless frozen?
107
- @materialized = true
96
+ def materialize
97
+ unless @materialized
98
+ values.each_key { |key| self[key] }
99
+ types.each_key { |key| self[key] }
100
+ unless frozen?
101
+ @materialized = true
102
+ end
108
103
  end
104
+ delegate_hash
109
105
  end
110
- delegate_hash
111
- end
112
106
 
113
107
  private
114
108
 
115
- def assign_default_value(name)
116
- type = additional_types.fetch(name, types[name])
117
- value_present = true
118
- value = values.fetch(name) { value_present = false }
119
-
120
- if value_present
121
- delegate_hash[name] = Attribute.from_database(name, value, type)
122
- elsif types.key?(name)
123
- attr = default_attributes[name]
124
- if attr
125
- delegate_hash[name] = attr.dup
126
- else
127
- delegate_hash[name] = Attribute.uninitialized(name, type)
109
+ def assign_default_value(name)
110
+ type = additional_types.fetch(name, types[name])
111
+ value_present = true
112
+ value = values.fetch(name) { value_present = false }
113
+
114
+ if value_present
115
+ delegate_hash[name] = Attribute.from_database(name, value, type)
116
+ elsif types.key?(name)
117
+ attr = default_attributes[name]
118
+ if attr
119
+ delegate_hash[name] = attr.dup
120
+ else
121
+ delegate_hash[name] = Attribute.uninitialized(name, type)
122
+ end
128
123
  end
129
124
  end
130
- end
131
125
  end
132
126
  end
@@ -0,0 +1,41 @@
1
+ module ActiveRecord
2
+ class AttributeSet
3
+ # Attempts to do more intelligent YAML dumping of an
4
+ # ActiveRecord::AttributeSet to reduce the size of the resulting string
5
+ class YAMLEncoder # :nodoc:
6
+ def initialize(default_types)
7
+ @default_types = default_types
8
+ end
9
+
10
+ def encode(attribute_set, coder)
11
+ coder["concise_attributes"] = attribute_set.each_value.map do |attr|
12
+ if attr.type.equal?(default_types[attr.name])
13
+ attr.with_type(nil)
14
+ else
15
+ attr
16
+ end
17
+ end
18
+ end
19
+
20
+ def decode(coder)
21
+ if coder["attributes"]
22
+ coder["attributes"]
23
+ else
24
+ attributes_hash = Hash[coder["concise_attributes"].map do |attr|
25
+ if attr.type.nil?
26
+ attr = attr.with_type(default_types[attr.name])
27
+ end
28
+ [attr.name, attr]
29
+ end]
30
+ AttributeSet.new(attributes_hash)
31
+ end
32
+ end
33
+
34
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
35
+ # Workaround for Ruby 2.2 "private attribute?" warning.
36
+ protected
37
+
38
+ attr_reader :default_types
39
+ end
40
+ end
41
+ end
@@ -1,8 +1,9 @@
1
- require 'active_record/attribute_set/builder'
1
+ require "active_record/attribute_set/builder"
2
+ require "active_record/attribute_set/yaml_encoder"
2
3
 
3
4
  module ActiveRecord
4
5
  class AttributeSet # :nodoc:
5
- delegate :fetch, :except, to: :attributes
6
+ delegate :each_value, :fetch, :except, to: :attributes
6
7
 
7
8
  def initialize(attributes)
8
9
  @attributes = attributes
@@ -97,14 +98,16 @@ module ActiveRecord
97
98
  attributes == other.attributes
98
99
  end
99
100
 
101
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
102
+ # Workaround for Ruby 2.2 "private attribute?" warning.
100
103
  protected
101
104
 
102
- attr_reader :attributes
105
+ attr_reader :attributes
103
106
 
104
107
  private
105
108
 
106
- def initialized_attributes
107
- attributes.select { |_, attr| attr.initialized? }
108
- end
109
+ def initialized_attributes
110
+ attributes.select { |_, attr| attr.initialized? }
111
+ end
109
112
  end
110
113
  end