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
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2014 David Heinemeier Hansson
1
+ Copyright (c) 2004-2016 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
17
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc CHANGED
@@ -26,13 +26,13 @@ The Product class is automatically mapped to the table named "products",
26
26
  which might look like this:
27
27
 
28
28
  CREATE TABLE products (
29
- id int(11) NOT NULL auto_increment,
29
+ id int NOT NULL auto_increment,
30
30
  name varchar(255),
31
31
  PRIMARY KEY (id)
32
32
  );
33
33
 
34
- This would also define the following accessors: `Product#name` and
35
- `Product#name=(new_name)`.
34
+ This would also define the following accessors: <tt>Product#name</tt> and
35
+ <tt>Product#name=(new_name)</tt>.
36
36
 
37
37
 
38
38
  * Associations between objects defined by simple class methods.
@@ -125,7 +125,7 @@ This would also define the following accessors: `Product#name` and
125
125
  )
126
126
 
127
127
  {Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
128
- MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html],
128
+ MySQL[link:classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
129
129
  PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
130
130
  SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
131
131
 
@@ -138,7 +138,7 @@ This would also define the following accessors: `Product#name` and
138
138
 
139
139
  * Database agnostic schema management with Migrations.
140
140
 
141
- class AddSystemSettings < ActiveRecord::Migration
141
+ class AddSystemSettings < ActiveRecord::Migration[5.0]
142
142
  def up
143
143
  create_table :system_settings do |t|
144
144
  t.string :name
@@ -188,11 +188,11 @@ Admit the Database:
188
188
 
189
189
  The latest version of Active Record can be installed with RubyGems:
190
190
 
191
- % [sudo] gem install activerecord
191
+ $ gem install activerecord
192
192
 
193
193
  Source code can be downloaded as part of the Rails project on GitHub:
194
194
 
195
- * https://github.com/rails/rails/tree/4-2-stable/activerecord
195
+ * https://github.com/rails/rails/tree/master/activerecord
196
196
 
197
197
 
198
198
  == License
@@ -215,4 +215,3 @@ Bug reports can be filed for the Ruby on Rails project here:
215
215
  Feature requests should be discussed on the rails-core mailing list here:
216
216
 
217
217
  * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
218
-
@@ -1,4 +1,3 @@
1
- require File.expand_path('../../../load_paths', __FILE__)
2
1
  require "active_record"
3
2
  require 'benchmark/ips'
4
3
 
@@ -39,8 +38,8 @@ class Exhibit < ActiveRecord::Base
39
38
  where("notes IS NOT NULL")
40
39
  end
41
40
 
42
- def self.look(exhibits) exhibits.each { |e| e.look } end
43
- def self.feel(exhibits) exhibits.each { |e| e.feel } end
41
+ def self.look(exhibits) exhibits.each(&:look) end
42
+ def self.feel(exhibits) exhibits.each(&:feel) end
44
43
  end
45
44
 
46
45
  def progress_bar(int); print "." if (int%100).zero? ; end
data/examples/simple.rb CHANGED
@@ -1,4 +1,3 @@
1
- require File.expand_path('../../../load_paths', __FILE__)
2
1
  require 'active_record'
3
2
 
4
3
  class Person < ActiveRecord::Base
@@ -1,14 +1,31 @@
1
1
  module ActiveRecord
2
- # = Active Record Aggregations
3
- module Aggregations # :nodoc:
2
+ # See ActiveRecord::Aggregations::ClassMethods for documentation
3
+ module Aggregations
4
4
  extend ActiveSupport::Concern
5
5
 
6
- def clear_aggregation_cache #:nodoc:
7
- @aggregation_cache.clear if persisted?
6
+ def initialize_dup(*) # :nodoc:
7
+ @aggregation_cache = {}
8
+ super
8
9
  end
9
10
 
10
- # Active Record implements aggregation through a macro-like class method called +composed_of+
11
- # for representing attributes as value objects. It expresses relationships like "Account [is]
11
+ def reload(*) # :nodoc:
12
+ clear_aggregation_cache
13
+ super
14
+ end
15
+
16
+ private
17
+
18
+ def clear_aggregation_cache # :nodoc:
19
+ @aggregation_cache.clear if persisted?
20
+ end
21
+
22
+ def init_internals # :nodoc:
23
+ @aggregation_cache = {}
24
+ super
25
+ end
26
+
27
+ # Active Record implements aggregation through a macro-like class method called #composed_of
28
+ # for representing attributes as value objects. It expresses relationships like "Account [is]
12
29
  # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
13
30
  # to the macro adds a description of how the value objects are created from the attributes of
14
31
  # the entity object (when the entity is initialized either as a new object or from finding an
@@ -16,7 +33,7 @@ module ActiveRecord
16
33
  # the database).
17
34
  #
18
35
  # class Customer < ActiveRecord::Base
19
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
36
+ # composed_of :balance, class_name: "Money", mapping: %w(amount currency)
20
37
  # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
21
38
  # end
22
39
  #
@@ -87,11 +104,6 @@ module ActiveRecord
87
104
  # customer.address_city = "Copenhagen"
88
105
  # customer.address # => Address.new("Hyancintvej", "Copenhagen")
89
106
  #
90
- # customer.address_street = "Vesterbrogade"
91
- # customer.address # => Address.new("Hyancintvej", "Copenhagen")
92
- # customer.clear_aggregation_cache
93
- # customer.address # => Address.new("Vesterbrogade", "Copenhagen")
94
- #
95
107
  # customer.address = Address.new("May Street", "Chicago")
96
108
  # customer.address_street # => "May Street"
97
109
  # customer.address_city # => "Chicago"
@@ -108,12 +120,12 @@ module ActiveRecord
108
120
  #
109
121
  # It's also important to treat the value objects as immutable. Don't allow the Money object to have
110
122
  # its amount changed after creation. Create a new Money object with the new value instead. The
111
- # Money#exchange_to method is an example of this. It returns a new value object instead of changing
123
+ # <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
112
124
  # its own values. Active Record won't persist value objects that have been changed through means
113
125
  # other than the writer method.
114
126
  #
115
127
  # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
116
- # object. Attempting to change it afterwards will result in a RuntimeError.
128
+ # object. Attempting to change it afterwards will result in a +RuntimeError+.
117
129
  #
118
130
  # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
119
131
  # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
@@ -122,17 +134,17 @@ module ActiveRecord
122
134
  #
123
135
  # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
124
136
  # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
125
- # option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
137
+ # option, as arguments. If the value class doesn't support this convention then #composed_of allows
126
138
  # a custom constructor to be specified.
127
139
  #
128
140
  # When a new value is assigned to the value object, the default assumption is that the new value
129
141
  # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
130
142
  # converted to an instance of value class if necessary.
131
143
  #
132
- # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be
133
- # aggregated using the NetAddr::CIDR value class (http://www.ruby-doc.org/gems/docs/n/netaddr-1.5.0/NetAddr/CIDR.html).
144
+ # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
145
+ # aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
134
146
  # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
135
- # New values can be assigned to the value object using either another NetAddr::CIDR object, a string
147
+ # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
136
148
  # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
137
149
  # these requirements:
138
150
  #
@@ -161,7 +173,7 @@ module ActiveRecord
161
173
  #
162
174
  # == Finding records by a value object
163
175
  #
164
- # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database
176
+ # Once a #composed_of relationship is specified for a model, records can be loaded from the database
165
177
  # by specifying an instance of the value object in the conditions hash. The following example
166
178
  # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
167
179
  #
@@ -174,7 +186,7 @@ module ActiveRecord
174
186
  # Options are:
175
187
  # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
176
188
  # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
177
- # to the Address class, but if the real class name is CompanyAddress, you'll have to specify it
189
+ # to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
178
190
  # with this option.
179
191
  # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
180
192
  # object. Each mapping is represented as an array where the first item is the name of the
@@ -244,14 +256,16 @@ module ActiveRecord
244
256
  def writer_method(name, class_name, mapping, allow_nil, converter)
245
257
  define_method("#{name}=") do |part|
246
258
  klass = class_name.constantize
247
- if part.is_a?(Hash)
248
- part = klass.new(*part.values)
249
- end
250
259
 
251
260
  unless part.is_a?(klass) || converter.nil? || part.nil?
252
261
  part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
253
262
  end
254
263
 
264
+ if part.is_a?(Hash)
265
+ raise ArgumentError unless part.size == part.keys.max
266
+ part = klass.new(*part.sort.map(&:last))
267
+ end
268
+
255
269
  if part.nil? && allow_nil
256
270
  mapping.each { |key, _| self[key] = nil }
257
271
  @aggregation_cache[name] = nil
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  class AssociationRelation < Relation
3
- def initialize(klass, table, association)
4
- super(klass, table)
3
+ def initialize(klass, table, predicate_builder, association)
4
+ super(klass, table, predicate_builder)
5
5
  @association = association
6
6
  end
7
7
 
@@ -10,7 +10,20 @@ module ActiveRecord
10
10
  end
11
11
 
12
12
  def ==(other)
13
- other == to_a
13
+ other == records
14
+ end
15
+
16
+ def build(*args, &block)
17
+ scoping { @association.build(*args, &block) }
18
+ end
19
+ alias new build
20
+
21
+ def create(*args, &block)
22
+ scoping { @association.create(*args, &block) }
23
+ end
24
+
25
+ def create!(*args, &block)
26
+ scoping { @association.create!(*args, &block) }
14
27
  end
15
28
 
16
29
  private
@@ -2,23 +2,25 @@ require 'active_support/core_ext/string/conversions'
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
5
- # Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
6
- # ActiveRecord::Associations::ThroughAssociationScope
5
+ # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
7
6
  class AliasTracker # :nodoc:
8
- attr_reader :aliases, :connection
7
+ attr_reader :aliases
9
8
 
10
- def self.empty(connection)
11
- new connection, Hash.new(0)
9
+ def self.create(connection, initial_table, type_caster)
10
+ aliases = Hash.new(0)
11
+ aliases[initial_table] = 1
12
+ new connection, aliases, type_caster
12
13
  end
13
14
 
14
- def self.create(connection, table_joins)
15
- if table_joins.empty?
16
- empty connection
15
+ def self.create_with_joins(connection, initial_table, joins, type_caster)
16
+ if joins.empty?
17
+ create(connection, initial_table, type_caster)
17
18
  else
18
- aliases = Hash.new { |h,k|
19
- h[k] = initial_count_for(connection, k, table_joins)
19
+ aliases = Hash.new { |h, k|
20
+ h[k] = initial_count_for(connection, k, joins)
20
21
  }
21
- new connection, aliases
22
+ aliases[initial_table] = 1
23
+ new connection, aliases, type_caster
22
24
  end
23
25
  end
24
26
 
@@ -51,19 +53,20 @@ module ActiveRecord
51
53
  end
52
54
 
53
55
  # table_joins is an array of arel joins which might conflict with the aliases we assign here
54
- def initialize(connection, aliases)
56
+ def initialize(connection, aliases, type_caster)
55
57
  @aliases = aliases
56
58
  @connection = connection
59
+ @type_caster = type_caster
57
60
  end
58
61
 
59
62
  def aliased_table_for(table_name, aliased_name)
60
63
  if aliases[table_name].zero?
61
64
  # If it's zero, we can have our table_name
62
65
  aliases[table_name] = 1
63
- Arel::Table.new(table_name)
66
+ Arel::Table.new(table_name, type_caster: @type_caster)
64
67
  else
65
68
  # Otherwise, we need to use an alias
66
- aliased_name = connection.table_alias_for(aliased_name)
69
+ aliased_name = @connection.table_alias_for(aliased_name)
67
70
 
68
71
  # Update the count
69
72
  aliases[aliased_name] += 1
@@ -73,14 +76,14 @@ module ActiveRecord
73
76
  else
74
77
  aliased_name
75
78
  end
76
- Arel::Table.new(table_name).alias(table_alias)
79
+ Arel::Table.new(table_name, type_caster: @type_caster).alias(table_alias)
77
80
  end
78
81
  end
79
82
 
80
83
  private
81
84
 
82
85
  def truncate(name)
83
- name.slice(0, connection.table_alias_length - 2)
86
+ name.slice(0, @connection.table_alias_length - 2)
84
87
  end
85
88
  end
86
89
  end
@@ -8,12 +8,12 @@ module ActiveRecord
8
8
  #
9
9
  # Association
10
10
  # SingularAssociation
11
- # HasOneAssociation
11
+ # HasOneAssociation + ForeignAssociation
12
12
  # HasOneThroughAssociation + ThroughAssociation
13
13
  # BelongsToAssociation
14
14
  # BelongsToPolymorphicAssociation
15
15
  # CollectionAssociation
16
- # HasManyAssociation
16
+ # HasManyAssociation + ForeignAssociation
17
17
  # HasManyThroughAssociation + ThroughAssociation
18
18
  class Association #:nodoc:
19
19
  attr_reader :owner, :target, :reflection
@@ -121,7 +121,7 @@ module ActiveRecord
121
121
  # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
122
122
  # through association's scope)
123
123
  def target_scope
124
- AssociationRelation.create(klass, klass.arel_table, self).merge!(klass.all)
124
+ AssociationRelation.create(klass, klass.arel_table, klass.predicate_builder, self).merge!(klass.all)
125
125
  end
126
126
 
127
127
  # Loads the \target if needed and returns it.
@@ -163,9 +163,12 @@ module ActiveRecord
163
163
  @reflection = @owner.class._reflect_on_association(reflection_name)
164
164
  end
165
165
 
166
- def initialize_attributes(record) #:nodoc:
166
+ def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
167
+ except_from_scope_attributes ||= {}
167
168
  skip_assign = [reflection.foreign_key, reflection.type].compact
168
- attributes = create_scope.except(*(record.changed - skip_assign))
169
+ assigned_keys = record.changed
170
+ assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
171
+ attributes = create_scope.except(*(assigned_keys - skip_assign))
169
172
  record.assign_attributes(attributes)
170
173
  set_inverse_instance(record)
171
174
  end
@@ -211,9 +214,12 @@ module ActiveRecord
211
214
  # the kind of the class of the associated objects. Meant to be used as
212
215
  # a sanity check when you are about to assign an associated record.
213
216
  def raise_on_type_mismatch!(record)
214
- unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
215
- message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
216
- raise ActiveRecord::AssociationTypeMismatch, message
217
+ unless record.is_a?(reflection.klass)
218
+ fresh_class = reflection.class_name.safe_constantize
219
+ unless fresh_class && record.is_a?(fresh_class)
220
+ message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
221
+ raise ActiveRecord::AssociationTypeMismatch, message
222
+ end
217
223
  end
218
224
  end
219
225
 
@@ -245,9 +251,17 @@ module ActiveRecord
245
251
 
246
252
  def build_record(attributes)
247
253
  reflection.build_association(attributes) do |record|
248
- initialize_attributes(record)
254
+ initialize_attributes(record, attributes)
249
255
  end
250
256
  end
257
+
258
+ # Returns true if statement cache should be skipped on the association reader.
259
+ def skip_statement_cache?
260
+ reflection.has_scope? ||
261
+ scope.eager_loading? ||
262
+ klass.scope_attributes? ||
263
+ reflection.source_reflection.active_record.default_scopes.any?
264
+ end
251
265
  end
252
266
  end
253
267
  end
@@ -2,41 +2,30 @@ module ActiveRecord
2
2
  module Associations
3
3
  class AssociationScope #:nodoc:
4
4
  def self.scope(association, connection)
5
- INSTANCE.scope association, connection
6
- end
7
-
8
- class BindSubstitution
9
- def initialize(block)
10
- @block = block
11
- end
12
-
13
- def bind_value(scope, column, value, alias_tracker)
14
- substitute = alias_tracker.connection.substitute_at(column)
15
- scope.bind_values += [[column, @block.call(value)]]
16
- substitute
17
- end
5
+ INSTANCE.scope(association, connection)
18
6
  end
19
7
 
20
8
  def self.create(&block)
21
- block = block ? block : lambda { |val| val }
22
- new BindSubstitution.new(block)
9
+ block ||= lambda { |val| val }
10
+ new(block)
23
11
  end
24
12
 
25
- def initialize(bind_substitution)
26
- @bind_substitution = bind_substitution
13
+ def initialize(value_transformation)
14
+ @value_transformation = value_transformation
27
15
  end
28
16
 
29
17
  INSTANCE = create
30
18
 
31
19
  def scope(association, connection)
32
- klass = association.klass
33
- reflection = association.reflection
34
- scope = klass.unscoped
35
- owner = association.owner
36
- alias_tracker = AliasTracker.empty connection
20
+ klass = association.klass
21
+ reflection = association.reflection
22
+ scope = klass.unscoped
23
+ owner = association.owner
24
+ alias_tracker = AliasTracker.create connection, association.klass.table_name, klass.type_caster
25
+ chain_head, chain_tail = get_chain(reflection, association, alias_tracker)
37
26
 
38
27
  scope.extending! Array(reflection.options[:extend])
39
- add_constraints(scope, owner, klass, reflection, alias_tracker)
28
+ add_constraints(scope, owner, klass, reflection, chain_head, chain_tail)
40
29
  end
41
30
 
42
31
  def join_type
@@ -60,132 +49,115 @@ module ActiveRecord
60
49
  binds
61
50
  end
62
51
 
63
- private
52
+ protected
64
53
 
65
- def construct_tables(chain, klass, refl, alias_tracker)
66
- chain.map do |reflection|
67
- alias_tracker.aliased_table_for(
68
- table_name_for(reflection, klass, refl),
69
- table_alias_for(reflection, refl, reflection != refl)
70
- )
71
- end
72
- end
73
-
74
- def table_alias_for(reflection, refl, join = false)
75
- name = "#{reflection.plural_name}_#{alias_suffix(refl)}"
76
- name << "_join" if join
77
- name
78
- end
54
+ attr_reader :value_transformation
79
55
 
56
+ private
80
57
  def join(table, constraint)
81
58
  table.create_join(table, table.create_on(constraint), join_type)
82
59
  end
83
60
 
84
- def column_for(table_name, column_name, alias_tracker)
85
- columns = alias_tracker.connection.schema_cache.columns_hash(table_name)
86
- columns[column_name]
87
- end
88
-
89
- def bind_value(scope, column, value, alias_tracker)
90
- @bind_substitution.bind_value scope, column, value, alias_tracker
91
- end
92
-
93
- def bind(scope, table_name, column_name, value, tracker)
94
- column = column_for table_name, column_name, tracker
95
- bind_value scope, column, value, tracker
96
- end
97
-
98
- def last_chain_scope(scope, table, reflection, owner, tracker, assoc_klass)
99
- join_keys = reflection.join_keys(assoc_klass)
61
+ def last_chain_scope(scope, table, reflection, owner, association_klass)
62
+ join_keys = reflection.join_keys(association_klass)
100
63
  key = join_keys.key
101
64
  foreign_key = join_keys.foreign_key
102
65
 
103
- bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
104
- scope = scope.where(table[key].eq(bind_val))
66
+ value = transform_value(owner[foreign_key])
67
+ scope = scope.where(table.name => { key => value })
105
68
 
106
69
  if reflection.type
107
- value = owner.class.base_class.name
108
- bind_val = bind scope, table.table_name, reflection.type, value, tracker
109
- scope = scope.where(table[reflection.type].eq(bind_val))
110
- else
111
- scope
70
+ polymorphic_type = transform_value(owner.class.base_class.name)
71
+ scope = scope.where(table.name => { reflection.type => polymorphic_type })
112
72
  end
73
+
74
+ scope
113
75
  end
114
76
 
115
- def next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
116
- join_keys = reflection.join_keys(assoc_klass)
77
+ def transform_value(value)
78
+ value_transformation.call(value)
79
+ end
80
+
81
+ def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
82
+ join_keys = reflection.join_keys(association_klass)
117
83
  key = join_keys.key
118
84
  foreign_key = join_keys.foreign_key
119
85
 
120
86
  constraint = table[key].eq(foreign_table[foreign_key])
121
87
 
122
88
  if reflection.type
123
- value = next_reflection.klass.base_class.name
124
- bind_val = bind scope, table.table_name, reflection.type, value, tracker
125
- scope = scope.where(table[reflection.type].eq(bind_val))
89
+ value = transform_value(next_reflection.klass.base_class.name)
90
+ scope = scope.where(table.name => { reflection.type => value })
126
91
  end
127
92
 
128
93
  scope = scope.joins(join(foreign_table, constraint))
129
94
  end
130
95
 
131
- def add_constraints(scope, owner, assoc_klass, refl, tracker)
132
- chain = refl.chain
133
- scope_chain = refl.scope_chain
96
+ class ReflectionProxy < SimpleDelegator # :nodoc:
97
+ attr_accessor :next
98
+ attr_reader :alias_name
134
99
 
135
- tables = construct_tables(chain, assoc_klass, refl, tracker)
100
+ def initialize(reflection, alias_name)
101
+ super(reflection)
102
+ @alias_name = alias_name
103
+ end
136
104
 
137
- owner_reflection = chain.last
138
- table = tables.last
139
- scope = last_chain_scope(scope, table, owner_reflection, owner, tracker, assoc_klass)
105
+ def all_includes; nil; end
106
+ end
140
107
 
141
- chain.each_with_index do |reflection, i|
142
- table, foreign_table = tables.shift, tables.first
108
+ def get_chain(reflection, association, tracker)
109
+ name = reflection.name
110
+ runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
111
+ previous_reflection = runtime_reflection
112
+ reflection.chain.drop(1).each do |refl|
113
+ alias_name = tracker.aliased_table_for(refl.table_name, refl.alias_candidate(name))
114
+ proxy = ReflectionProxy.new(refl, alias_name)
115
+ previous_reflection.next = proxy
116
+ previous_reflection = proxy
117
+ end
118
+ [runtime_reflection, previous_reflection]
119
+ end
143
120
 
144
- unless reflection == chain.last
145
- next_reflection = chain[i + 1]
146
- scope = next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
147
- end
121
+ def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tail)
122
+ owner_reflection = chain_tail
123
+ table = owner_reflection.alias_name
124
+ scope = last_chain_scope(scope, table, owner_reflection, owner, association_klass)
148
125
 
149
- is_first_chain = i == 0
150
- klass = is_first_chain ? assoc_klass : reflection.klass
126
+ reflection = chain_head
127
+ loop do
128
+ break unless reflection
129
+ table = reflection.alias_name
130
+
131
+ unless reflection == chain_tail
132
+ next_reflection = reflection.next
133
+ foreign_table = next_reflection.alias_name
134
+ scope = next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
135
+ end
151
136
 
152
137
  # Exclude the scope of the association itself, because that
153
138
  # was already merged in the #scope method.
154
- scope_chain[i].each do |scope_chain_item|
155
- item = eval_scope(klass, scope_chain_item, owner)
139
+ reflection.constraints.each do |scope_chain_item|
140
+ item = eval_scope(reflection.klass, scope_chain_item, owner)
156
141
 
157
142
  if scope_chain_item == refl.scope
158
- scope.merge! item.except(:where, :includes, :bind)
143
+ scope.merge! item.except(:where, :includes)
159
144
  end
160
145
 
161
- if is_first_chain
146
+ reflection.all_includes do
162
147
  scope.includes! item.includes_values
163
148
  end
164
149
 
165
- scope.where_values += item.where_values
166
- scope.bind_values += item.bind_values
150
+ scope.unscope!(*item.unscope_values)
151
+ scope.where_clause += item.where_clause
167
152
  scope.order_values |= item.order_values
168
153
  end
154
+
155
+ reflection = reflection.next
169
156
  end
170
157
 
171
158
  scope
172
159
  end
173
160
 
174
- def alias_suffix(refl)
175
- refl.name
176
- end
177
-
178
- def table_name_for(reflection, klass, refl)
179
- if reflection == refl
180
- # If this is a polymorphic belongs_to, we want to get the klass from the
181
- # association because it depends on the polymorphic_type attribute of
182
- # the owner
183
- klass.table_name
184
- else
185
- reflection.table_name
186
- end
187
- end
188
-
189
161
  def eval_scope(klass, scope, owner)
190
162
  klass.unscoped.instance_exec(owner, &scope)
191
163
  end