activerecord 4.2.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -18,10 +18,9 @@ module ActiveRecord
18
18
  # conversation.archived? # => true
19
19
  # conversation.status # => "archived"
20
20
  #
21
- # # conversation.update! status: 1
21
+ # # conversation.status = 1
22
22
  # conversation.status = "archived"
23
23
  #
24
- # # conversation.update! status: nil
25
24
  # conversation.status = nil
26
25
  # conversation.status.nil? # => true
27
26
  # conversation.status # => nil
@@ -32,6 +31,12 @@ module ActiveRecord
32
31
  # Conversation.active
33
32
  # Conversation.archived
34
33
  #
34
+ # Of course, you can also query them directly if the scopes don't fit your
35
+ # needs:
36
+ #
37
+ # Conversation.where(status: [:active, :archived])
38
+ # Conversation.where.not(status: :active)
39
+ #
35
40
  # You can set the default value from the database declaration, like:
36
41
  #
37
42
  # create_table :conversations do |t|
@@ -41,13 +46,13 @@ module ActiveRecord
41
46
  # Good practice is to let the first declared status be the default.
42
47
  #
43
48
  # Finally, it's also possible to explicitly map the relation between attribute and
44
- # database integer with a +Hash+:
49
+ # database integer with a hash:
45
50
  #
46
51
  # class Conversation < ActiveRecord::Base
47
52
  # enum status: { active: 0, archived: 1 }
48
53
  # end
49
54
  #
50
- # Note that when an +Array+ is used, the implicit mapping from the values to database
55
+ # Note that when an array is used, the implicit mapping from the values to database
51
56
  # integers is derived from the order the values appear in the array. In the example,
52
57
  # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
53
58
  # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
@@ -55,22 +60,42 @@ module ActiveRecord
55
60
  #
56
61
  # Therefore, once a value is added to the enum array, its position in the array must
57
62
  # be maintained, and new values should only be added to the end of the array. To
58
- # remove unused values, the explicit +Hash+ syntax should be used.
63
+ # remove unused values, the explicit hash syntax should be used.
59
64
  #
60
65
  # In rare circumstances you might need to access the mapping directly.
61
66
  # The mappings are exposed through a class method with the pluralized attribute
62
- # name:
67
+ # name, which return the mapping in a +HashWithIndifferentAccess+:
63
68
  #
64
- # Conversation.statuses # => { "active" => 0, "archived" => 1 }
69
+ # Conversation.statuses[:active] # => 0
70
+ # Conversation.statuses["archived"] # => 1
65
71
  #
66
- # Use that class method when you need to know the ordinal value of an enum:
72
+ # Use that class method when you need to know the ordinal value of an enum.
73
+ # For example, you can use that when manually building SQL strings:
67
74
  #
68
75
  # Conversation.where("status <> ?", Conversation.statuses[:archived])
69
76
  #
70
- # Where conditions on an enum attribute must use the ordinal value of an enum.
77
+ # You can use the +:_prefix+ or +:_suffix+ options when you need to define
78
+ # multiple enums with same values. If the passed value is +true+, the methods
79
+ # are prefixed/suffixed with the name of the enum. It is also possible to
80
+ # supply a custom value:
81
+ #
82
+ # class Conversation < ActiveRecord::Base
83
+ # enum status: [:active, :archived], _suffix: true
84
+ # enum comments_status: [:active, :inactive], _prefix: :comments
85
+ # end
86
+ #
87
+ # With the above example, the bang and predicate methods along with the
88
+ # associated scopes are now prefixed and/or suffixed accordingly:
89
+ #
90
+ # conversation.active_status!
91
+ # conversation.archived_status? # => false
92
+ #
93
+ # conversation.comments_inactive!
94
+ # conversation.comments_active? # => false
95
+
71
96
  module Enum
72
97
  def self.extended(base) # :nodoc:
73
- base.class_attribute(:defined_enums)
98
+ base.class_attribute(:defined_enums, instance_writer: false)
74
99
  base.defined_enums = {}
75
100
  end
76
101
 
@@ -79,56 +104,93 @@ module ActiveRecord
79
104
  super
80
105
  end
81
106
 
