activerecord 4.2.0 → 5.2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +466 -280
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +439 -330
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -324
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +136 -90
  133. data/lib/active_record/errors.rb +180 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +117 -35
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -339
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +208 -123
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +30 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,83 +1,68 @@
1
- require 'active_support/core_ext/module/method_transplanting'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module AttributeMethods
5
5
  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
6
  extend ActiveSupport::Concern
20
7
 
21
8
  included do
22
9
  attribute_method_suffix "="
23
10
  end
24
11
 
25
- module ClassMethods
26
- protected
12
+ module ClassMethods # :nodoc:
13
+ private
27
14
 
28
- if Module.methods_transplantable?
29
15
  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
16
+ safe_name = name.unpack("h*".freeze).first
38
17
  ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
18
+ sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
39
19
 
40
20
  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
41
21
  def __temp__#{safe_name}=(value)
42
22
  name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
43
- write_attribute(name, value)
23
+ #{sync_with_transaction_state}
24
+ _write_attribute(name, value)
44
25
  end
45
26
  alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
46
27
  undef_method :__temp__#{safe_name}=
47
28
  STR
48
29
  end
49
- end
50
30
  end
51
31
 
52
32
  # Updates the attribute identified by <tt>attr_name</tt> with the
53
- # specified +value+. Empty strings for fixnum and float columns are
33
+ # specified +value+. Empty strings for Integer and Float columns are
54
34
  # turned into +nil+.
55
35
  def write_attribute(attr_name, value)
56
- write_attribute_with_type_cast(attr_name, value, true)
57
- end
36
+ name = if self.class.attribute_alias?(attr_name)
37
+ self.class.attribute_alias(attr_name).to_s
38
+ else
39
+ attr_name.to_s
40
+ end
58
41
 
59
- def raw_write_attribute(attr_name, value)
60
- write_attribute_with_type_cast(attr_name, value, false)
42
+ primary_key = self.class.primary_key
43
+ name = primary_key if name == "id".freeze && primary_key
44
+ sync_with_transaction_state if name == primary_key
45
+ _write_attribute(name, value)
61
46
  end
62
47
 
63
- private
64
- # Handle *= for method_missing.
65
- def attribute=(attribute_name, value)
66
- write_attribute(attribute_name, value)
48
+ # This method exists to avoid the expensive primary_key check internally, without
49
+ # breaking compatibility with the write_attribute API
50
+ def _write_attribute(attr_name, value) # :nodoc:
51
+ @attributes.write_from_user(attr_name.to_s, value)
52
+ value
67
53
  end
68
54
 
69
- def write_attribute_with_type_cast(attr_name, value, should_type_cast)
70
- attr_name = attr_name.to_s
71
- attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
72
-
73
- if should_type_cast
74
- @attributes.write_from_user(attr_name, value)
75
- else
76
- @attributes.write_cast_value(attr_name, value)
55
+ private
56
+ def write_attribute_without_type_cast(attr_name, value)
57
+ name = attr_name.to_s
58
+ @attributes.write_cast_value(name, value)
59
+ value
77
60
  end
78
61
 
79
- value
80
- end
62
+ # Handle *= for method_missing.
63
+ def attribute=(attribute_name, value)
64
+ _write_attribute(attribute_name, value)
65
+ end
81
66
  end
82
67
  end
83
68
  end
@@ -1,7 +1,6 @@
1
- require 'active_support/core_ext/enumerable'
2
- require 'active_support/core_ext/string/filters'
3
- require 'mutex_m'
4
- require 'thread_safe'
1
+ # frozen_string_literal: true
2
+
3
+ require "mutex_m"
5
4
 
6
5
  module ActiveRecord
7
6
  # = Active Record Attribute Methods
@@ -34,32 +33,10 @@ module ActiveRecord
34
33
 
35
34
  BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
36
35
 
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
36
+ class GeneratedAttributeMethods < Module #:nodoc:
37
+ include Mutex_m
59
38
  end
60
39
 
61
- class GeneratedAttributeMethods < Module; end # :nodoc:
62
-
63
40
  module ClassMethods
64
41
  def inherited(child_class) #:nodoc:
65
42
  child_class.initialize_generated_modules
@@ -67,7 +44,7 @@ module ActiveRecord
67
44
  end
68
45
 
69
46
  def initialize_generated_modules # :nodoc:
70
- @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
47
+ @generated_attribute_methods = GeneratedAttributeMethods.new
71
48
  @attribute_methods_generated = false
