activerecord 3.2.19 → 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 (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ # Returns the version of the currently loaded Active Record as a <tt>Gem::Version</tt>
3
+ def self.gem_version
4
+ Gem::Version.new VERSION::STRING
5
+ end
6
+
7
+ module VERSION
8
+ MAJOR = 5
9
+ MINOR = 0
10
+ TINY = 0
11
+ PRE = nil
12
+
13
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
+ end
15
+ end
@@ -1,19 +1,77 @@
1
- require 'active_support/concern'
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
2
 
3
3
  module ActiveRecord
4
+ # == Single table inheritance
5
+ #
6
+ # Active Record allows inheritance by storing the name of the class in a column that by
7
+ # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
8
+ # This means that an inheritance looking like this:
9
+ #
10
+ # class Company < ActiveRecord::Base; end
11
+ # class Firm < Company; end
12
+ # class Client < Company; end
13
+ # class PriorityClient < Client; end
14
+ #
15
+ # When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
16
+ # the companies table with type = "Firm". You can then fetch this row again using
17
+ # <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
18
+ #
19
+ # Be aware that because the type column is an attribute on the record every new
20
+ # subclass will instantly be marked as dirty and the type column will be included
21
+ # in the list of changed attributes on the record. This is different from non
22
+ # STI classes:
23
+ #
24
+ # Company.new.changed? # => false
25
+ # Firm.new.changed? # => true
26
+ # Firm.new.changes # => {"type"=>["","Firm"]}
27
+ #
28
+ # If you don't have a type column defined in your table, single-table inheritance won't
29
+ # be triggered. In that case, it'll work just like normal subclasses with no special magic
30
+ # for differentiating between them or reloading the right type with find.
31
+ #
32
+ # Note, all the attributes for all the cases are kept in the same table. Read more:
33
+ # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
34
+ #
4
35
  module Inheritance
5
36
  extend ActiveSupport::Concern
6
37
 
7
38
  included do
8
- # Determine whether to store the full constant name including namespace when using STI
9
- class_attribute :store_full_sti_class
39
+ # Determines whether to store the full constant name including namespace when using STI.
40
+ class_attribute :store_full_sti_class, instance_writer: false
10
41
  self.store_full_sti_class = true
11
42
  end
12
43
 
13
44
  module ClassMethods
14
- # True if this isn't a concrete subclass needing a STI type condition.
45
+ # Determines if one of the attributes passed in is the inheritance column,
46
+ # and if the inheritance column is attr accessible, it initializes an
47
+ # instance of the given subclass instead of the base class.
48
+ def new(*args, &block)
49
+ if abstract_class? || self == Base
50
+ raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
51
+ end
52
+
53
+ attrs = args.first
54
+ if has_attribute?(inheritance_column)
55
+ subclass = subclass_from_attributes(attrs)
56
+
57
+ if subclass.nil? && base_class == self
58
+ subclass = subclass_from_attributes(column_defaults)
59
+ end
60
+ end
61
+
62
+ if subclass && subclass != self
63
+ subclass.new(*args, &block)
64
+ else
65
+ super
66
+ end
67
+ end
68
+
69
+ # Returns +true+ if this does not need STI type condition. Returns
70
+ # +false+ if STI type condition needs to be applied.
15
71
  def descends_from_active_record?
16
- if superclass.abstract_class?
72
+ if self == Base
73
+ false
74
+ elsif superclass.abstract_class?
17
75
  superclass.descends_from_active_record?
18
76
  else
19
77
  superclass == Base || !columns_hash.include?(inheritance_column)
@@ -25,25 +83,41 @@ module ActiveRecord
25
83
  :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
26
84
  end
27
85
 
28
- def symbolized_base_class
29
- @symbolized_base_class ||= base_class.to_s.to_sym
30
- end
31
-
32
- def symbolized_sti_name
33
- @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
34
- end
35
-
36
- # Returns the base AR subclass that this class descends from. If A
37
- # extends AR::Base, A.base_class will return A. If B descends from A
86
+ # Returns the class descending directly from ActiveRecord::Base, or
87
+ # an abstract class, if any, in the inheritance hierarchy.
88
+ #
89
+ # If A extends ActiveRecord::Base, A.base_class will return A. If B descends from A
38
90
  # through some arbitrarily deep hierarchy, B.base_class will return A.
39
91
  #
40
92
  # If B < A and C < B and if A is an abstract_class then both B.base_class
41
93
  # and C.base_class would return B as the answer since A is an abstract_class.
42
94
  def base_class
43
- class_of_active_record_descendant(self)
95
+ unless self < Base
96
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
97
+ end
98
+
99
+ if superclass == Base || superclass.abstract_class?
100
+ self
101
+ else
102
+ superclass.base_class
103
+ end
44
104
  end
45
105
 
46
106
  # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
107
+ # If you are using inheritance with ActiveRecord and don't want child classes
108
+ # to utilize the implied STI table name of the parent class, this will need to be true.
109
+ # For example, given the following:
110
+ #
111
+ # class SuperClass < ActiveRecord::Base
112
+ # self.abstract_class = true
113
+ # end
114
+ # class Child < SuperClass
115
+ # self.table_name = 'the_table_i_really_want'
116
+ # end
117
+ #
118
+ #
119
+ # <tt>self.abstract_class = true</tt> is required to make <tt>Child<.find,.create, or any Arel method></tt> use <tt>the_table_i_really_want</tt> instead of a table called <tt>super_classes</tt>
120
+ #
47
121
  attr_accessor :abstract_class
48
122
 
49
123
  # Returns whether this class is an abstract class or not.
@@ -55,36 +129,8 @@ module ActiveRecord
55
129
  store_full_sti_class ? name : name.demodulize
56
130
  end
57
131
 
58
- # Finder methods must instantiate through this method to work with the
59
- # single-table inheritance model that makes it possible to create
60
- # objects of different types from the same table.
61
- def instantiate(record)
62
- sti_class = find_sti_class(record[inheritance_column])
63
- record_id = sti_class.primary_key && record[sti_class.primary_key]
64
-
65
- if ActiveRecord::IdentityMap.enabled? && record_id
66
- instance = use_identity_map(sti_class, record_id, record)
67
- else
68
- instance = sti_class.allocate.init_with('attributes' => record)
69
- end
70
-
71
- instance
72
- end
73
-
74
132
  protected
75
133
 
76
- # Returns the class descending directly from ActiveRecord::Base or an
77
- # abstract class, if any, in the inheritance hierarchy.
78
- def class_of_active_record_descendant(klass)
79
- if klass == Base || klass.superclass == Base || klass.superclass.abstract_class?
80
- klass
81
- elsif klass.superclass.nil?
82
- raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
83
- else
84
- class_of_active_record_descendant(klass.superclass)
85
- end
86
- end
87
-
88
134
  # Returns the class type of the record using the current module as a prefix. So descendants of
89
135
  # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
90
136
  def compute_type(type_name)
@@ -99,66 +145,85 @@ module ActiveRecord
99
145
  candidates << type_name
100
146
 
101
147
  candidates.each do |candidate|
102
- begin
103
- constant = ActiveSupport::Dependencies.constantize(candidate)
104
- return constant if candidate == constant.to_s
105
- rescue NameError => e
106
- # We don't want to swallow NoMethodError < NameError errors
107
- raise e unless e.instance_of?(NameError)
108
- end
148
+ constant = ActiveSupport::Dependencies.safe_constantize(candidate)
149
+ return constant if candidate == constant.to_s
109
150
  end
110
151
 
111
- raise NameError, "uninitialized constant #{candidates.first}"
152
+ raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
112
153
  end
113
154
  end
114
155
 
115
156
  private
116
157
 
117
- def use_identity_map(sti_class, record_id, record)
118
- if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
119
- record_id = record_id.to_i
120
- end
121
-
122
- if instance = IdentityMap.get(sti_class, record_id)
123
- instance.reinit_with('attributes' => record)
158
+ # Called by +instantiate+ to decide which class to use for a new
159
+ # record instance. For single-table inheritance, we check the record
160
+ # for a +type+ column and return the corresponding class.
161
+ def discriminate_class_for_record(record)
162
+ if using_single_table_inheritance?(record)
163
+ find_sti_class(record[inheritance_column])
124
164
  else
125
- instance = sti_class.allocate.init_with('attributes' => record)
126
- IdentityMap.add(instance)
165
+ super
127
166
  end
167
+ end
128
168
 
129
- instance
169
+ def using_single_table_inheritance?(record)
170
+ record[inheritance_column].present? && has_attribute?(inheritance_column)
130
171
  end
131
172
 
132
173
  def find_sti_class(type_name)
133
- if type_name.blank? || !columns_hash.include?(inheritance_column)
134
- self
135
- else
136
- begin
137
- if store_full_sti_class
138
- ActiveSupport::Dependencies.constantize(type_name)
139
- else
140
- compute_type(type_name)
141
- end
142
- rescue NameError
143
- raise SubclassNotFound,
144
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
145
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
146
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
147
- "or overwrite #{name}.inheritance_column to use another column for that information."
174
+ type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
175
+ subclass = begin
176
+ if store_full_sti_class
177
+ ActiveSupport::Dependencies.constantize(type_name)
178
+ else
179
+ compute_type(type_name)
148
180
  end
181
+ rescue NameError
182
+ raise SubclassNotFound,
183
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
184
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
185
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
186
+ "or overwrite #{name}.inheritance_column to use another column for that information."
187
+ end
188
+ unless subclass == self || descendants.include?(subclass)
189
+ raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
149
190
  end
191
+ subclass
150
192
  end
151
193
 
152
194
  def type_condition(table = arel_table)
153
- sti_column = table[inheritance_column.to_sym]
154
- sti_names = ([self] + descendants).map { |model| model.sti_name }
195
+ sti_column = arel_attribute(inheritance_column, table)
196
+ sti_names = ([self] + descendants).map(&:sti_name)
155
197
 
156
198
  sti_column.in(sti_names)
157
199
  end
200
+
201
+ # Detect the subclass from the inheritance column of attrs. If the inheritance column value
202
+ # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
203
+ def subclass_from_attributes(attrs)
204
+ attrs = attrs.to_h if attrs.respond_to?(:permitted?)
205
+ if attrs.is_a?(Hash)
206
+ subclass_name = attrs.with_indifferent_access[inheritance_column]
207
+
208
+ if subclass_name.present?
209
+ find_sti_class(subclass_name)
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ def initialize_dup(other)
216
+ super
217
+ ensure_proper_type
158
218
  end
159
219
 
160
220
  private
161
221
 
222
+ def initialize_internals_callback
223
+ super
224
+ ensure_proper_type
225
+ end
226
+
162
227
  # Sets the attribute used for single table inheritance to this class name if this is not the
163
228
  # ActiveRecord::Base descendant.
164
229
  # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
1
3
  module ActiveRecord
2
4
  module Integration
3
5
  extend ActiveSupport::Concern
@@ -5,13 +7,15 @@ module ActiveRecord
5
7
  included do
6
8
  ##
7
9
  # :singleton-method:
8
- # Indicates the format used to generate the timestamp format in the cache key.
9
- # This is +:number+, by default.
10
+ # Indicates the format used to generate the timestamp in the cache key.
11
+ # Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
12
+ #
13
+ # This is +:usec+, by default.
10
14
  class_attribute :cache_timestamp_format, :instance_writer => false
11
- self.cache_timestamp_format = :number
15
+ self.cache_timestamp_format = :usec
12
16
  end
13
17
 
14
- # Returns a String, which Action Pack uses for constructing an URL to this
18
+ # Returns a String, which Action Pack uses for constructing a URL to this
15
19
  # object. The default implementation returns this record's id as a String,
16
20
  # or nil if this record's unsaved.
17
21
  #
@@ -19,7 +23,7 @@ module ActiveRecord
19
23
  # <tt>resources :users</tt> route. Normally, +user_path+ will
20
24
  # construct a path with the user object's 'id' in it:
21
25
  #
22
- # user = User.find_by_name('Phusion')
26
+ # user = User.find_by(name: 'Phusion')
23
27
  # user_path(user) # => "/users/1"
24
28
  #
25
29
  # You can override +to_param+ in your model to make +user_path+ construct
@@ -31,7 +35,7 @@ module ActiveRecord
31
35
  # end
32
36
  # end
33
37
  #
34
- # user = User.find_by_name('Phusion')
38
+ # user = User.find_by(name: 'Phusion')
35
39
  # user_path(user) # => "/users/Phusion"
36
40
  def to_param
37
41
  # We can't use alias_method here, because method 'id' optimizes itself on the fly.
@@ -40,20 +44,69 @@ module ActiveRecord
40
44
 
41
45
  # Returns a cache key that can be used to identify this record.
42
46
  #
43
- # ==== Examples
44
- #
45
47
  # Product.new.cache_key # => "products/new"
46
48
  # Product.find(5).cache_key # => "products/5" (updated_at not available)
47
49
  # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
48
- def cache_key
50
+ #
51
+ # You can also pass a list of named timestamps, and the newest in the list will be
52
+ # used to generate the key:
53
+ #
54
+ # Person.find(5).cache_key(:updated_at, :last_reviewed_at)
55
+ def cache_key(*timestamp_names)
49
56
  case
50
57
  when new_record?
51
- "#{self.class.model_name.cache_key}/new"
52
- when timestamp = self[:updated_at]
58
+ "#{model_name.cache_key}/new"
59
+ when timestamp_names.any?
60
+ timestamp = max_updated_column_timestamp(timestamp_names)
53
61
  timestamp = timestamp.utc.to_s(cache_timestamp_format)
54
- "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
62
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
63
+ when timestamp = max_updated_column_timestamp
64
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
65
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
55
66
  else
56
- "#{self.class.model_name.cache_key}/#{id}"
67
+ "#{model_name.cache_key}/#{id}"
68
+ end
69
+ end
70
+
71
+ module ClassMethods
72
+ # Defines your model's +to_param+ method to generate "pretty" URLs
73
+ # using +method_name+, which can be any attribute or method that
74
+ # responds to +to_s+.
75
+ #
76
+ # class User < ActiveRecord::Base
77
+ # to_param :name
78
+ # end
79
+ #
80
+ # user = User.find_by(name: 'Fancy Pants')
81
+ # user.id # => 123
82
+ # user_path(user) # => "/users/123-fancy-pants"
83
+ #
84
+ # Values longer than 20 characters will be truncated. The value
85
+ # is truncated word by word.
86
+ #
87
+ # user = User.find_by(name: 'David Heinemeier Hansson')
88
+ # user.id # => 125
89
+ # user_path(user) # => "/users/125-david"
90
+ #
91
+ # Because the generated param begins with the record's +id+, it is
92
+ # suitable for passing to +find+. In a controller, for example:
93
+ #
94
+ # params[:id] # => "123-fancy-pants"
95
+ # User.find(params[:id]).id # => 123
96
+ def to_param(method_name = nil)
97
+ if method_name.nil?
98
+ super()
99
+ else
100
+ define_method :to_param do
101
+ if (default = super()) &&
102
+ (result = send(method_name).to_s).present? &&
103
+ (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
104
+ "#{default}-#{param}"
105
+ else
106
+ default
107
+ end
108
+ end
109
+ end
57
110
  end
58
111
  end
59
112
  end
@@ -0,0 +1,56 @@
1
+ require 'active_record/scoping/default'
2
+ require 'active_record/scoping/named'
3
+
4
+ module ActiveRecord
5
+ # This class is used to create a table that keeps track of values and keys such
6
+ # as which environment migrations were run in.
7
+ class InternalMetadata < ActiveRecord::Base # :nodoc:
8
+ class << self
9
+ def primary_key
10
+ "key"
11
+ end
12
+
13
+ def table_name
14
+ "#{table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{table_name_suffix}"
15
+ end
16
+
17
+ def original_table_name
18
+ "#{table_name_prefix}active_record_internal_metadatas#{table_name_suffix}"
19
+ end
20
+
21
+ def []=(key, value)
22
+ find_or_initialize_by(key: key).update_attributes!(value: value)
23
+ end
24
+
25
+ def [](key)
26
+ where(key: key).pluck(:value).first
27
+ end
28
+
29
+ def table_exists?
30
+ ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) }
31
+ end
32
+
33
+ def original_table_exists?
34
+ # This method will be removed in Rails 5.1
35
+ # Since it is only necessary when `active_record_internal_metadatas` could exist
36
+ ActiveSupport::Deprecation.silence { connection.table_exists?(original_table_name) }
37
+ end
38
+
39
+ # Creates an internal metadata table with columns +key+ and +value+
40
+ def create_table
41
+ if original_table_exists?
42
+ connection.rename_table(original_table_name, table_name)
43
+ end
44
+ unless table_exists?
45
+ key_options = connection.internal_string_options_for_primary_key
46
+
47
+ connection.create_table(table_name, id: false) do |t|
48
+ t.string :key, key_options
49
+ t.string :value
50
+ t.timestamps
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveRecord
2
+ module LegacyYamlAdapter
3
+ def self.convert(klass, coder)
4
+ return coder unless coder.is_a?(Psych::Coder)
5
+
6
+ case coder["active_record_yaml_version"]
7
+ when 1 then coder
8
+ else
9
+ if coder["attributes"].is_a?(AttributeSet)
10
+ Rails420.convert(klass, coder)
11
+ else
12
+ Rails41.convert(klass, coder)
13
+ end
14
+ end
15
+ end
16
+
17
+ module Rails420
18
+ def self.convert(klass, coder)
19
+ attribute_set = coder["attributes"]
20
+
21
+ klass.attribute_names.each do |attr_name|
22
+ attribute = attribute_set[attr_name]
23
+ if attribute.type.is_a?(Delegator)
24
+ type_from_klass = klass.type_for_attribute(attr_name)
25
+ attribute_set[attr_name] = attribute.with_type(type_from_klass)
26
+ end
27
+ end
28
+
29
+ coder
30
+ end
31
+ end
32
+
33
+ module Rails41
34
+ def self.convert(klass, coder)
35
+ attributes = klass.attributes_builder
36
+ .build_from_database(coder["attributes"])
37
+ new_record = coder["attributes"][klass.primary_key].blank?
38
+
39
+ {
40
+ "attributes" => attributes,
41
+ "new_record" => new_record,
42
+ }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -4,12 +4,20 @@ en:
4
4
  #created_at: "Created at"
5
5
  #updated_at: "Updated at"
6
6
 
7
+ # Default error messages
8
+ errors:
9
+ messages:
10
+ required: "must exist"
11
+ taken: "has already been taken"
12
+
7
13
  # Active Record models configuration
8
14
  activerecord:
9
15
  errors:
10
16
  messages:
11
- taken: "has already been taken"
12
17
  record_invalid: "Validation failed: %{errors}"
18
+ restrict_dependent_destroy:
19
+ has_one: "Cannot delete record because a dependent %{record} exists"
20
+ has_many: "Cannot delete record because dependent %{record} exist"
13
21
  # Append your own errors here or at the model/attributes scope.
14
22
 
15
23
  # You can define own errors for models or model attributes.