107
+ class EnumType < Type::Value # :nodoc:
108
+ def initialize(name, mapping, subtype)
109
+ @name = name
110
+ @mapping = mapping
111
+ @subtype = subtype
112
+ end
113
+
114
+ def cast(value)
115
+ return if value.blank?
116
+
117
+ if mapping.has_key?(value)
118
+ value.to_s
119
+ elsif mapping.has_value?(value)
120
+ mapping.key(value)
121
+ else
122
+ assert_valid_value(value)
123
+ end
124
+ end
125
+
126
+ def deserialize(value)
127
+ return if value.nil?
128
+ mapping.key(subtype.deserialize(value))
129
+ end
130
+
131
+ def serialize(value)
132
+ mapping.fetch(value, value)
133
+ end
134
+
135
+ def assert_valid_value(value)
136
+ unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
137
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
138
+ end
139
+ end
140
+
141
+ protected
142
+
143
+ attr_reader :name, :mapping, :subtype
144
+ end
145
+
82
146
  def enum(definitions)
83
147
  klass = self
148
+ enum_prefix = definitions.delete(:_prefix)
149
+ enum_suffix = definitions.delete(:_suffix)
84
150
  definitions.each do |name, values|
85
151
  # statuses = { }
86
152
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
87
153
  name = name.to_sym
88
154
 
89
- # def self.statuses statuses end
155
+ # def self.statuses() statuses end
90
156
  detect_enum_conflict!(name, name.to_s.pluralize, true)
91
157
  klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
92
158
 
93
- _enum_methods_module.module_eval do
94
- # def status=(value) self[:status] = statuses[value] end
95
- klass.send(:detect_enum_conflict!, name, "#{name}=")
96
- define_method("#{name}=") { |value|
97
- if enum_values.has_key?(value) || value.blank?
98
- self[name] = enum_values[value]
99
- elsif enum_values.has_value?(value)
100
- # Assigning a value directly is not a end-user feature, hence it's not documented.
101
- # This is used internally to make building objects from the generated scopes work
102
- # as expected, i.e. +Conversation.archived.build.archived?+ should be true.
103
- self[name] = value
104
- else
105
- raise ArgumentError, "'#{value}' is not a valid #{name}"
106
- end
107
- }
108
-
109
- # def status() statuses.key self[:status] end
110
- klass.send(:detect_enum_conflict!, name, name)
111
- define_method(name) { enum_values.key self[name] }
159
+ detect_enum_conflict!(name, name)
160
+ detect_enum_conflict!(name, "#{name}=")
112
161
 
113
- # def status_before_type_cast() statuses.key self[:status] end
114
- klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
115
- define_method("#{name}_before_type_cast") { enum_values.key self[name] }
162
+ decorate_attribute_type(name, :enum) do |subtype|
163
+ EnumType.new(name, enum_values, subtype)
164
+ end
116
165
 
166
+ _enum_methods_module.module_eval do
117
167
  pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
118
168
  pairs.each do |value, i|
169
+ if enum_prefix == true
170
+ prefix = "#{name}_"
171
+ elsif enum_prefix
172
+ prefix = "#{enum_prefix}_"
173
+ end
174
+ if enum_suffix == true
175
+ suffix = "_#{name}"
176
+ elsif enum_suffix
177
+ suffix = "_#{enum_suffix}"
178
+ end
179
+
180
+ value_method_name = "#{prefix}#{value}#{suffix}"
119
181
  enum_values[value] = i
120
182
 
121
183
  # def active?() status == 0 end
122
- klass.send(:detect_enum_conflict!, name, "#{value}?")
123
- define_method("#{value}?") { self[name] == i }
184
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
185
+ define_method("#{value_method_name}?") { self[name] == value.to_s }
124
186
 
125
187
  # def active!() update! status: :active end
126
- klass.send(:detect_enum_conflict!, name, "#{value}!")
127
- define_method("#{value}!") { update! name => value }
188
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
189
+ define_method("#{value_method_name}!") { update! name => value }
128
190
 
129
191
  # scope :active, -> { where status: 0 }
130
- klass.send(:detect_enum_conflict!, name, value, true)
131
- klass.scope value, -> { klass.where name => i }
192
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
193
+ klass.scope value_method_name, -> { where(name => value) }
132
194
  end
133
195
  end
134
196
  defined_enums[name.to_s] = enum_values
@@ -138,25 +200,7 @@ module ActiveRecord
138
200
  private
139
201
  def _enum_methods_module
140
202
  @_enum_methods_module ||= begin
141
- mod = Module.new do
142
- private
143
- def save_changed_attribute(attr_name, old)
144
- if (mapping = self.class.defined_enums[attr_name.to_s])
145
- value = _read_attribute(attr_name)
146
- if attribute_changed?(attr_name)
147
- if mapping[old] == value
148
- clear_attribute_changes([attr_name])
149
- end
150
- else
151
- if old != value
152
- set_attribute_was(attr_name, mapping.key(old))
153
- end
154
- end
155
- else
156
- super
157
- end
158
- end
159
- end
203
+ mod = Module.new
160
204
  include mod