72
49
  include @generated_attribute_methods
73
50
 
@@ -83,10 +60,9 @@ module ActiveRecord
83
60
  generated_attribute_methods.synchronize do
84
61
  return false if @attribute_methods_generated
85
62
  superclass.define_attribute_methods unless self == base_class
86
- super(column_names)
63
+ super(attribute_names)
87
64
  @attribute_methods_generated = true
88
65
  end
89
- true
90
66
  end
91
67
 
92
68
  def undefine_attribute_methods # :nodoc:
@@ -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
@@ -172,7 +148,7 @@ module ActiveRecord
172
148
  # Person.attribute_method?(:age=) # => true
173
149
  # Person.attribute_method?(:nothing) # => false
174
150
  def attribute_method?(attribute)
175
- super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
151
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, "")))
176
152
  end
177
153
 
178
154
  # Returns an array of column names as strings if it's not an abstract class and
@@ -185,14 +161,78 @@ 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
189
- else
190
- []
191
- end
164
+ attribute_types.keys
165
+ else
166
+ []
167
+ end
168
+ end
169
+
170
+ # Regexp whitelist. Matches the following:
171
+ # "#{table_name}.#{column_name}"
172
+ # "#{column_name}"
173
+ COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i
174
+
175
+ # Regexp whitelist. Matches the following:
176
+ # "#{table_name}.#{column_name}"
177
+ # "#{table_name}.#{column_name} #{direction}"
178
+ # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
179
+ # "#{table_name}.#{column_name} NULLS LAST"
180
+ # "#{column_name}"
181
+ # "#{column_name} #{direction}"
182
+ # "#{column_name} #{direction} NULLS FIRST"
183
+ # "#{column_name} NULLS LAST"
184
+ COLUMN_NAME_ORDER_WHITELIST = /
185
+ \A
186
+ (?:\w+\.)?
187
+ \w+
188
+ (?:\s+asc|\s+desc)?
189
+ (?:\s+nulls\s+(?:first|last))?
190
+ \z
191
+ /ix
192
+
193
+ def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
194
+ unexpected = args.reject do |arg|
195
+ arg.kind_of?(Arel::Node) ||
196
+ arg.is_a?(Arel::Nodes::SqlLiteral) ||
197
+ arg.is_a?(Arel::Attributes::Attribute) ||
198
+ arg.to_s.split(/\s*,\s*/).all? { |part| whitelist.match?(part) }
199
+ end
200
+
201
+ return if unexpected.none?
202
+
203
+ if allow_unsafe_raw_sql == :deprecated
204
+ ActiveSupport::Deprecation.warn(
205
+ "Dangerous query method (method whose arguments are used as raw " \
206
+ "SQL) called with non-attribute argument(s): " \
207
+ "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
208
+ "arguments will be disallowed in Rails 6.0. This method should " \
209
+ "not be called with user-provided values, such as request " \
210
+ "parameters or model attributes. Known-safe values can be passed " \
211
+ "by wrapping them in Arel.sql()."
212
+ )
213
+ else
214
+ raise(ActiveRecord::UnknownAttributeReference,
215
+ "Query method called with non-attribute argument(s): " +
216
+ unexpected.map(&:inspect).join(", ")
217
+ )
218
+ end
219
+ end
220
+
221
+ # Returns true if the given attribute exists, otherwise false.
222
+ #
223
+ # class Person < ActiveRecord::Base
224
+ # end
225
+ #
226
+ # Person.has_attribute?('name') # => true
227
+ # Person.has_attribute?(:age) # => true
228
+ # Person.has_attribute?(:nothing) # => false
229
+ def has_attribute?(attr_name)
230
+ attribute_types.key?(attr_name.to_s)
192
231
  end
193
232
 
194
233
  # Returns the column object for the named attribute.
195
- # Returns nil if the named attribute does not exist.
234
+ # Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
235
+ # named attribute does not exist.
196
236
  #
197
237
  # class Person < ActiveRecord::Base
198
238
  # end
@@ -202,39 +242,42 @@ module ActiveRecord
202
242
  # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
203
243
  #
204
244
  # person.column_for_attribute(:nothing)
205
- # # => nil
245
+ # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
206
246
  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
247
+ name = name.to_s
248
+ columns_hash.fetch(name) do
249
+ ConnectionAdapters::NullColumn.new(name)
214
250
  end
