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,16 @@
1
+ #FIXME Remove if ArJdbcMysql will give.
2
+ module ArJdbcMySQL #:nodoc:
3
+ class Error < StandardError #:nodoc:
4
+ attr_accessor :error_number, :sql_state
5
+
6
+ def initialize msg
7
+ super
8
+ @error_number = nil
9
+ @sql_state = nil
10
+ end
11
+
12
+ # Mysql gem compatibility
13
+ alias_method :errno, :error_number
14
+ alias_method :error, :message
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveRecord
2
+ module ReadonlyAttributes
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :_attr_readonly, instance_accessor: false
7
+ self._attr_readonly = []
8
+ end
9
+
10
+ module ClassMethods
11
+ # Attributes listed as readonly will be used to create a new record but update operations will
12
+ # ignore these fields.
13
+ def attr_readonly(*attributes)
14
+ self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
15
+ end
16
+
17
+ # Returns an array of all the attributes that have been specified as readonly.
18
+ def readonly_attributes
19
+ self._attr_readonly
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,881 @@
1
+ require 'thread'
2
+ require 'active_support/core_ext/string/filters'
3
+
4
+ module ActiveRecord
5
+ # = Active Record Reflection
6
+ module Reflection # :nodoc:
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :_reflections
11
+ class_attribute :aggregate_reflections
12
+ self._reflections = {}
13
+ self.aggregate_reflections = {}
14
+ end
15
+
16
+ def self.create(macro, name, scope, options, ar)
17
+ klass = case macro
18
+ when :composed_of
19
+ AggregateReflection
20
+ when :has_many
21
+ HasManyReflection
22
+ when :has_one
23
+ HasOneReflection
24
+ when :belongs_to
25
+ BelongsToReflection
26
+ else
27
+ raise "Unsupported Macro: #{macro}"
28
+ end
29
+
30
+ reflection = klass.new(name, scope, options, ar)
31
+ options[:through] ? ThroughReflection.new(reflection) : reflection
32
+ end
33
+
34
+ def self.add_reflection(ar, name, reflection)
35
+ ar._reflections = ar._reflections.merge(name.to_s => reflection)
36
+ end
37
+
38
+ def self.add_aggregate_reflection(ar, name, reflection)
39
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
40
+ end
41
+
42
+ # \Reflection enables interrogating of Active Record classes and objects
43
+ # about their associations and aggregations. This information can,
44
+ # for example, be used in a form builder that takes an Active Record object
45
+ # and creates input fields for all of the attributes depending on their type
46
+ # and displays the associations to other objects.
47
+ #
48
+ # MacroReflection class has info for AggregateReflection and AssociationReflection
49
+ # classes.
50
+ module ClassMethods
51
+ # Returns an array of AggregateReflection objects for all the aggregations in the class.
52
+ def reflect_on_all_aggregations
53
+ aggregate_reflections.values
54
+ end
55
+
56
+ # Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
57
+ #
58
+ # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
59
+ #
60
+ def reflect_on_aggregation(aggregation)
61
+ aggregate_reflections[aggregation.to_s]
62
+ end
63
+
64
+ # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
65
+ #
66
+ # Account.reflections # => {"balance" => AggregateReflection}
67
+ #
68
+ # @api public
69
+ def reflections
70
+ ref = {}
71
+ _reflections.each do |name, reflection|
72
+ parent_name, parent_reflection = reflection.parent_reflection
73
+ if parent_name
74
+ ref[parent_name] = parent_reflection
75
+ else
76
+ ref[name] = reflection
77
+ end
78
+ end
79
+ ref
80
+ end
81
+
82
+ # Returns an array of AssociationReflection objects for all the
83
+ # associations in the class. If you only want to reflect on a certain
84
+ # association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
85
+ # <tt>:belongs_to</tt>) as the first parameter.
86
+ #
87
+ # Example:
88
+ #
89
+ # Account.reflect_on_all_associations # returns an array of all associations
90
+ # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
91
+ #
92
+ # @api public
93
+ def reflect_on_all_associations(macro = nil)
94
+ association_reflections = reflections.values
95
+ macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
96
+ end
97
+
98
+ # Returns the AssociationReflection object for the +association+ (use the symbol).
99
+ #
100
+ # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
101
+ # Invoice.reflect_on_association(:line_items).macro # returns :has_many
102
+ #
103
+ # @api public
104
+ def reflect_on_association(association)
105
+ reflections[association.to_s]
106
+ end
107
+
108
+ # @api private
109
+ def _reflect_on_association(association) #:nodoc:
110
+ _reflections[association.to_s]
111
+ end
112
+
113
+ # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
114
+ #
115
+ # @api public
116
+ def reflect_on_all_autosave_associations
117
+ reflections.values.select { |reflection| reflection.options[:autosave] }
118
+ end
119
+ end
120
+
121
+ # Holds all the methods that are shared between MacroReflection, AssociationReflection
122
+ # and ThroughReflection
123
+ class AbstractReflection # :nodoc:
124
+ def table_name
125
+ klass.table_name
126
+ end
127
+
128
+ # Returns a new, unsaved instance of the associated class. +attributes+ will
129
+ # be passed to the class's constructor.
130
+ def build_association(attributes, &block)
131
+ klass.new(attributes, &block)
132
+ end
133
+
134
+ def quoted_table_name
135
+ klass.quoted_table_name
136
+ end
137
+
138
+ def primary_key_type
139
+ klass.type_for_attribute(klass.primary_key)
140
+ end
141
+
142
+ # Returns the class name for the macro.
143
+ #
144
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
145
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
146
+ def class_name
147
+ @class_name ||= (options[:class_name] || derive_class_name).to_s
148
+ end
149
+
150
+ JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
151
+
152
+ def join_keys(assoc_klass)
153
+ JoinKeys.new(foreign_key, active_record_primary_key)
154
+ end
155
+
156
+ def source_macro
157
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
158
+ ActiveRecord::Base.source_macro is deprecated and will be removed
159
+ without replacement.
160
+ MSG
161
+
162
+ macro
163
+ end
164
+ end
165
+ # Base class for AggregateReflection and AssociationReflection. Objects of
166
+ # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
167
+ #
168
+ # MacroReflection
169
+ # AssociationReflection
170
+ # AggregateReflection
171
+ # HasManyReflection
172
+ # HasOneReflection
173
+ # BelongsToReflection
174
+ # ThroughReflection
175
+ class MacroReflection < AbstractReflection
176
+ # Returns the name of the macro.
177
+ #
178
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
179
+ # <tt>has_many :clients</tt> returns <tt>:clients</tt>
180
+ attr_reader :name
181
+
182
+ attr_reader :scope
183
+
184
+ # Returns the hash of options used for the macro.
185
+ #
186
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
187
+ # <tt>has_many :clients</tt> returns <tt>{}</tt>
188
+ attr_reader :options
189
+
190
+ attr_reader :active_record
191
+
192
+ attr_reader :plural_name # :nodoc:
193
+
194
+ def initialize(name, scope, options, active_record)
195
+ @name = name
196
+ @scope = scope
197
+ @options = options
198
+ @active_record = active_record
199
+ @klass = options[:class]
200
+ @plural_name = active_record.pluralize_table_names ?
201
+ name.to_s.pluralize : name.to_s
202
+ end
203
+
204
+ def autosave=(autosave)
205
+ @automatic_inverse_of = false
206
+ @options[:autosave] = autosave
207
+ _, parent_reflection = self.parent_reflection
208
+ if parent_reflection
209
+ parent_reflection.autosave = autosave
210
+ end
211
+ end
212
+
213
+ # Returns the class for the macro.
214
+ #
215
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
216
+ # <tt>has_many :clients</tt> returns the Client class
217
+ def klass
218
+ @klass ||= compute_class(class_name)
219
+ end
220
+
221
+ def compute_class(name)
222
+ name.constantize
223
+ end
224
+
225
+ # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
226
+ # and +other_aggregation+ has an options hash assigned to it.
227
+ def ==(other_aggregation)
228
+ super ||
229
+ other_aggregation.kind_of?(self.class) &&
230
+ name == other_aggregation.name &&
231
+ !other_aggregation.options.nil? &&
232
+ active_record == other_aggregation.active_record
233
+ end
234
+
235
+ private
236
+ def derive_class_name
237
+ name.to_s.camelize
238
+ end
239
+ end
240
+
241
+
242
+ # Holds all the meta-data about an aggregation as it was specified in the
243
+ # Active Record class.
244
+ class AggregateReflection < MacroReflection #:nodoc:
245
+ def mapping
246
+ mapping = options[:mapping] || [name, name]
247
+ mapping.first.is_a?(Array) ? mapping : [mapping]
248
+ end
249
+ end
250
+
251
+ # Holds all the meta-data about an association as it was specified in the
252
+ # Active Record class.
253
+ class AssociationReflection < MacroReflection #:nodoc:
254
+ # Returns the target association's class.
255
+ #
256
+ # class Author < ActiveRecord::Base
257
+ # has_many :books
258
+ # end
259
+ #
260
+ # Author.reflect_on_association(:books).klass
261
+ # # => Book
262
+ #
263
+ # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
264
+ # a new association object. Use +build_association+ or +create_association+
265
+ # instead. This allows plugins to hook into association object creation.
266
+ def klass
267
+ @klass ||= compute_class(class_name)
268
+ end
269
+
270
+ def compute_class(name)
271
+ active_record.send(:compute_type, name)
272
+ end
273
+
274
+ attr_reader :type, :foreign_type
275
+ attr_accessor :parent_reflection # [:name, Reflection]
276
+
277
+ def initialize(name, scope, options, active_record)
278
+ super
279
+ @automatic_inverse_of = nil
280
+ @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
281
+ @foreign_type = options[:foreign_type] || "#{name}_type"
282
+ @constructable = calculate_constructable(macro, options)
283
+ @association_scope_cache = {}
284
+ @scope_lock = Mutex.new
285
+ end
286
+
287
+ def association_scope_cache(conn, owner)
288
+ key = conn.prepared_statements
289
+ if polymorphic?
290
+ key = [key, owner._read_attribute(@foreign_type)]
291
+ end
292
+ @association_scope_cache[key] ||= @scope_lock.synchronize {
293
+ @association_scope_cache[key] ||= yield
294
+ }
295
+ end
296
+
297
+ def constructable? # :nodoc:
298
+ @constructable
299
+ end
300
+
301
+ def join_table
302
+ @join_table ||= options[:join_table] || derive_join_table
303
+ end
304
+
305
+ def foreign_key
306
+ @foreign_key ||= options[:foreign_key] || derive_foreign_key
307
+ end
308
+
309
+ def association_foreign_key
310
+ @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
311
+ end
312
+
313
+ # klass option is necessary to support loading polymorphic associations
314
+ def association_primary_key(klass = nil)
315
+ options[:primary_key] || primary_key(klass || self.klass)
316
+ end
317
+
318
+ def active_record_primary_key
319
+ @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
320
+ end
321
+
322
+ def counter_cache_column
323
+ if options[:counter_cache] == true
324
+ "#{active_record.name.demodulize.underscore.pluralize}_count"
325
+ elsif options[:counter_cache]
326
+ options[:counter_cache].to_s
327
+ end
328
+ end
329
+
330
+ def check_validity!
331
+ check_validity_of_inverse!
332
+ end
333
+
334
+ def check_validity_of_inverse!
335
+ unless polymorphic?
336
+ if has_inverse? && inverse_of.nil?
337
+ raise InverseOfAssociationNotFoundError.new(self)
338
+ end
339
+ end
340
+ end
341
+
342
+ def check_preloadable!
343
+ return unless scope
344
+
345
+ if scope.arity > 0
346
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
347
+ The association scope '#{name}' is instance dependent (the scope
348
+ block takes an argument). Preloading happens before the individual
349
+ instances are created. This means that there is no instance being
350
+ passed to the association scope. This will most likely result in
351
+ broken or incorrect behavior. Joining, Preloading and eager loading
352
+ of these associations is deprecated and will be removed in the future.
353
+ MSG
354
+ end
355
+ end
356
+ alias :check_eager_loadable! :check_preloadable!
357
+
358
+ def join_id_for(owner) # :nodoc:
359
+ owner[active_record_primary_key]
360
+ end
361
+
362
+ def through_reflection
363
+ nil
364
+ end
365
+
366
+ def source_reflection
367
+ self
368
+ end
369
+
370
+ # A chain of reflections from this one back to the owner. For more see the explanation in
371
+ # ThroughReflection.
372
+ def chain
373
+ [self]
374
+ end
375
+
376
+ def nested?
377
+ false
378
+ end
379
+
380
+ # An array of arrays of scopes. Each item in the outside array corresponds to a reflection
381
+ # in the #chain.
382
+ def scope_chain
383
+ scope ? [[scope]] : [[]]
384
+ end
385
+
386
+ def has_inverse?
387
+ inverse_name
388
+ end
389
+
390
+ def inverse_of
391
+ return unless inverse_name
392
+
393
+ @inverse_of ||= klass._reflect_on_association inverse_name
394
+ end
395
+
396
+ def polymorphic_inverse_of(associated_class)
397
+ if has_inverse?
398
+ if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
399
+ inverse_relationship
400
+ else
401
+ raise InverseOfAssociationNotFoundError.new(self, associated_class)
402
+ end
403
+ end
404
+ end
405
+
406
+ # Returns the macro type.
407
+ #
408
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
409
+ def macro; raise NotImplementedError; end
410
+
411
+ # Returns whether or not this association reflection is for a collection
412
+ # association. Returns +true+ if the +macro+ is either +has_many+ or
413
+ # +has_and_belongs_to_many+, +false+ otherwise.
414
+ def collection?
415
+ false
416
+ end
417
+
418
+ # Returns whether or not the association should be validated as part of
419
+ # the parent's validation.
420
+ #
421
+ # Unless you explicitly disable validation with
422
+ # <tt>validate: false</tt>, validation will take place when:
423
+ #
424
+ # * you explicitly enable validation; <tt>validate: true</tt>
425
+ # * you use autosave; <tt>autosave: true</tt>
426
+ # * the association is a +has_many+ association
427
+ def validate?
428
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
429
+ end
430
+
431
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
432
+ def belongs_to?; false; end
433
+
434
+ # Returns +true+ if +self+ is a +has_one+ reflection.
435
+ def has_one?; false; end
436
+
437
+ def association_class
438
+ case macro
439
+ when :belongs_to
440
+ if polymorphic?
441
+ Associations::BelongsToPolymorphicAssociation
442
+ else
443
+ Associations::BelongsToAssociation
444
+ end
445
+ when :has_many
446
+ if options[:through]
447
+ Associations::HasManyThroughAssociation
448
+ else
449
+ Associations::HasManyAssociation
450
+ end
451
+ when :has_one
452
+ if options[:through]
453
+ Associations::HasOneThroughAssociation
454
+ else
455
+ Associations::HasOneAssociation
456
+ end
457
+ end
458
+ end
459
+
460
+ def polymorphic?
461
+ options[:polymorphic]
462
+ end
463
+
464
+ VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
465
+ INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
466
+
467
+ protected
468
+
469
+ def actual_source_reflection # FIXME: this is a horrible name
470
+ self
471
+ end
472
+
473
+ private
474
+
475
+ def calculate_constructable(macro, options)
476
+ case macro
477
+ when :belongs_to
478
+ !polymorphic?
479
+ when :has_one
480
+ !options[:through]
481
+ else
482
+ true
483
+ end
484
+ end
485
+
486
+ # Attempts to find the inverse association name automatically.
487
+ # If it cannot find a suitable inverse association name, it returns
488
+ # nil.
489
+ def inverse_name
490
+ options.fetch(:inverse_of) do
491
+ if @automatic_inverse_of == false
492
+ nil
493
+ else
494
+ @automatic_inverse_of ||= automatic_inverse_of
495
+ end
496
+ end
497
+ end
498
+
499
+ # returns either nil or the inverse association name that it finds.
500
+ def automatic_inverse_of
501
+ if can_find_inverse_of_automatically?(self)
502
+ inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name).to_sym
503
+
504
+ begin
505
+ reflection = klass._reflect_on_association(inverse_name)
506
+ rescue NameError
507
+ # Give up: we couldn't compute the klass type so we won't be able
508
+ # to find any associations either.
509
+ reflection = false
510
+ end
511
+
512
+ if valid_inverse_reflection?(reflection)
513
+ return inverse_name
514
+ end
515
+ end
516
+
517
+ false
518
+ end
519
+
520
+ # Checks if the inverse reflection that is returned from the
521
+ # +automatic_inverse_of+ method is a valid reflection. We must
522
+ # make sure that the reflection's active_record name matches up
523
+ # with the current reflection's klass name.
524
+ #
525
+ # Note: klass will always be valid because when there's a NameError
526
+ # from calling +klass+, +reflection+ will already be set to false.
527
+ def valid_inverse_reflection?(reflection)
528
+ reflection &&
529
+ klass.name == reflection.active_record.name &&
530
+ can_find_inverse_of_automatically?(reflection)
531
+ end
532
+
533
+ # Checks to see if the reflection doesn't have any options that prevent
534
+ # us from being able to guess the inverse automatically. First, the
535
+ # <tt>inverse_of</tt> option cannot be set to false. Second, we must
536
+ # have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
537
+ # Third, we must not have options such as <tt>:polymorphic</tt> or
538
+ # <tt>:foreign_key</tt> which prevent us from correctly guessing the
539
+ # inverse association.
540
+ #
541
+ # Anything with a scope can additionally ruin our attempt at finding an
542
+ # inverse, so we exclude reflections with scopes.
543
+ def can_find_inverse_of_automatically?(reflection)
544
+ reflection.options[:inverse_of] != false &&
545
+ VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
546
+ !INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] } &&
547
+ !reflection.scope
548
+ end
549
+
550
+ def derive_class_name
551
+ class_name = name.to_s
552
+ class_name = class_name.singularize if collection?
553
+ class_name.camelize
554
+ end
555
+
556
+ def derive_foreign_key
557
+ if belongs_to?
558
+ "#{name}_id"
559
+ elsif options[:as]
560
+ "#{options[:as]}_id"
561
+ else
562
+ active_record.name.foreign_key
563
+ end
564
+ end
565
+
566
+ def derive_join_table
567
+ ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
568
+ end
569
+
570
+ def primary_key(klass)
571
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
572
+ end
573
+ end
574
+
575
+ class HasManyReflection < AssociationReflection # :nodoc:
576
+ def initialize(name, scope, options, active_record)
577
+ super(name, scope, options, active_record)
578
+ end
579
+
580
+ def macro; :has_many; end
581
+
582
+ def collection?; true; end
583
+ end
584
+
585
+ class HasOneReflection < AssociationReflection # :nodoc:
586
+ def initialize(name, scope, options, active_record)
587
+ super(name, scope, options, active_record)
588
+ end
589
+
590
+ def macro; :has_one; end
591
+
592
+ def has_one?; true; end
593
+ end
594
+
595
+ class BelongsToReflection < AssociationReflection # :nodoc:
596
+ def initialize(name, scope, options, active_record)
597
+ super(name, scope, options, active_record)
598
+ end
599
+
600
+ def macro; :belongs_to; end
601
+
602
+ def belongs_to?; true; end
603
+
604
+ def join_keys(assoc_klass)
605
+ key = polymorphic? ? association_primary_key(assoc_klass) : association_primary_key
606
+ JoinKeys.new(key, foreign_key)
607
+ end
608
+
609
+ def join_id_for(owner) # :nodoc:
610
+ owner[foreign_key]
611
+ end
612
+ end
613
+
614
+ class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
615
+ def initialize(name, scope, options, active_record)
616
+ super
617
+ end
618
+
619
+ def macro; :has_and_belongs_to_many; end
620
+
621
+ def collection?
622
+ true
623
+ end
624
+ end
625
+
626
+ # Holds all the meta-data about a :through association as it was specified
627
+ # in the Active Record class.
628
+ class ThroughReflection < AbstractReflection #:nodoc:
629
+ attr_reader :delegate_reflection
630
+ delegate :foreign_key, :foreign_type, :association_foreign_key,
631
+ :active_record_primary_key, :type, :to => :source_reflection
632
+
633
+ def initialize(delegate_reflection)
634
+ @delegate_reflection = delegate_reflection
635
+ @klass = delegate_reflection.options[:class]
636
+ @source_reflection_name = delegate_reflection.options[:source]
637
+ end
638
+
639
+ def klass
640
+ @klass ||= delegate_reflection.compute_class(class_name)
641
+ end
642
+
643
+ # Returns the source of the through reflection. It checks both a singularized
644
+ # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
645
+ #
646
+ # class Post < ActiveRecord::Base
647
+ # has_many :taggings
648
+ # has_many :tags, through: :taggings
649
+ # end
650
+ #
651
+ # class Tagging < ActiveRecord::Base
652
+ # belongs_to :post
653
+ # belongs_to :tag
654
+ # end
655
+ #
656
+ # tags_reflection = Post.reflect_on_association(:tags)
657
+ # tags_reflection.source_reflection
658
+ # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
659
+ #
660
+ def source_reflection
661
+ through_reflection.klass._reflect_on_association(source_reflection_name)
662
+ end
663
+
664
+ # Returns the AssociationReflection object specified in the <tt>:through</tt> option
665
+ # of a HasManyThrough or HasOneThrough association.
666
+ #
667
+ # class Post < ActiveRecord::Base
668
+ # has_many :taggings
669
+ # has_many :tags, through: :taggings
670
+ # end
671
+ #
672
+ # tags_reflection = Post.reflect_on_association(:tags)
673
+ # tags_reflection.through_reflection
674
+ # # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
675
+ #
676
+ def through_reflection
677
+ active_record._reflect_on_association(options[:through])
678
+ end
679
+
680
+ # Returns an array of reflections which are involved in this association. Each item in the
681
+ # array corresponds to a table which will be part of the query for this association.
682
+ #
683
+ # The chain is built by recursively calling #chain on the source reflection and the through
684
+ # reflection. The base case for the recursion is a normal association, which just returns
685
+ # [self] as its #chain.
686
+ #
687
+ # class Post < ActiveRecord::Base
688
+ # has_many :taggings
689
+ # has_many :tags, through: :taggings
690
+ # end
691
+ #
692
+ # tags_reflection = Post.reflect_on_association(:tags)
693
+ # tags_reflection.chain
694
+ # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
695
+ # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
696
+ #
697
+ def chain
698
+ @chain ||= begin
699
+ a = source_reflection.chain
700
+ b = through_reflection.chain
701
+ chain = a + b
702
+ chain[0] = self # Use self so we don't lose the information from :source_type
703
+ chain
704
+ end
705
+ end
706
+
707
+ # Consider the following example:
708
+ #
709
+ # class Person
710
+ # has_many :articles
711
+ # has_many :comment_tags, through: :articles
712
+ # end
713
+ #
714
+ # class Article
715
+ # has_many :comments
716
+ # has_many :comment_tags, through: :comments, source: :tags
717
+ # end
718
+ #
719
+ # class Comment
720
+ # has_many :tags
721
+ # end
722
+ #
723
+ # There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
724
+ # but only Comment.tags will be represented in the #chain. So this method creates an array
725
+ # of scopes corresponding to the chain.
726
+ def scope_chain
727
+ @scope_chain ||= begin
728
+ scope_chain = source_reflection.scope_chain.map(&:dup)
729
+
730
+ # Add to it the scope from this reflection (if any)
731
+ scope_chain.first << scope if scope
732
+
733
+ through_scope_chain = through_reflection.scope_chain.map(&:dup)
734
+
735
+ if options[:source_type]
736
+ type = foreign_type
737
+ source_type = options[:source_type]
738
+ through_scope_chain.first << lambda { |object|
739
+ where(type => source_type)
740
+ }
741
+ end
742
+
743
+ # Recursively fill out the rest of the array from the through reflection
744
+ scope_chain + through_scope_chain
745
+ end
746
+ end
747
+
748
+ def join_keys(assoc_klass)
749
+ source_reflection.join_keys(assoc_klass)
750
+ end
751
+
752
+ # The macro used by the source association
753
+ def source_macro
754
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
755
+ ActiveRecord::Base.source_macro is deprecated and will be removed
756
+ without replacement.
757
+ MSG
758
+
759
+ source_reflection.source_macro
760
+ end
761
+
762
+ # A through association is nested if there would be more than one join table
763
+ def nested?
764
+ chain.length > 2
765
+ end
766
+
767
+ # We want to use the klass from this reflection, rather than just delegate straight to
768
+ # the source_reflection, because the source_reflection may be polymorphic. We still
769
+ # need to respect the source_reflection's :primary_key option, though.
770
+ def association_primary_key(klass = nil)
771
+ # Get the "actual" source reflection if the immediate source reflection has a
772
+ # source reflection itself
773
+ actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
774
+ end
775
+
776
+ # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
777
+ #
778
+ # class Post < ActiveRecord::Base
779
+ # has_many :taggings
780
+ # has_many :tags, through: :taggings
781
+ # end
782
+ #
783
+ # tags_reflection = Post.reflect_on_association(:tags)
784
+ # tags_reflection.source_reflection_names
785
+ # # => [:tag, :tags]
786
+ #
787
+ def source_reflection_names
788
+ options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
789
+ end
790
+
791
+ def source_reflection_name # :nodoc:
792
+ return @source_reflection_name if @source_reflection_name
793
+
794
+ names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
795
+ names = names.find_all { |n|
796
+ through_reflection.klass._reflect_on_association(n)
797
+ }
798
+
799
+ if names.length > 1
800
+ example_options = options.dup
801
+ example_options[:source] = source_reflection_names.first
802
+ ActiveSupport::Deprecation.warn \
803
+ "Ambiguous source reflection for through association. Please " \
804
+ "specify a :source directive on your declaration like:\n" \
805
+ "\n" \
806
+ " class #{active_record.name} < ActiveRecord::Base\n" \
807
+ " #{macro} :#{name}, #{example_options}\n" \
808
+ " end"
809
+ end
810
+
811
+ @source_reflection_name = names.first
812
+ end
813
+
814
+ def source_options
815
+ source_reflection.options
816
+ end
817
+
818
+ def through_options
819
+ through_reflection.options
820
+ end
821
+
822
+ def join_id_for(owner) # :nodoc:
823
+ source_reflection.join_id_for(owner)
824
+ end
825
+
826
+ def check_validity!
827
+ if through_reflection.nil?
828
+ raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
829
+ end
830
+
831
+ if through_reflection.polymorphic?
832
+ if has_one?
833
+ raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
834
+ else
835
+ raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
836
+ end
837
+ end
838
+
839
+ if source_reflection.nil?
840
+ raise HasManyThroughSourceAssociationNotFoundError.new(self)
841
+ end
842
+
843
+ if options[:source_type] && !source_reflection.polymorphic?
844
+ raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
845
+ end
846
+
847
+ if source_reflection.polymorphic? && options[:source_type].nil?
848
+ raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
849
+ end
850
+
851
+ if has_one? && through_reflection.collection?
852
+ raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
853
+ end
854
+
855
+ check_validity_of_inverse!
856
+ end
857
+
858
+ protected
859
+
860
+ def actual_source_reflection # FIXME: this is a horrible name
861
+ source_reflection.send(:actual_source_reflection)
862
+ end
863
+
864
+ def primary_key(klass)
865
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
866
+ end
867
+
868
+ private
869
+ def derive_class_name
870
+ # get the class_name of the belongs_to association of the through reflection
871
+ options[:source_type] || source_reflection.class_name
872
+ end
873
+
874
+ delegate_methods = AssociationReflection.public_instance_methods -
875
+ public_instance_methods
876
+
877
+ delegate(*delegate_methods, to: :delegate_reflection)
878
+
879
+ end
880
+ end
881
+ end