161
205
  mod
162
206
  end
@@ -169,30 +213,22 @@ module ActiveRecord
169
213
 
170
214
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
171
215
  if klass_method && dangerous_class_method?(method_name)
172
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
173
- enum: enum_name,
174
- klass: self.name,
175
- type: 'class',
176
- method: method_name,
177
- source: 'Active Record'
178
- }
216
+ raise_conflict_error(enum_name, method_name, type: 'class')
179
217
  elsif !klass_method && dangerous_attribute_method?(method_name)
180
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
181
- enum: enum_name,
182
- klass: self.name,
183
- type: 'instance',
184
- method: method_name,
185
- source: 'Active Record'
186
- }
218
+ raise_conflict_error(enum_name, method_name)
187
219
  elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
188
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
189
- enum: enum_name,
190
- klass: self.name,
191
- type: 'instance',
192
- method: method_name,
193
- source: 'another enum'
194
- }
220
+ raise_conflict_error(enum_name, method_name, source: 'another enum')
195
221
  end
196
222
  end
223
+
224
+ def raise_conflict_error(enum_name, method_name, type: 'instance', source: 'Active Record')
225
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
226
+ enum: enum_name,
227
+ klass: self.name,
228
+ type: type,
229
+ method: method_name,
230
+ source: source
231
+ }
232
+ end
197
233
  end
198
234
  end
@@ -7,8 +7,10 @@ module ActiveRecord
7
7
  end
8
8
 
9
9
  # Raised when the single-table inheritance mechanism fails to locate the subclass
10
- # (for example due to improper usage of column that +inheritance_column+ points to).
11
- class SubclassNotFound < ActiveRecordError #:nodoc:
10
+ # (for example due to improper usage of column that
11
+ # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
12
+ # points to).
13
+ class SubclassNotFound < ActiveRecordError
12
14
  end
13
15
 
14
16
  # Raised when an object assigned to an association has an incorrect type.
@@ -40,27 +42,40 @@ module ActiveRecord
40
42
  class AdapterNotFound < ActiveRecordError
41
43
  end
42
44
 
43
- # Raised when connection to the database could not been established (for
44
- # example when +connection=+ is given a nil object).
45
+ # Raised when connection to the database could not been established (for example when
46
+ # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
47
+ # is given a nil object).
45
48
  class ConnectionNotEstablished < ActiveRecordError
46
49
  end
47
50
 
48
- # Raised when Active Record cannot find record by given id or set of ids.
51
+ # Raised when Active Record cannot find a record by given id or set of ids.
49
52
  class RecordNotFound < ActiveRecordError
53
+ attr_reader :model, :primary_key, :id
54
+
55
+ def initialize(message = nil, model = nil, primary_key = nil, id = nil)
56
+ @primary_key = primary_key
57
+ @model = model
58
+ @id = id
59
+
60
+ super(message)
61
+ end
50
62
  end
51
63
 
52
- # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
53
- # saved because record is invalid.
64
+ # Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
65
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
66
+ # methods when a record is invalid and can not be saved.
54
67
  class RecordNotSaved < ActiveRecordError
55
68
  attr_reader :record
56
69
 
57
- def initialize(message, record = nil)
70
+ def initialize(message = nil, record = nil)
58
71
  @record = record
59
72
  super(message)
60
73
  end
61
74
  end
62
75
 
63
- # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
76
+ # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
77
+ # when a call to {#destroy}[rdoc-ref:Persistence#destroy!]
78
+ # would return false.
64
79
  #
65
80
  # begin
66
81
  # complex_operation_that_internally_calls_destroy!
@@ -71,26 +86,34 @@ module ActiveRecord
71
86
  class RecordNotDestroyed < ActiveRecordError
72
87
  attr_reader :record
73
88
 
74
- def initialize(record)
89
+ def initialize(message = nil, record = nil)
75
90
  @record = record
76
- super()
91
+ super(message)
77
92
  end
78
93
  end
79
94
 
80
95
  # Superclass for all database execution errors.
81
96
  #
82
- # Wraps the underlying database error as +original_exception+.
97
+ # Wraps the underlying database error as +cause+.
83
98
  class StatementInvalid < ActiveRecordError
84
- attr_reader :original_exception
85
99
 
86
- def initialize(message, original_exception = nil)
87
- super(message)
88
- @original_exception = original_exception
100
+ def initialize(message = nil, original_exception = nil)
101
+ if original_exception
102
+ ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
103
+ "Exceptions will automatically capture the original exception.", caller)
104
+ end
105
+
106
+ super(message || $!.try(:message))
107
+ end
108
+
109
+ def original_exception
110
+ ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
111
+ cause
89
112
  end
