activerecord 4.2.0 → 5.0.0

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -19,10 +19,10 @@ module ActiveRecord
19
19
  if Numeric === value || value !~ /[^0-9]/
20
20
  !value.to_i.zero?
21
21
  else
22
- return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
22
+ return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
23
23
  !value.blank?
24
24
  end
25
- elsif column.number?
25
+ elsif value.respond_to?(:zero?)
26
26
  !value.zero?
27
27
  else
28
28
  !value.blank?
@@ -1,10 +1,11 @@
1
- require 'active_support/core_ext/module/method_transplanting'
2
-
3
1
  module ActiveRecord
4
2
  module AttributeMethods
5
3
  module Read
6
- ReaderMethodCache = Class.new(AttributeMethodCache) {
7
- private
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ protected
8
+
8
9
  # We want to generate the methods via module_eval rather than
9
10
  # define_method, because define_method is slower on dispatch.
10
11
  # Evaluating many similar methods may use more memory as the instruction
@@ -23,81 +24,52 @@ module ActiveRecord
23
24
  # to allocate an object on each call to the attribute method.
24
25
  # Making it frozen means that it doesn't get duped when used to
25
26
  # key the @attributes in read_attribute.
26
- def method_body(method_name, const_name)
27
- <<-EOMETHOD
28
- def #{method_name}
29
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
30
- _read_attribute(name) { |n| missing_attribute(n, caller) }
31
- end
32
- EOMETHOD
33
- end
34
- }.new
35
-
36
- extend ActiveSupport::Concern
37
-
38
- module ClassMethods
39
- [:cache_attributes, :cached_attributes, :cache_attribute?].each do |method_name|
40
- define_method method_name do |*|
41
- cached_attributes_deprecation_warning(method_name)
42
- true
43
- end
44
- end
45
-
46
- protected
47
-
48
- def cached_attributes_deprecation_warning(method_name)
49
- ActiveSupport::Deprecation.warn "Calling `#{method_name}` is no longer necessary. All attributes are cached."
50
- end
51
-
52
- if Module.methods_transplantable?
53
- def define_method_attribute(name)
54
- method = ReaderMethodCache[name]
55
- generated_attribute_methods.module_eval { define_method name, method }
56
- end
57
- else
58
- def define_method_attribute(name)
59
- safe_name = name.unpack('h*').first
60
- temp_method = "__temp__#{safe_name}"
27
+ def define_method_attribute(name)
28
+ safe_name = name.unpack('h*'.freeze).first
29
+ temp_method = "__temp__#{safe_name}"
61
30
 
62
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
31
+ ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
63
32
 
64
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
65
- def #{temp_method}
66
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
67
- _read_attribute(name) { |n| missing_attribute(n, caller) }
68
- end
69
- STR
70
-
71
- generated_attribute_methods.module_eval do
72
- alias_method name, temp_method
73
- undef_method temp_method
33
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
34
+ def #{temp_method}
35
+ name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
36
+ _read_attribute(name) { |n| missing_attribute(n, caller) }
74
37
  end
38
+ STR
39
+
40
+ generated_attribute_methods.module_eval do
41
+ alias_method name, temp_method
42
+ undef_method temp_method
75
43
  end
76
44
  end
77
45
  end
78
46
 
79
- ID = 'id'.freeze
80
-
81
47
  # Returns the value of the attribute identified by <tt>attr_name</tt> after
82
48
  # it has been typecast (for example, "2004-12-12" in a date column is cast
83
49
  # to a date object, like Date.new(2004, 12, 12)).
84
50
  def read_attribute(attr_name, &block)
85
51
  name = attr_name.to_s
86
- name = self.class.primary_key if name == ID
52
+ name = self.class.primary_key if name == 'id'.freeze
87
53
  _read_attribute(name, &block)
88
54
  end
89
55
 
90
56
  # This method exists to avoid the expensive primary_key check internally, without
91
57
  # breaking compatibility with the read_attribute API