215
- column
216
251
  end
217
252
  end
218
253
 
219
254
  # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
220
255
  # <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
256
+ # which will all return +true+. It also defines the attribute methods if they have
222
257
  # not been generated.
223
258
  #
224
259
  # class Person < ActiveRecord::Base
225
260
  # end
226
261
  #
227
262
  # person = Person.new
228
- # person.respond_to(:name) # => true
229
- # person.respond_to(:name=) # => true
230
- # person.respond_to(:name?) # => true
231
- # person.respond_to('age') # => true
232
- # person.respond_to('age=') # => true
233
- # person.respond_to('age?') # => true
234
- # person.respond_to(:nothing) # => false
263
+ # person.respond_to?(:name) # => true
264
+ # person.respond_to?(:name=) # => true
265
+ # person.respond_to?(:name?) # => true
266
+ # person.respond_to?('age') # => true
267
+ # person.respond_to?('age=') # => true
268
+ # person.respond_to?('age?') # => true
269
+ # person.respond_to?(:nothing) # => false
235
270
  def respond_to?(name, include_private = false)
236
271
  return false unless super
237
- name = name.to_s
272
+
273
+ case name
274
+ when :to_partial_path
275
+ name = "to_partial_path".freeze
276
+ when :to_model
277
+ name = "to_model".freeze
278
+ else
279
+ name = name.to_s
280
+ end
238
281
 
239
282
  # If the result is true then check for the select case.
240
283
  # For queries selecting a subset of columns, return false for unselected columns.
@@ -244,7 +287,7 @@ module ActiveRecord
244
287
  return has_attribute?(name)
245
288
  end
246
289
 
247
- return true
290
+ true
248
291
  end
249
292
 
250
293
  # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
@@ -287,9 +330,8 @@ module ActiveRecord
287
330
  # Returns an <tt>#inspect</tt>-like string for the value of the
288
331
  # attribute +attr_name+. String attributes are truncated up to 50
289
332
  # characters, Date and Time attributes are returned in the
290
- # <tt>:db</tt> format, Array attributes are truncated up to 10 values.
291
- # Other attributes return the value of <tt>#inspect</tt> without
292
- # modification.
333
+ # <tt>:db</tt> format. Other attributes return the value of
334
+ # <tt>#inspect</tt> without modification.
293
335
  #
294
336
  # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
295
337
  #
@@ -300,7 +342,7 @@ module ActiveRecord
300
342
  # # => "\"2012-10-22 00:15:07\""
301
343
  #
302
344
  # person.attribute_for_inspect(:tag_ids)
303
- # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
345
+ # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
304
346
  def attribute_for_inspect(attr_name)
305
347
  value = read_attribute(attr_name)
306
348
 
@@ -308,9 +350,6 @@ module ActiveRecord
308
350
  "#{value[0, 50]}...".inspect
309
351
  elsif value.is_a?(Date) || value.is_a?(Time)
310
352
  %("#{value.to_s(:db)}")