90
113
  end
91
114
 
92
115
  # Defunct wrapper class kept for compatibility.
93
- # +StatementInvalid+ wraps the original exception now.
116
+ # StatementInvalid wraps the original exception now.
94
117
  class WrappedDatabaseException < StatementInvalid
95
118
  end
96
119
 
@@ -102,9 +125,13 @@ module ActiveRecord
102
125
  class InvalidForeignKey < WrappedDatabaseException
103
126
  end
104
127
 
128
+ # Raised when a record cannot be inserted or updated because a value too long for a column type.
129
+ class ValueTooLong < StatementInvalid
130
+ end
131
+
105
132
  # Raised when number of bind variables in statement given to +:condition+ key
106
- # (for example, when using +find+ method) does not match number of expected
107
- # values supplied.
133
+ # (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
134
+ # does not match number of expected values supplied.
108
135
  #
109
136
  # For example, when there are two placeholders with only one value supplied:
110
137
  #
@@ -116,6 +143,11 @@ module ActiveRecord
116
143
  class NoDatabaseError < StatementInvalid
117
144
  end
118
145
 
146
+ # Raised when Postgres returns 'cached plan must not change result type' and
147
+ # we cannot retry gracefully (e.g. inside a transaction)
148
+ class PreparedStatementCacheExpired < StatementInvalid
149
+ end
150
+
119
151
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
120
152
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
121
153
  # the page before the other.
@@ -125,16 +157,22 @@ module ActiveRecord
125
157
  class StaleObjectError < ActiveRecordError
126
158
  attr_reader :record, :attempted_action
127
159
 
128
- def initialize(record, attempted_action)
129
- super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
130
- @record = record
131
- @attempted_action = attempted_action
160
+ def initialize(record = nil, attempted_action = nil)
161
+ if record && attempted_action
162
+ @record = record
163
+ @attempted_action = attempted_action
164
+ super("Attempted to #{attempted_action} a stale object: #{record.class.name}.")
165
+ else
166
+ super("Stale object error.")
167
+ end
132
168
  end
133
169
 
134
170
  end
135
171
 
136
172
  # Raised when association is being configured improperly or user tries to use
137
- # offset and limit together with +has_many+ or +has_and_belongs_to_many+
173
+ # offset and limit together with
174
+ # {ActiveRecord::Base.has_many}[rdoc-ref:Associations::ClassMethods#has_many] or
175
+ # {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many]
138
176
  # associations.
139
177
  class ConfigurationError < ActiveRecordError
140
178
  end
@@ -143,9 +181,10 @@ module ActiveRecord
143
181
  class ReadOnlyRecord < ActiveRecordError
144
182
  end
145
183
 
146
- # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
147
- # to distinguish a deliberate rollback from other exceptional situations.
148
- # Normally, raising an exception will cause the +transaction+ method to rollback
184
+ # {ActiveRecord::Base.transaction}[rdoc-ref:Transactions::ClassMethods#transaction]
185
+ # uses this exception to distinguish a deliberate rollback from other exceptional situations.
186
+ # Normally, raising an exception will cause the
187
+ # {.transaction}[rdoc-ref:Transactions::ClassMethods#transaction] method to rollback
149
188
  # the database transaction *and* pass on the exception. But if you raise an
150
189
  # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
151
190
  # without passing on the exception.
@@ -179,38 +218,29 @@ module ActiveRecord
179
218
  end
180
219
 
181
220
  # Raised when unknown attributes are supplied via mass assignment.
182
- class UnknownAttributeError < NoMethodError
183
-
184
- attr_reader :record, :attribute
185
-
186
- def initialize(record, attribute)
187
- @record = record
188
- @attribute = attribute.to_s
189
- super("unknown attribute '#{attribute}' for #{@record.class}.")
190
- end
191
-
192
- end
221
+ UnknownAttributeError = ActiveModel::UnknownAttributeError
193
222
 
194
223
  # Raised when an error occurred while doing a mass assignment to an attribute through the
195
- # +attributes=+ method. The exception has an +attribute+ property that is the name of the
196
- # offending attribute.
224
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
225
+ # The exception has an +attribute+ property that is the name of the offending attribute.
197
226
  class AttributeAssignmentError < ActiveRecordError
198
227
  attr_reader :exception, :attribute
199
228
 
