activerecord 4.2.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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,24 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ class CollectionAssociation < Association #:nodoc:
5
+
6
+ private
7
+
8
+ def build_scope
9
+ super.order(preload_scope.values[:order] || reflection_scope.values[:order])
10
+ end
11
+
12
+ def preload(preloader)
13
+ associated_records_by_owner(preloader).each do |owner, records|
14
+ association = owner.association(reflection.name)
15
+ association.loaded!
16
+ association.target.concat(records)
17
+ records.each { |record| association.set_inverse_instance(record) }
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ class HasMany < CollectionAssociation #:nodoc:
5
+
6
+ def association_key_name
7
+ reflection.foreign_key
8
+ end
9
+
10
+ def owner_key_name
11
+ reflection.active_record_primary_key
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ class HasManyThrough < CollectionAssociation #:nodoc:
5
+ include ThroughAssociation
6
+
7
+ def associated_records_by_owner(preloader)
8
+ records_by_owner = super
9
+
10
+ if reflection_scope.distinct_value
11
+ records_by_owner.each_value { |records| records.uniq! }
12
+ end
13
+
14
+ records_by_owner
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ class HasOne < SingularAssociation #:nodoc:
5
+
6
+ def association_key_name
7
+ reflection.foreign_key
8
+ end
9
+
10
+ def owner_key_name
11
+ reflection.active_record_primary_key
12
+ end
13
+
14
+ private
15
+
16
+ def build_scope
17
+ super.order(preload_scope.values[:order] || reflection_scope.values[:order])
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ class HasOneThrough < SingularAssociation #:nodoc:
5
+ include ThroughAssociation
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ class SingularAssociation < Association #:nodoc:
5
+
6
+ private
7
+
8
+ def preload(preloader)
9
+ associated_records_by_owner(preloader).each do |owner, associated_records|
10
+ record = associated_records.first
11
+
12
+ association = owner.association(reflection.name)
13
+ association.target = record
14
+ association.set_inverse_instance(record) if record
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,96 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ module ThroughAssociation #:nodoc:
5
+ def through_reflection
6
+ reflection.through_reflection
7
+ end
8
+
9
+ def source_reflection
10
+ reflection.source_reflection
11
+ end
12
+
13
+ def associated_records_by_owner(preloader)
14
+ preloader.preload(owners,
15
+ through_reflection.name,
16
+ through_scope)
17
+
18
+ through_records = owners.map do |owner|
19
+ association = owner.association through_reflection.name
20
+
21
+ [owner, Array(association.reader)]
22
+ end
23
+
24
+ reset_association owners, through_reflection.name
25
+
26
+ middle_records = through_records.flat_map { |(_,rec)| rec }
27
+
28
+ preloaders = preloader.preload(middle_records,
29
+ source_reflection.name,
30
+ reflection_scope)
31
+
32
+ @preloaded_records = preloaders.flat_map(&:preloaded_records)
33
+
34
+ middle_to_pl = preloaders.each_with_object({}) do |pl,h|
35
+ pl.owners.each { |middle|
36
+ h[middle] = pl
37
+ }
38
+ end
39
+
40
+ record_offset = {}
41
+ @preloaded_records.each_with_index do |record,i|
42
+ record_offset[record] = i
43
+ end
44
+
45
+ through_records.each_with_object({}) { |(lhs,center),records_by_owner|
46
+ pl_to_middle = center.group_by { |record| middle_to_pl[record] }
47
+
48
+ records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
49
+ rhs_records = middles.flat_map { |r|
50
+ association = r.association source_reflection.name
51
+
52
+ association.reader
53
+ }.compact
54
+
55
+ rhs_records.sort_by { |rhs| record_offset[rhs] }
56
+ end
57
+ }
58
+ end
59
+
60
+ private
61
+
62
+ def reset_association(owners, association_name)
63
+ should_reset = (through_scope != through_reflection.klass.unscoped) ||
64
+ (reflection.options[:source_type] && through_reflection.collection?)
65
+
66
+ # Don't cache the association - we would only be caching a subset
67
+ if should_reset
68
+ owners.each { |owner|
69
+ owner.association(association_name).reset
70
+ }
71
+ end
72
+ end
73
+
74
+
75
+ def through_scope
76
+ scope = through_reflection.klass.unscoped
77
+
78
+ if options[:source_type]
79
+ scope.where! reflection.foreign_type => options[:source_type]
80
+ else
81
+ unless reflection_scope.where_values.empty?
82
+ scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
83
+ scope.where_values = reflection_scope.values[:where]
84
+ scope.bind_values = reflection_scope.bind_values
85
+ end
86
+
87
+ scope.references! reflection_scope.values[:references]
88
+ scope = scope.order reflection_scope.values[:order] if scope.eager_loading?
89
+ end
90
+
91
+ scope
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,86 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class SingularAssociation < Association #:nodoc:
4
+ # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
5
+ def reader(force_reload = false)
6
+ if force_reload
7
+ klass.uncached { reload }
8
+ elsif !loaded? || stale_target?
9
+ reload
10
+ end
11
+
12
+ target
13
+ end
14
+
15
+ # Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
16
+ def writer(record)
17
+ replace(record)
18
+ end
19
+
20
+ def create(attributes = {}, &block)
21
+ _create_record(attributes, &block)
22
+ end
23
+
24
+ def create!(attributes = {}, &block)
25
+ _create_record(attributes, true, &block)
26
+ end
27
+
28
+ def build(attributes = {})
29
+ record = build_record(attributes)
30
+ yield(record) if block_given?
31
+ set_new_record(record)
32
+ record
33
+ end
34
+
35
+ private
36
+
37
+ def create_scope
38
+ scope.scope_for_create.stringify_keys.except(klass.primary_key)
39
+ end
40
+
41
+ def get_records
42
+ if reflection.scope_chain.any?(&:any?) ||
43
+ scope.eager_loading? ||
44
+ klass.current_scope ||
45
+ klass.default_scopes.any?
46
+
47
+ return scope.limit(1).to_a
48
+ end
49
+
50
+ conn = klass.connection
51
+ sc = reflection.association_scope_cache(conn, owner) do
52
+ StatementCache.create(conn) { |params|
53
+ as = AssociationScope.create { params.bind }
54
+ target_scope.merge(as.scope(self, conn)).limit(1)
55
+ }
56
+ end
57
+
58
+ binds = AssociationScope.get_bind_values(owner, reflection.chain)
59
+ sc.execute binds, klass, klass.connection
60
+ end
61
+
62
+ def find_target
63
+ if record = get_records.first
64
+ set_inverse_instance record
65
+ end
66
+ end
67
+
68
+ def replace(record)
69
+ raise NotImplementedError, "Subclasses must implement a replace(record) method"
70
+ end
71
+
72
+ def set_new_record(record)
73
+ replace(record)
74
+ end
75
+
76
+ def _create_record(attributes, raise_error = false)
77
+ record = build_record(attributes)
78
+ yield(record) if block_given?
79
+ saved = record.save
80
+ set_new_record(record)
81
+ raise RecordInvalid.new(record) if !saved && raise_error
82
+ record
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,96 @@
1
+ module ActiveRecord
2
+ # = Active Record Through Association
3
+ module Associations
4
+ module ThroughAssociation #:nodoc:
5
+
6
+ delegate :source_reflection, :through_reflection, :to => :reflection
7
+
8
+ protected
9
+
10
+ # We merge in these scopes for two reasons:
11
+ #
12
+ # 1. To get the default_scope conditions for any of the other reflections in the chain
13
+ # 2. To get the type conditions for any STI models in the chain
14
+ def target_scope
15
+ scope = super
16
+ reflection.chain.drop(1).each do |reflection|
17
+ relation = reflection.klass.all
18
+
19
+ reflection_scope = reflection.scope
20
+ if reflection_scope && reflection_scope.arity.zero?
21
+ relation.merge!(reflection_scope)
22
+ end
23
+
24
+ scope.merge!(
25
+ relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
26
+ )
27
+ end
28
+ scope
29
+ end
30
+
31
+ private
32
+
33
+ # Construct attributes for :through pointing to owner and associate. This is used by the
34
+ # methods which create and delete records on the association.
35
+ #
36
+ # We only support indirectly modifying through associations which has a belongs_to source.
37
+ # This is the "has_many :tags, through: :taggings" situation, where the join model
38
+ # typically has a belongs_to on both side. In other words, associations which could also
39
+ # be represented as has_and_belongs_to_many associations.
40
+ #
41
+ # We do not support creating/deleting records on the association where the source has
42
+ # some other type, because this opens up a whole can of worms, and in basically any
43
+ # situation it is more natural for the user to just create or modify their join records
44
+ # directly as required.
45
+ def construct_join_attributes(*records)
46
+ ensure_mutable
47
+
48
+ if source_reflection.association_primary_key(reflection.klass) == reflection.klass.primary_key
49
+ join_attributes = { source_reflection.name => records }
50
+ else
51
+ join_attributes = {
52
+ source_reflection.foreign_key =>
53
+ records.map { |record|
54
+ record.send(source_reflection.association_primary_key(reflection.klass))
55
+ }
56
+ }
57
+ end
58
+
59
+ if options[:source_type]
60
+ join_attributes[source_reflection.foreign_type] =
61
+ records.map { |record| record.class.base_class.name }
62
+ end
63
+
64
+ if records.count == 1
65
+ Hash[join_attributes.map { |k, v| [k, v.first] }]
66
+ else
67
+ join_attributes
68
+ end
69
+ end
70
+
71
+ # Note: this does not capture all cases, for example it would be crazy to try to
72
+ # properly support stale-checking for nested associations.
73
+ def stale_state
74
+ if through_reflection.belongs_to?
75
+ owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
76
+ end
77
+ end
78
+
79
+ def foreign_key_present?
80
+ through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
81
+ end
82
+
83
+ def ensure_mutable
84
+ unless source_reflection.belongs_to?
85
+ raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
86
+ end
87
+ end
88
+
89
+ def ensure_not_nested
90
+ if reflection.nested?
91
+ raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,149 @@
1
+ module ActiveRecord
2
+ class Attribute # :nodoc:
3
+ class << self
4
+ def from_database(name, value, type)
5
+ FromDatabase.new(name, value, type)
6
+ end
7
+
8
+ def from_user(name, value, type)
9
+ FromUser.new(name, value, type)
10
+ end
11
+
12
+ def with_cast_value(name, value, type)
13
+ WithCastValue.new(name, value, type)
14
+ end
15
+
16
+ def null(name)
17
+ Null.new(name)
18
+ end
19
+
20
+ def uninitialized(name, type)
21
+ Uninitialized.new(name, type)
22
+ end
23
+ end
24
+
25
+ attr_reader :name, :value_before_type_cast, :type
26
+
27
+ # This method should not be called directly.
28
+ # Use #from_database or #from_user
29
+ def initialize(name, value_before_type_cast, type)
30
+ @name = name
31
+ @value_before_type_cast = value_before_type_cast
32
+ @type = type
33
+ end
34
+
35
+ def value
36
+ # `defined?` is cheaper than `||=` when we get back falsy values
37
+ @value = original_value unless defined?(@value)
38
+ @value
39
+ end
40
+
41
+ def original_value
42
+ type_cast(value_before_type_cast)
43
+ end
44
+
45
+ def value_for_database
46
+ type.type_cast_for_database(value)
47
+ end
48
+
49
+ def changed_from?(old_value)
50
+ type.changed?(old_value, value, value_before_type_cast)
51
+ end
52
+
53
+ def changed_in_place_from?(old_value)
54
+ type.changed_in_place?(old_value, value)
55
+ end
56
+
57
+ def with_value_from_user(value)
58
+ self.class.from_user(name, value, type)
59
+ end
60
+
61
+ def with_value_from_database(value)
62
+ self.class.from_database(name, value, type)
63
+ end
64
+
65
+ def with_cast_value(value)
66
+ self.class.with_cast_value(name, value, type)
67
+ end
68
+
69
+ def type_cast(*)
70
+ raise NotImplementedError
71
+ end
72
+
73
+ def initialized?
74
+ true
75
+ end
76
+
77
+ def ==(other)
78
+ self.class == other.class &&
79
+ name == other.name &&
80
+ value_before_type_cast == other.value_before_type_cast &&
81
+ type == other.type
82
+ end
83
+
84
+ protected
85
+
86
+ def initialize_dup(other)
87
+ if defined?(@value) && @value.duplicable?
88
+ @value = @value.dup
89
+ end
90
+ end
91
+
92
+ class FromDatabase < Attribute # :nodoc:
93
+ def type_cast(value)
94
+ type.type_cast_from_database(value)
95
+ end
96
+ end
97
+
98
+ class FromUser < Attribute # :nodoc:
99
+ def type_cast(value)
100
+ type.type_cast_from_user(value)
101
+ end
102
+ end
103
+
104
+ class WithCastValue < Attribute # :nodoc:
105
+ def type_cast(value)
106
+ value
107
+ end
108
+
109
+ def changed_in_place_from?(old_value)
110
+ false
111
+ end
112
+ end
113
+
114
+ class Null < Attribute # :nodoc:
115
+ def initialize(name)
116
+ super(name, nil, Type::Value.new)
117
+ end
118
+
119
+ def value
120
+ nil
121
+ end
122
+
123
+ def with_value_from_database(value)
124
+ raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
125
+ end
126
+ alias_method :with_value_from_user, :with_value_from_database
127
+ end
128
+
129
+ class Uninitialized < Attribute # :nodoc:
130
+ def initialize(name, type)
131
+ super(name, nil, type)
132
+ end
133
+
134
+ def value
135
+ if block_given?
136
+ yield name
137
+ end
138
+ end
139
+
140
+ def value_for_database
141
+ end
142
+
143
+ def initialized?
144
+ false
145
+ end
146
+ end
147
+ private_constant :FromDatabase, :FromUser, :Null, :Uninitialized
148
+ end
149
+ end