92
- def _read_attribute(attr_name) # :nodoc:
93
- @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
58
+ if defined?(JRUBY_VERSION)
59
+ # This form is significantly faster on JRuby, and this is one of our biggest hotspots.
60
+ # https://github.com/jruby/jruby/pull/2562
61
+ def _read_attribute(attr_name, &block) # :nodoc
62
+ @attributes.fetch_value(attr_name.to_s, &block)
63
+ end
64
+ else
65
+ def _read_attribute(attr_name) # :nodoc:
66
+ @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
67
+ end
94
68
  end
95
69
 
96
- private
70
+ alias :attribute :_read_attribute
71
+ private :attribute
97
72
 
98
- def attribute(attribute_name)
99
- _read_attribute(attribute_name)
100
- end
101
73
  end
102
74
  end
103
75
  end
@@ -1,5 +1,3 @@
1
- require 'active_support/core_ext/string/filters'
2
-
3
1
  module ActiveRecord
4
2
  module AttributeMethods
5
3
  module Serialization
@@ -11,7 +9,19 @@ module ActiveRecord
11
9
  # attribute using this method and it will be handled automatically. The
12
10
  # serialization is done through YAML. If +class_name+ is specified, the
13
11
  # serialized object must be of that class on assignment and retrieval.
14
- # Otherwise <tt>SerializationTypeMismatch</tt> will be raised.
12
+ # Otherwise SerializationTypeMismatch will be raised.
13
+ #
14
+ # Empty objects as <tt>{}</tt>, in the case of +Hash+, or <tt>[]</tt>, in the case of
15
+ # +Array+, will always be persisted as null.
16
+ #
17
+ # Keep in mind that database adapters handle certain serialization tasks
18
+ # for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
19
+ # converted between JSON object/array syntax and Ruby +Hash+ or +Array+
20
+ # objects transparently. There is no need to use #serialize in this
21
+ # case.
22
+ #
23
+ # For more complex cases, such as conversion to or from your application
24
+ # domain objects, consider using the ActiveRecord::Attributes API.
15
25
  #
16
26
  # ==== Parameters
17
27
  #
@@ -51,19 +61,6 @@ module ActiveRecord
51
61
  Type::Serialized.new(type, coder)
52
62
  end
53
63
  end
54
-
55
- def serialized_attributes
56
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
57
- `serialized_attributes` is deprecated without replacement, and will
58
- be removed in Rails 5.0.
59
- MSG
60
-
61
- @serialized_attributes ||= Hash[
62
- columns.select { |t| t.cast_type.is_a?(Type::Serialized) }.map { |c|
63
- [c.name, c.cast_type.coder]
64
- }
65
- ]
66
- end
67
64
  end
68
65
  end
69
66
  end
@@ -1,28 +1,54 @@
1
+ require 'active_support/core_ext/string/strip'
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module TimeZoneConversion
4
6
  class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
5
- include Type::Decorator
6
-
7
- def type_cast_from_database(value)
7
+ def deserialize(value)
8
8
  convert_time_to_time_zone(super)
9
9
  end
10
10
 
11
- def type_cast_from_user(value)
12
- if value.is_a?(Array)
13
- value.map { |v| type_cast_from_user(v) }
11
+ def cast(value)
12
+ return if value.nil?
13
+
14
+ if value.is_a?(Hash)
15
+ set_time_zone_without_conversion(super)
14
16
  elsif value.respond_to?(:in_time_zone)
15
- value.in_time_zone || super
17
+ begin
18
+ super(user_input_in_time_zone(value)) || super
19
+ rescue ArgumentError
20
+ nil
21
+ end
22
+ else
23
+ map_avoiding_infinite_recursion(super) { |v| cast(v) }
16
24
  end
17
25
  end
18
26
 
27
+ private
28
+
19
29
  def convert_time_to_time_zone(value)
20
- if value.is_a?(Array)
21
- value.map { |v| convert_time_to_time_zone(v) }
22
- elsif value.acts_like?(:time)
30
+ return if value.nil?
31
+
32
+ if value.acts_like?(:time)
23
33
  value.in_time_zone
