activerecord 4.2.6 → 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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1307 -1105
  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 +3 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +11 -9
  11. data/lib/active_record/associations/association_scope.rb +73 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  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 +7 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  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 +50 -31
  21. data/lib/active_record/associations/collection_proxy.rb +69 -29
  22. data/lib/active_record/associations/foreign_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_association.rb +20 -71
  24. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  25. data/lib/active_record/associations/has_one_association.rb +12 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +29 -19
  28. data/lib/active_record/associations/preloader/association.rb +46 -52
  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 +14 -4
  34. data/lib/active_record/associations/singular_association.rb +7 -1
  35. data/lib/active_record/associations/through_association.rb +11 -3
  36. data/lib/active_record/associations.rb +317 -209
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +68 -18
  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 +1 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  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 +61 -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 +6 -4
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attributes.rb +199 -80
  54. data/lib/active_record/autosave_association.rb +49 -16
  55. data/lib/active_record/base.rb +32 -23
  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 +452 -182
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -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 +378 -140
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -59
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -362
  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 +25 -176
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  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 +1 -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 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  100. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  105. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  106. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  107. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  108. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  110. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  111. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  112. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  113. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  114. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  115. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  117. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +148 -203
  118. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  119. data/lib/active_record/connection_handling.rb +37 -14
  120. data/lib/active_record/core.rb +89 -107
  121. data/lib/active_record/counter_cache.rb +13 -24
  122. data/lib/active_record/dynamic_matchers.rb +1 -20
  123. data/lib/active_record/enum.rb +113 -76
  124. data/lib/active_record/errors.rb +87 -48
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +26 -5
  128. data/lib/active_record/fixtures.rb +76 -40
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +32 -40
  131. data/lib/active_record/integration.rb +4 -4
  132. data/lib/active_record/internal_metadata.rb +56 -0
  133. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  134. data/lib/active_record/locale/en.yml +3 -2
  135. data/lib/active_record/locking/optimistic.rb +15 -15
  136. data/lib/active_record/locking/pessimistic.rb +1 -1
  137. data/lib/active_record/log_subscriber.rb +43 -21
  138. data/lib/active_record/migration/command_recorder.rb +59 -18
  139. data/lib/active_record/migration/compatibility.rb +126 -0
  140. data/lib/active_record/migration.rb +364 -109
  141. data/lib/active_record/model_schema.rb +128 -38
  142. data/lib/active_record/nested_attributes.rb +58 -29
  143. data/lib/active_record/null_relation.rb +16 -8
  144. data/lib/active_record/persistence.rb +121 -80
  145. data/lib/active_record/query_cache.rb +15 -18
  146. data/lib/active_record/querying.rb +10 -9
  147. data/lib/active_record/railtie.rb +27 -18
  148. data/lib/active_record/railties/controller_runtime.rb +1 -1
  149. data/lib/active_record/railties/databases.rake +58 -45
  150. data/lib/active_record/readonly_attributes.rb +1 -1
  151. data/lib/active_record/reflection.rb +282 -115
  152. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  153. data/lib/active_record/relation/batches.rb +139 -34
  154. data/lib/active_record/relation/calculations.rb +80 -102
  155. data/lib/active_record/relation/delegation.rb +7 -20
  156. data/lib/active_record/relation/finder_methods.rb +163 -81
  157. data/lib/active_record/relation/from_clause.rb +32 -0
  158. data/lib/active_record/relation/merger.rb +16 -42
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -15
  160. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  163. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  166. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  167. data/lib/active_record/relation/predicate_builder.rb +120 -107
  168. data/lib/active_record/relation/query_attribute.rb +19 -0
  169. data/lib/active_record/relation/query_methods.rb +308 -244
  170. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  171. data/lib/active_record/relation/spawn_methods.rb +4 -7
  172. data/lib/active_record/relation/where_clause.rb +174 -0
  173. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  174. data/lib/active_record/relation.rb +176 -116
  175. data/lib/active_record/result.rb +4 -3
  176. data/lib/active_record/runtime_registry.rb +1 -1
  177. data/lib/active_record/sanitization.rb +95 -66
  178. data/lib/active_record/schema.rb +26 -22
  179. data/lib/active_record/schema_dumper.rb +62 -38
  180. data/lib/active_record/schema_migration.rb +11 -17
  181. data/lib/active_record/scoping/default.rb +23 -9
  182. data/lib/active_record/scoping/named.rb +49 -28
  183. data/lib/active_record/scoping.rb +32 -15
  184. data/lib/active_record/secure_token.rb +38 -0
  185. data/lib/active_record/serialization.rb +2 -4
  186. data/lib/active_record/statement_cache.rb +16 -14
  187. data/lib/active_record/store.rb +8 -3
  188. data/lib/active_record/suppressor.rb +58 -0
  189. data/lib/active_record/table_metadata.rb +68 -0
  190. data/lib/active_record/tasks/database_tasks.rb +58 -41
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -20
  192. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  193. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  194. data/lib/active_record/timestamp.rb +20 -9
  195. data/lib/active_record/touch_later.rb +58 -0
  196. data/lib/active_record/transactions.rb +138 -56
  197. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  198. data/lib/active_record/type/date.rb +2 -41
  199. data/lib/active_record/type/date_time.rb +2 -49
  200. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  201. data/lib/active_record/type/internal/timezone.rb +15 -0
  202. data/lib/active_record/type/serialized.rb +15 -14
  203. data/lib/active_record/type/time.rb +10 -16
  204. data/lib/active_record/type/type_map.rb +4 -4
  205. data/lib/active_record/type.rb +66 -17
  206. data/lib/active_record/type_caster/connection.rb +29 -0
  207. data/lib/active_record/type_caster/map.rb +19 -0
  208. data/lib/active_record/type_caster.rb +7 -0
  209. data/lib/active_record/validations/absence.rb +23 -0
  210. data/lib/active_record/validations/associated.rb +10 -3
  211. data/lib/active_record/validations/length.rb +24 -0
  212. data/lib/active_record/validations/presence.rb +11 -12
  213. data/lib/active_record/validations/uniqueness.rb +30 -29
  214. data/lib/active_record/validations.rb +33 -32
  215. data/lib/active_record.rb +7 -2
  216. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  217. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  218. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  219. data/lib/rails/generators/active_record/migration.rb +7 -0
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +58 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -50
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -105
@@ -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,32 +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
17
  begin