311
- elsif value.is_a?(Array) && value.size > 10
312
- inspected = value.first(10).inspect
313
- %(#{inspected[0...-1]}, ...])
314
353
  else
315
354
  value.inspect
316
355
  end
@@ -342,8 +381,6 @@ module ActiveRecord
342
381
  #
343
382
  # Note: +:id+ is always present.
344
383
  #
345
- # Alias for the <tt>read_attribute</tt> method.
346
- #
347
384
  # class Person < ActiveRecord::Base
348
385
  # belongs_to :organization
349
386
  # end
@@ -360,7 +397,7 @@ module ActiveRecord
360
397
  end
361
398
 
362
399
  # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
363
- # (Alias for the protected <tt>write_attribute</tt> method).
400
+ # (Alias for the protected #write_attribute method).
364
401
  #
365
402
  # class Person < ActiveRecord::Base
366
403
  # end
@@ -368,72 +405,88 @@ module ActiveRecord
368
405
  # person = Person.new
369
406
  # person[:age] = '22'
370
407
  # person[:age] # => 22
371
- # person[:age] # => Fixnum
408
+ # person[:age].class # => Integer
372
409
  def []=(attr_name, value)
373
410
  write_attribute(attr_name, value)
374
411
  end
375
412
 
376
- protected
377
-
378
- def clone_attribute_value(reader_method, attribute_name) # :nodoc:
379
- value = send(reader_method, attribute_name)
380
- value.duplicable? ? value.clone : value
381
- rescue TypeError, NoMethodError
382
- value
383
- end
384
-
385
- def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
386
- arel_attributes_with_values(attributes_for_create(attribute_names))
413
+ # Returns the name of all database fields which have been read from this
414
+ # model. This can be useful in development mode to determine which fields
415
+ # need to be selected. For performance critical pages, selecting only the
416
+ # required fields can be an easy performance win (assuming you aren't using
417
+ # all of the fields on the model).
418
+ #
419
+ # For example:
420
+ #
421
+ # class PostsController < ActionController::Base
422
+ # after_action :print_accessed_fields, only: :index
423
+ #
424
+ # def index
425
+ # @posts = Post.all
426
+ # end
427
+ #
428
+ # private
429
+ #
430
+ # def print_accessed_fields
431
+ # p @posts.first.accessed_fields
432
+ # end
433
+ # end
434
+ #
435
+ # Which allows you to quickly change your code to:
436
+ #
437
+ # class PostsController < ActionController::Base
438
+ # def index
439
+ # @posts = Post.select(:id, :title, :author_id, :updated_at)
440
+ # end
441
+ # end
442
+ def accessed_fields
443
+ @attributes.accessed
387
444
  end
388
445
 
389
- def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
390
- arel_attributes_with_values(attributes_for_update(attribute_names))
391
- end
446
+ protected
392
447
 
393
- def attribute_method?(attr_name) # :nodoc:
394
- # We check defined? because Syck calls respond_to? before actually calling initialize.
395
- defined?(@attributes) && @attributes.key?(attr_name)
396
- end
448
+ def attribute_method?(attr_name) # :nodoc:
449
+ # We check defined? because Syck calls respond_to? before actually calling initialize.
450
+ defined?(@attributes) && @attributes.key?(attr_name)
451
+ end
397
452
 
398
453
  private
399
454
 
400
- # Returns a Hash of the Arel::Attributes and attribute values that have been
401
- # typecasted for use in an Arel insert/update method.
402
- def arel_attributes_with_values(attribute_names)
403
- attrs = {}
404
- arel_table = self.class.arel_table
455
+ def attributes_with_values_for_create(attribute_names)
456
+ attributes_with_values(attributes_for_create(attribute_names))
457
+ end
405
458
 
406
- attribute_names.each do |name|
407
- attrs[arel_table[name]] = typecasted_attribute_value(name)
459
+ def attributes_with_values_for_update(attribute_names)
460
+ attributes_with_values(attributes_for_update(attribute_names))
408
461
  end
409
- attrs
410
- end
411
462
 
412
- # Filters the primary keys and readonly attributes from the attribute names.
413
- def attributes_for_update(attribute_names)
414
- attribute_names.reject do |name|
415
- readonly_attribute?(name)
463
+ def attributes_with_values(attribute_names)
464
+ attribute_names.each_with_object({}) do |name, attrs|
465
+ attrs[name] = _read_attribute(name)
466
+ end
416
467
  end
417
- end
418
468
 
419
- # Filters out the primary keys, from the attribute names, when the primary
420
- # key is to be generated (e.g. the id attribute has no value).
421
- def attributes_for_create(attribute_names)
422
- attribute_names.reject do |name|
423
- pk_attribute?(name) && id.nil?
469
+ # Filters the primary keys and readonly attributes from the attribute names.
470
+ def attributes_for_update(attribute_names)
471
+ attribute_names.reject do |name|
472
+ readonly_attribute?(name)
473
+ end
424
474
  end
425
- end
426
475
 
427
- def readonly_attribute?(name)
428
- self.class.readonly_attributes.include?(name)
429
- end
476
+ # Filters out the primary keys, from the attribute names, when the primary
477
+ # key is to be generated (e.g. the id attribute has no value).
478
+ def attributes_for_create(attribute_names)
479
+ attribute_names.reject do |name|
480
+ pk_attribute?(name) && id.nil?
481
+ end
482
+ end
430
483
 
431
- def pk_attribute?(name)
432
- name == self.class.primary_key
433
- end
484
+ def readonly_attribute?(name)
485
+ self.class.readonly_attributes.include?(name)
486
+ end
434
487
 
435
- def typecasted_attribute_value(name)
436
- _read_attribute(name)
437
- end
488
+ def pk_attribute?(name)
489
+ name == self.class.primary_key
490
+ end
438
491
  end
439
492
  end