24
- else
34
+ elsif value.is_a?(::Float)
25
35
  value
36
+ else
37
+ map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
38
+ end
39
+ end
40
+
41
+ def set_time_zone_without_conversion(value)
42
+ ::Time.zone.local_to_utc(value).in_time_zone
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
26
52
  end
27
53
  end
28
54
  end
@@ -35,6 +61,9 @@ module ActiveRecord
35
61
 
36
62
  class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
37
63
  self.skip_time_zone_conversion_for_attributes = []
64
+
65
+ class_attribute :time_zone_aware_types, instance_writer: false
66
+ self.time_zone_aware_types = [:datetime, :not_explicitly_configured]
38
67
  end
39
68
 
40
69
  module ClassMethods
@@ -55,9 +84,31 @@ module ActiveRecord
55
84
  end
56
85
 
57
86
  def create_time_zone_conversion_attribute?(name, cast_type)
58
- time_zone_aware_attributes &&
59
- !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
60
- (:datetime == cast_type.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
+
101
+ To keep the old behavior, you must add the following to your initializer:
102
+
103
+ config.active_record.time_zone_aware_types = [:datetime]
104
+
105
+ To silence this deprecation warning, add the following:
106
+
107
+ config.active_record.time_zone_aware_types = [:datetime, :time]
108
+ MESSAGE
109
+ end
110
+
111
+ result
61
112
  end
62
113
  end
63
114
  end
@@ -1,21 +1,6 @@
1
- require 'active_support/core_ext/module/method_transplanting'
2
-
3
1
  module ActiveRecord
4
2
  module AttributeMethods
5
3
  module Write
6
- WriterMethodCache = Class.new(AttributeMethodCache) {
7
- private
8
-
9
- def method_body(method_name, const_name)
10
- <<-EOMETHOD
11
- def #{method_name}(value)
12
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
13
- write_attribute(name, value)
14
- end
15
- EOMETHOD
16
- end
17
- }.new
18
-
19
4
  extend ActiveSupport::Concern
20
5
 
21
6
  included do
@@ -25,38 +10,29 @@ module ActiveRecord
25
10
  module ClassMethods
26
11
  protected
27
12
 
28
- if Module.methods_transplantable?
29
- def define_method_attribute=(name)
30
- method = WriterMethodCache[name]
31
- generated_attribute_methods.module_eval {
32
- define_method "#{name}=", method
33
- }
34
- end
35
- else
36
- def define_method_attribute=(name)
37
- safe_name = name.unpack('h*').first
38
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
39
-
40
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
41
- def __temp__#{safe_name}=(value)
42
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
43
- write_attribute(name, value)
44
- end
45
- alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
46
- undef_method :__temp__#{safe_name}=
47
- STR
48
- end
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
49
25
  end
50
26
  end
51
27
 
52
28
  # Updates the attribute identified by <tt>attr_name</tt> with the
53
- # specified +value+. Empty strings for fixnum and float columns are
29
+ # specified +value+. Empty strings for Integer and Float columns are
54
30
  # turned into +nil+.
55
31
  def write_attribute(attr_name, value)
56
32
  write_attribute_with_type_cast(attr_name, value, true)
57
33
  end
58
34
 
59
- def raw_write_attribute(attr_name, value)
35
+ def raw_write_attribute(attr_name, value) # :nodoc:
60
36
  write_attribute_with_type_cast(attr_name, value, false)
61
37
  end
62
38
 
@@ -1,7 +1,7 @@
1
1
  require 'active_support/core_ext/enumerable'
2
2
  require 'active_support/core_ext/string/filters'
3
3
  require 'mutex_m'
4
- require 'thread_safe'
4
+ require 'concurrent/map'
5
5
 
6
6
  module ActiveRecord
7
7
  # = Active Record Attribute Methods
@@ -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 = ThreadSafe::Cache.new
41
- end
42
-
43
- def [](name)
44
- @method_cache.compute_if_absent(name) do
45
- safe_name = name.unpack('h*').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
@@ -83,7 +59,7 @@ module ActiveRecord
83
59
  generated_attribute_methods.synchronize do
84
60
  return false if @attribute_methods_generated
85
61
  superclass.define_attribute_methods unless self == base_class
86
- super(column_names)
62
+ super(attribute_names)
87
63
  @attribute_methods_generated = true
88
64
  end
89
65
  true
@@ -96,7 +72,7 @@ module ActiveRecord
96
72
  end
97
73
  end
98
74
 
99
- # Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
75
+ # Raises an ActiveRecord::DangerousAttributeError exception when an
100
76
  # \Active \Record method is defined in the model, otherwise +false+.
101
77
  #
102
78
  # class Person < ActiveRecord::Base
@@ -106,7 +82,7 @@ module ActiveRecord
106
82
  # end
107
83
  #
108
84
  # Person.instance_method_already_implemented?(:save)
109
- # # => ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord
85
+ # # => ActiveRecord::DangerousAttributeError: save is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
110
86
  #
111
87
  # Person.instance_method_already_implemented?(:name)
112
88
  # # => false
@@ -150,7 +126,7 @@ module ActiveRecord
150
126
  BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
151
127
  end
152
128
 
153
- def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
129
+ def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
154
130
  if klass.respond_to?(name, true)
155
131
  if superklass.respond_to?(name, true)
156
132
  klass.method(name).owner != superklass.method(name).owner
@@ -185,14 +161,27 @@ module ActiveRecord
185
161
  # # => ["id", "created_at", "updated_at", "name", "age"]
186
162
  def attribute_names
187
163
  @attribute_names ||= if !abstract_class? && table_exists?
188
- column_names
164
+ attribute_types.keys
189
165
  else
190
166
  []
191
167
  end
192
168
  end
193
169
 
170
+ # Returns true if the given attribute exists, otherwise false.
171
+ #
172
+ # class Person < ActiveRecord::Base
173
+ # end
174
+ #
175
+ # Person.has_attribute?('name') # => true
176
+ # Person.has_attribute?(:age) # => true
177
+ # Person.has_attribute?(:nothing) # => false
178
+ def has_attribute?(attr_name)
179
+ attribute_types.key?(attr_name.to_s)
180
+ end
181
+
194
182
  # Returns the column object for the named attribute.
195
- # Returns nil if the named attribute does not exist.
183
+ # Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
184
+ # named attribute does not exist.
196
185
  #
197
186
  # class Person < ActiveRecord::Base
198
187
  # end
@@ -202,23 +191,18 @@ module ActiveRecord
202
191
  # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
203
192
  #
204
193
  # person.column_for_attribute(:nothing)
205
- # # => nil
194
+ # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
206
195
  def column_for_attribute(name)
207
- column = columns_hash[name.to_s]
208
- if column.nil?
209
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
210
- `#column_for_attribute` will return a null object for non-existent
211
- columns in Rails 5. Use `#has_attribute?` if you need to check for
212
- an attribute's existence.
213
- MSG
196
+ name = name.to_s
197
+ columns_hash.fetch(name) do
198
+ ConnectionAdapters::NullColumn.new(name)
214
199
  end
215
- column
216
200
  end
217
201
  end
218
202
 
219
203
  # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
220
204
  # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
221
- # which will all return +true+. It also define the attribute methods if they have
205
+ # which will all return +true+. It also defines the attribute methods if they have
222
206
  # not been generated.
223
207
  #
224
208
  # class Person < ActiveRecord::Base
@@ -234,7 +218,15 @@ module ActiveRecord
234
218
  # person.respond_to(:nothing) # => false
235
219
  def respond_to?(name, include_private = false)
236
220
  return false unless super
237
- name = name.to_s
221
+
222
+ case name
223
+ when :to_partial_path
224
+ name = "to_partial_path".freeze
225
+ when :to_model
226
+ name = "to_model".freeze
227
+ else
228
+ name = name.to_s
229
+ end
238
230
 
239
231
  # If the result is true then check for the select case.
240
232
  # For queries selecting a subset of columns, return false for unselected columns.
@@ -342,7 +334,7 @@ module ActiveRecord
342
334
  #
343
335
  # Note: +:id+ is always present.
344
336
  #
345
- # Alias for the <tt>read_attribute</tt> method.
337
+ # Alias for the #read_attribute method.
346
338
  #
347
339
  # class Person < ActiveRecord::Base
348
340
  # belongs_to :organization
@@ -360,7 +352,7 @@ module ActiveRecord
360
352
  end
361
353
 
362
354
  # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
363
- # (Alias for the protected <tt>write_attribute</tt> method).
355
+ # (Alias for the protected #write_attribute method).
364
356
  #
365
357
  # class Person < ActiveRecord::Base
366
358
  # end
@@ -368,11 +360,44 @@ module ActiveRecord
368
360
  # person = Person.new
369
361
  # person[:age] = '22'
370
362
  # person[:age] # => 22
371
- # person[:age] # => Fixnum
363
+ # person[:age].class # => Integer
372
364
  def []=(attr_name, value)
373
365
  write_attribute(attr_name, value)
374
366
  end
375
367
 
368
+ # Returns the name of all database fields which have been read from this
369
+ # model. This can be useful in development mode to determine which fields
370
+ # need to be selected. For performance critical pages, selecting only the
371
+ # required fields can be an easy performance win (assuming you aren't using
372
+ # all of the fields on the model).
373
+ #
374
+ # For example:
375
+ #
376
+ # class PostsController < ActionController::Base
377
+ # after_action :print_accessed_fields, only: :index
378
+ #
379
+ # def index
380
+ # @posts = Post.all
381
+ # end
382
+ #
383
+ # private
384
+ #
385
+ # def print_accessed_fields
386
+ # p @posts.first.accessed_fields
387
+ # end
388
+ # end
389
+ #
390
+ # Which allows you to quickly change your code to:
391
+ #
392
+ # class PostsController < ActionController::Base
393
+ # def index
394
+ # @posts = Post.select(:id, :title, :author_id, :updated_at)
395
+ # end
396
+ # end
397
+ def accessed_fields
398
+ @attributes.accessed
399
+ end
400
+
376
401
  protected
377
402
 
378
403
  def clone_attribute_value(reader_method, attribute_name) # :nodoc:
@@ -0,0 +1,70 @@
1
+ module ActiveRecord
2
+ class AttributeMutationTracker # :nodoc:
3
+ def initialize(attributes)
4
+ @attributes = attributes
5
+ end
6
+
7
+ def changed_values
8
+ attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
9
+ if changed?(attr_name)
10
+ result[attr_name] = attributes[attr_name].original_value
11
+ end
12
+ end
13
+ end
14
+
15
+ def changes
16
+ 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)]
19
+ end
20
+ end
21
+ end
22
+
23
+ def changed?(attr_name)
24
+ attr_name = attr_name.to_s
25
+ attributes[attr_name].changed?
26
+ end
27
+
28
+ def changed_in_place?(attr_name)
29
+ attributes[attr_name].changed_in_place?
30
+ end
31
+
32
+ def forget_change(attr_name)
33
+ attr_name = attr_name.to_s
34
+ attributes[attr_name] = attributes[attr_name].forgetting_assignment
35
+ end
36
+
37
+ protected
38
+
39
+ attr_reader :attributes
40
+
41
+ private
42
+
43
+ def attr_names
44
+ attributes.keys
45
+ end
46
+ end
47
+
48
+ class NullMutationTracker # :nodoc:
49
+ include Singleton
50
+
51
+ def changed_values
52
+ {}
53
+ end
54
+
55
+ def changes
56
+ {}
57
+ end
58
+
59
+ def changed?(*)
60
+ false
61
+ end
62
+
63
+ def changed_in_place?(*)
64
+ false
65
+ end
66
+
67
+ def forget_change(*)
68
+ end
69
+ end
70
+ end