200
- def initialize(message, exception, attribute)
229
+ def initialize(message = nil, exception = nil, attribute = nil)
201
230
  super(message)
202
231
  @exception = exception
203
232
  @attribute = attribute
204
233
  end
205
234
  end
206
235
 
207
- # Raised when there are multiple errors while doing a mass assignment through the +attributes+
236
+ # Raised when there are multiple errors while doing a mass assignment through the
237
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
208
238
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
209
239
  # objects, each corresponding to the error while assigning to an attribute.
210
240
  class MultiparameterAssignmentErrors < ActiveRecordError
211
241
  attr_reader :errors
212
242
 
213
- def initialize(errors)
243
+ def initialize(errors = nil)
214
244
  @errors = errors
215
245
  end
216
246
  end
@@ -219,11 +249,16 @@ module ActiveRecord
219
249
  class UnknownPrimaryKey < ActiveRecordError
220
250
  attr_reader :model
221
251
 
222
- def initialize(model)
223
- super("Unknown primary key for table #{model.table_name} in model #{model}.")
224
- @model = model
252
+ def initialize(model = nil, description = nil)
253
+ if model
254
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
255
+ message += "\n#{description}" if description
256
+ @model = model
257
+ super(message)
258
+ else
259
+ super("Unknown primary key.")
260
+ end
225
261
  end
226
-
227
262
  end
228
263
 
229
264
  # Raised when a relation cannot be mutated because it's already loaded.
@@ -246,7 +281,12 @@ module ActiveRecord
246
281
  # * You are joining an existing open transaction
247
282
  # * You are creating a nested (savepoint) transaction
248
283
  #
249
- # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
284
+ # The mysql2 and postgresql adapters support setting the transaction isolation level.
250
285
  class TransactionIsolationError < ActiveRecordError
251
286
  end
287
+
288
+ # IrreversibleOrderError is raised when a relation's order is too complex for
289
+ # +reverse_order+ to automatically reverse.
290
+ class IrreversibleOrderError < ActiveRecordError
291
+ end
252
292
  end
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  #
8
8
  # returns the collected queries local to the current thread.
9
9
  #
10
- # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
10
+ # See the documentation of ActiveSupport::PerThreadRegistry
11
11
  # for further details.
12
12
  class ExplainRegistry # :nodoc:
13
13
  extend ActiveSupport::PerThreadRegistry
@@ -14,12 +14,12 @@ module ActiveRecord
14
14
  end
15
15
 
16
16
  # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
17
- # our own EXPLAINs now matter how loopingly beautiful that would be.
17
+ # our own EXPLAINs no matter how loopingly beautiful that would be.
18
18
  #
19
19
  # On the other hand, we want to monitor the performance of our real database
20
20
  # queries, not the performance of the access to the query cache.
21
21
  IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
22
- EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
22
+ EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
23
23
  def ignore_payload?(payload)
24
24
  payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
25
25
  end
@@ -17,29 +17,50 @@ module ActiveRecord
17
17
 
18
18
  def initialize(file)
19
19
  @file = file
20
- @rows = nil
21
20
  end
22
21
 
23
22
  def each(&block)
24
23
  rows.each(&block)
25
24
  end
26
25
 
26
+ def model_class
27
+ config_row['model_class']
28
+ end
27
29
 
28
30
  private
29
31
  def rows
30
- return @rows if @rows
32
+ @rows ||= raw_rows.reject { |fixture_name, _| fixture_name == '_fixture' }
33
+ end
31
34
 
32
- begin
35
+ def config_row
36
+ @config_row ||= begin
37
+ row = raw_rows.find { |fixture_name, _| fixture_name == '_fixture' }
38
+ if row
39
+ row.last
40
+ else
41
+ {'model_class': nil}
42
+ end
43
+ end
44
+ end
45
+
46
+ def raw_rows
47
+ @raw_rows ||= begin
33
48
  data = YAML.load(render(IO.read(@file)))
49
+ data ? validate(data).to_a : []
34
50
  rescue ArgumentError, Psych::SyntaxError => error
35
51
  raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
36
52
  end
37
- @rows = data ? validate(data).to_a : []
53
+ end
54
+
55
+ def prepare_erb(content)
56
+ erb = ERB.new(content)
57
+ erb.filename = @file
58
+ erb
38
59
  end
39
60
 
40
61
  def render(content)
41
62
  context = ActiveRecord::FixtureSet::RenderContext.create_subclass.new
42
- ERB.new(content).result(context.get_binding)
63
+ prepare_erb(content).result(context.get_binding)
43
64
  end
44
65
 
45
66
  # Validate our unmarshalled data.