16
- value.in_time_zone || super
18
+ super(user_input_in_time_zone(value)) || super
17
19
  rescue ArgumentError
18
20
  nil
19
21
  end
22
+ else
23
+ map_avoiding_infinite_recursion(super) { |v| cast(v) }
20
24
  end
21
25
  end
22
26
 
27
+ private
28
+
23
29
  def convert_time_to_time_zone(value)
24
- if value.is_a?(Array)
25
- value.map { |v| convert_time_to_time_zone(v) }
26
- elsif value.acts_like?(:time)
30
+ return if value.nil?
31
+
32
+ if value.acts_like?(:time)
27
33
  value.in_time_zone
28
- else
34
+ elsif value.is_a?(::Float)
29
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
30
52
  end
31
53
  end
32
54
  end
@@ -39,6 +61,9 @@ module ActiveRecord
39
61
 
40
62
  class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
41
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]
42
67
  end
43
68
 
44
69
  module ClassMethods
@@ -59,9 +84,31 @@ module ActiveRecord
59
84
  end
60
85
 
61
86
  def create_time_zone_conversion_attribute?(name, cast_type)
62
- time_zone_aware_attributes &&
63
- !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
64
- (: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
65
112
  end
66
113
  end
67
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
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  end
23
23
 
24
24
  class LazyAttributeHash # :nodoc:
25
- delegate :transform_values, to: :materialize
25
+ delegate :transform_values, :each_key, to: :materialize
26
26
 
27
27
  def initialize(types, values, additional_types)
28
28
  @types = types
@@ -47,12 +47,14 @@ module ActiveRecord
47
47
  delegate_hash[key] = value
48
48
  end
49
49
 
50
- def initialized_keys
51
- delegate_hash.keys | values.keys
50
+ def deep_dup
51
+ dup.tap do |copy|
52
+ copy.instance_variable_set(:@delegate_hash, delegate_hash.transform_values(&:dup))
53
+ end
52
54
  end
53
55
 
54
56
  def initialize_dup(_)
55
- @delegate_hash = delegate_hash.transform_values(&:dup)
57
+ @delegate_hash = Hash[delegate_hash]
56
58
  super
57
59
  end
58
60