activerecord 3.0.0 → 4.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2102 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +35 -44
- data/examples/performance.rb +110 -100
- data/lib/active_record/aggregations.rb +59 -75
- data/lib/active_record/associations/alias_tracker.rb +76 -0
- data/lib/active_record/associations/association.rb +248 -0
- data/lib/active_record/associations/association_scope.rb +135 -0
- data/lib/active_record/associations/belongs_to_association.rb +60 -59
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
- data/lib/active_record/associations/builder/association.rb +108 -0
- data/lib/active_record/associations/builder/belongs_to.rb +98 -0
- data/lib/active_record/associations/builder/collection_association.rb +89 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +25 -0
- data/lib/active_record/associations/builder/singular_association.rb +32 -0
- data/lib/active_record/associations/collection_association.rb +608 -0
- data/lib/active_record/associations/collection_proxy.rb +986 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
- data/lib/active_record/associations/has_many_association.rb +83 -76
- data/lib/active_record/associations/has_many_through_association.rb +147 -66
- data/lib/active_record/associations/has_one_association.rb +67 -108
- data/lib/active_record/associations/has_one_through_association.rb +21 -25
- data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
- data/lib/active_record/associations/join_dependency.rb +235 -0
- data/lib/active_record/associations/join_helper.rb +45 -0
- data/lib/active_record/associations/preloader/association.rb +121 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +63 -0
- data/lib/active_record/associations/preloader.rb +178 -0
- data/lib/active_record/associations/singular_association.rb +64 -0
- data/lib/active_record/associations/through_association.rb +87 -0
- data/lib/active_record/associations.rb +512 -1224
- data/lib/active_record/attribute_assignment.rb +201 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
- data/lib/active_record/attribute_methods/dirty.rb +51 -28
- data/lib/active_record/attribute_methods/primary_key.rb +94 -22
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +63 -72
- data/lib/active_record/attribute_methods/serialization.rb +162 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
- data/lib/active_record/attribute_methods/write.rb +39 -13
- data/lib/active_record/attribute_methods.rb +362 -29
- data/lib/active_record/autosave_association.rb +132 -75
- data/lib/active_record/base.rb +83 -1627
- data/lib/active_record/callbacks.rb +69 -47
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
- data/lib/active_record/connection_adapters/column.rb +318 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
- data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +463 -0
- data/lib/active_record/counter_cache.rb +108 -101
- data/lib/active_record/dynamic_matchers.rb +131 -0
- data/lib/active_record/errors.rb +54 -13
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +703 -785
- data/lib/active_record/inheritance.rb +200 -0
- data/lib/active_record/integration.rb +60 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +69 -60
- data/lib/active_record/locking/pessimistic.rb +34 -12
- data/lib/active_record/log_subscriber.rb +40 -6
- data/lib/active_record/migration/command_recorder.rb +164 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +614 -216
- data/lib/active_record/model_schema.rb +345 -0
- data/lib/active_record/nested_attributes.rb +248 -119
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +275 -57
- data/lib/active_record/query_cache.rb +29 -9
- data/lib/active_record/querying.rb +62 -0
- data/lib/active_record/railtie.rb +135 -21
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +17 -5
- data/lib/active_record/railties/databases.rake +249 -359
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +30 -0
- data/lib/active_record/reflection.rb +283 -103
- data/lib/active_record/relation/batches.rb +38 -34
- data/lib/active_record/relation/calculations.rb +252 -139
- data/lib/active_record/relation/delegation.rb +125 -0
- data/lib/active_record/relation/finder_methods.rb +182 -188
- data/lib/active_record/relation/merger.rb +161 -0
- data/lib/active_record/relation/predicate_builder.rb +86 -21
- data/lib/active_record/relation/query_methods.rb +917 -134
- data/lib/active_record/relation/spawn_methods.rb +53 -92
- data/lib/active_record/relation.rb +405 -143
- data/lib/active_record/result.rb +67 -0
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +168 -0
- data/lib/active_record/schema.rb +20 -14
- data/lib/active_record/schema_dumper.rb +55 -46
- data/lib/active_record/schema_migration.rb +39 -0
- data/lib/active_record/scoping/default.rb +146 -0
- data/lib/active_record/scoping/named.rb +175 -0
- data/lib/active_record/scoping.rb +82 -0
- data/lib/active_record/serialization.rb +8 -46
- data/lib/active_record/serializers/xml_serializer.rb +21 -68
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +156 -0
- data/lib/active_record/tasks/database_tasks.rb +203 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +57 -28
- data/lib/active_record/timestamp.rb +49 -18
- data/lib/active_record/transactions.rb +106 -63
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +25 -24
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +123 -83
- data/lib/active_record/validations.rb +29 -29
- data/lib/active_record/version.rb +7 -5
- data/lib/active_record.rb +83 -34
- data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
- data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
- data/lib/rails/generators/active_record.rb +4 -8
- metadata +163 -121
- data/CHANGELOG +0 -6023
- data/examples/associations.png +0 -0
- data/lib/active_record/association_preload.rb +0 -403
- data/lib/active_record/associations/association_collection.rb +0 -562
- data/lib/active_record/associations/association_proxy.rb +0 -295
- data/lib/active_record/associations/through_association_scope.rb +0 -154
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
- data/lib/active_record/dynamic_finder_match.rb +0 -53
- data/lib/active_record/dynamic_scope_match.rb +0 -32
- data/lib/active_record/named_scope.rb +0 -138
- data/lib/active_record/observer.rb +0 -140
- data/lib/active_record/session_store.rb +0 -340
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -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,30 @@
|
|
1
|
+
|
2
|
+
module ActiveRecord
|
3
|
+
module ReadonlyAttributes
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :_attr_readonly, instance_accessor: false
|
8
|
+
self._attr_readonly = []
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
# Attributes listed as readonly will be used to create a new record but update operations will
|
13
|
+
# ignore these fields.
|
14
|
+
def attr_readonly(*attributes)
|
15
|
+
self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns an array of all the attributes that have been specified as readonly.
|
19
|
+
def readonly_attributes
|
20
|
+
self._attr_readonly
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def _attr_readonly
|
25
|
+
message = "Instance level _attr_readonly method is deprecated, please use class level method."
|
26
|
+
ActiveSupport::Deprecation.warn message
|
27
|
+
defined?(@_attr_readonly) ? @_attr_readonly : self.class._attr_readonly
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -3,6 +3,11 @@ module ActiveRecord
|
|
3
3
|
module Reflection # :nodoc:
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
+
included do
|
7
|
+
class_attribute :reflections
|
8
|
+
self.reflections = {}
|
9
|
+
end
|
10
|
+
|
6
11
|
# Reflection enables to interrogate Active Record classes and objects
|
7
12
|
# about their associations and aggregations. This information can,
|
8
13
|
# for example, be used in a form builder that takes an Active Record object
|
@@ -12,31 +17,22 @@ module ActiveRecord
|
|
12
17
|
# MacroReflection class has info for AggregateReflection and AssociationReflection
|
13
18
|
# classes.
|
14
19
|
module ClassMethods
|
15
|
-
def create_reflection(macro, name, options, active_record)
|
20
|
+
def create_reflection(macro, name, scope, options, active_record)
|
16
21
|
case macro
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
|
23
|
+
klass = options[:through] ? ThroughReflection : AssociationReflection
|
24
|
+
reflection = klass.new(macro, name, scope, options, active_record)
|
25
|
+
when :composed_of
|
26
|
+
reflection = AggregateReflection.new(macro, name, scope, options, active_record)
|
22
27
|
end
|
23
|
-
write_inheritable_hash :reflections, name => reflection
|
24
|
-
reflection
|
25
|
-
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
# Invoice.reflections
|
31
|
-
# Account.reflections
|
32
|
-
#
|
33
|
-
def reflections
|
34
|
-
read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
|
29
|
+
self.reflections = self.reflections.merge(name => reflection)
|
30
|
+
reflection
|
35
31
|
end
|
36
32
|
|
37
33
|
# Returns an array of AggregateReflection objects for all the aggregations in the class.
|
38
34
|
def reflect_on_all_aggregations
|
39
|
-
reflections.values.
|
35
|
+
reflections.values.grep(AggregateReflection)
|
40
36
|
end
|
41
37
|
|
42
38
|
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
|
@@ -44,7 +40,8 @@ module ActiveRecord
|
|
44
40
|
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
45
41
|
#
|
46
42
|
def reflect_on_aggregation(aggregation)
|
47
|
-
|
43
|
+
reflection = reflections[aggregation]
|
44
|
+
reflection if reflection.is_a?(AggregateReflection)
|
48
45
|
end
|
49
46
|
|
50
47
|
# Returns an array of AssociationReflection objects for all the
|
@@ -58,7 +55,7 @@ module ActiveRecord
|
|
58
55
|
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
59
56
|
#
|
60
57
|
def reflect_on_all_associations(macro = nil)
|
61
|
-
association_reflections = reflections.values.
|
58
|
+
association_reflections = reflections.values.grep(AssociationReflection)
|
62
59
|
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
|
63
60
|
end
|
64
61
|
|
@@ -68,7 +65,8 @@ module ActiveRecord
|
|
68
65
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
69
66
|
#
|
70
67
|
def reflect_on_association(association)
|
71
|
-
|
68
|
+
reflection = reflections[association]
|
69
|
+
reflection if reflection.is_a?(AssociationReflection)
|
72
70
|
end
|
73
71
|
|
74
72
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
@@ -77,37 +75,51 @@ module ActiveRecord
|
|
77
75
|
end
|
78
76
|
end
|
79
77
|
|
80
|
-
|
81
|
-
# Abstract base class for AggregateReflection and AssociationReflection. Objects of
|
78
|
+
# Base class for AggregateReflection and AssociationReflection. Objects of
|
82
79
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
80
|
+
#
|
81
|
+
# MacroReflection
|
82
|
+
# AggregateReflection
|
83
|
+
# AssociationReflection
|
84
|
+
# ThroughReflection
|
83
85
|
class MacroReflection
|
84
|
-
attr_reader :active_record
|
85
|
-
|
86
|
-
def initialize(macro, name, options, active_record)
|
87
|
-
@macro, @name, @options, @active_record = macro, name, options, active_record
|
88
|
-
end
|
89
|
-
|
90
86
|
# Returns the name of the macro.
|
91
87
|
#
|
92
|
-
# <tt>composed_of :balance, :
|
88
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
|
93
89
|
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
94
90
|
attr_reader :name
|
95
91
|
|
96
92
|
# Returns the macro type.
|
97
93
|
#
|
98
|
-
# <tt>composed_of :balance, :
|
94
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
|
99
95
|
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
100
96
|
attr_reader :macro
|
101
97
|
|
98
|
+
attr_reader :scope
|
99
|
+
|
102
100
|
# Returns the hash of options used for the macro.
|
103
101
|
#
|
104
|
-
# <tt>composed_of :balance, :
|
102
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
|
105
103
|
# <tt>has_many :clients</tt> returns +{}+
|
106
104
|
attr_reader :options
|
107
105
|
|
106
|
+
attr_reader :active_record
|
107
|
+
|
108
|
+
attr_reader :plural_name # :nodoc:
|
109
|
+
|
110
|
+
def initialize(macro, name, scope, options, active_record)
|
111
|
+
@macro = macro
|
112
|
+
@name = name
|
113
|
+
@scope = scope
|
114
|
+
@options = options
|
115
|
+
@active_record = active_record
|
116
|
+
@plural_name = active_record.pluralize_table_names ?
|
117
|
+
name.to_s.pluralize : name.to_s
|
118
|
+
end
|
119
|
+
|
108
120
|
# Returns the class for the macro.
|
109
121
|
#
|
110
|
-
# <tt>composed_of :balance, :
|
122
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
111
123
|
# <tt>has_many :clients</tt> returns the Client class
|
112
124
|
def klass
|
113
125
|
@klass ||= class_name.constantize
|
@@ -115,20 +127,20 @@ module ActiveRecord
|
|
115
127
|
|
116
128
|
# Returns the class name for the macro.
|
117
129
|
#
|
118
|
-
# <tt>composed_of :balance, :
|
130
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
119
131
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
120
132
|
def class_name
|
121
|
-
@class_name ||= options[:class_name] || derive_class_name
|
133
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
122
134
|
end
|
123
135
|
|
124
136
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
125
137
|
# and +other_aggregation+ has an options hash assigned to it.
|
126
138
|
def ==(other_aggregation)
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
139
|
+
super ||
|
140
|
+
other_aggregation.kind_of?(self.class) &&
|
141
|
+
name == other_aggregation.name &&
|
142
|
+
other_aggregation.options &&
|
143
|
+
active_record == other_aggregation.active_record
|
132
144
|
end
|
133
145
|
|
134
146
|
private
|
@@ -141,6 +153,10 @@ module ActiveRecord
|
|
141
153
|
# Holds all the meta-data about an aggregation as it was specified in the
|
142
154
|
# Active Record class.
|
143
155
|
class AggregateReflection < MacroReflection #:nodoc:
|
156
|
+
def mapping
|
157
|
+
mapping = options[:mapping] || [name, name]
|
158
|
+
mapping.first.is_a?(Array) ? mapping : [mapping]
|
159
|
+
end
|
144
160
|
end
|
145
161
|
|
146
162
|
# Holds all the meta-data about an association as it was specified in the
|
@@ -162,32 +178,15 @@ module ActiveRecord
|
|
162
178
|
@klass ||= active_record.send(:compute_type, class_name)
|
163
179
|
end
|
164
180
|
|
165
|
-
def initialize(
|
181
|
+
def initialize(*args)
|
166
182
|
super
|
167
183
|
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
|
168
184
|
end
|
169
185
|
|
170
|
-
# Returns a new, unsaved instance of the associated class. +
|
186
|
+
# Returns a new, unsaved instance of the associated class. +attributes+ will
|
171
187
|
# be passed to the class's constructor.
|
172
|
-
def build_association(
|
173
|
-
klass.new(
|
174
|
-
end
|
175
|
-
|
176
|
-
# Creates a new instance of the associated class, and immediately saves it
|
177
|
-
# with ActiveRecord::Base#save. +options+ will be passed to the class's
|
178
|
-
# creation method. Returns the newly created object.
|
179
|
-
def create_association(*options)
|
180
|
-
klass.create(*options)
|
181
|
-
end
|
182
|
-
|
183
|
-
# Creates a new instance of the associated class, and immediately saves it
|
184
|
-
# with ActiveRecord::Base#save!. +options+ will be passed to the class's
|
185
|
-
# creation method. If the created record doesn't pass validations, then an
|
186
|
-
# exception will be raised.
|
187
|
-
#
|
188
|
-
# Returns the newly created object.
|
189
|
-
def create_association!(*options)
|
190
|
-
klass.create!(*options)
|
188
|
+
def build_association(attributes, &block)
|
189
|
+
klass.new(attributes, &block)
|
191
190
|
end
|
192
191
|
|
193
192
|
def table_name
|
@@ -198,8 +197,20 @@ module ActiveRecord
|
|
198
197
|
@quoted_table_name ||= klass.quoted_table_name
|
199
198
|
end
|
200
199
|
|
201
|
-
def
|
202
|
-
@
|
200
|
+
def join_table
|
201
|
+
@join_table ||= options[:join_table] || derive_join_table
|
202
|
+
end
|
203
|
+
|
204
|
+
def foreign_key
|
205
|
+
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
206
|
+
end
|
207
|
+
|
208
|
+
def foreign_type
|
209
|
+
@foreign_type ||= options[:foreign_type] || "#{name}_type"
|
210
|
+
end
|
211
|
+
|
212
|
+
def type
|
213
|
+
@type ||= options[:as] && "#{options[:as]}_type"
|
203
214
|
end
|
204
215
|
|
205
216
|
def primary_key_column
|
@@ -207,19 +218,28 @@ module ActiveRecord
|
|
207
218
|
end
|
208
219
|
|
209
220
|
def association_foreign_key
|
210
|
-
@association_foreign_key ||=
|
221
|
+
@association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
|
222
|
+
end
|
223
|
+
|
224
|
+
# klass option is necessary to support loading polymorphic associations
|
225
|
+
def association_primary_key(klass = nil)
|
226
|
+
options[:primary_key] || primary_key(klass || self.klass)
|
227
|
+
end
|
228
|
+
|
229
|
+
def active_record_primary_key
|
230
|
+
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
|
211
231
|
end
|
212
232
|
|
213
233
|
def counter_cache_column
|
214
234
|
if options[:counter_cache] == true
|
215
235
|
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
216
236
|
elsif options[:counter_cache]
|
217
|
-
options[:counter_cache]
|
237
|
+
options[:counter_cache].to_s
|
218
238
|
end
|
219
239
|
end
|
220
240
|
|
221
|
-
def columns(tbl_name
|
222
|
-
@columns ||= klass.connection.columns(tbl_name
|
241
|
+
def columns(tbl_name)
|
242
|
+
@columns ||= klass.connection.columns(tbl_name)
|
223
243
|
end
|
224
244
|
|
225
245
|
def reset_column_information
|
@@ -228,6 +248,10 @@ module ActiveRecord
|
|
228
248
|
|
229
249
|
def check_validity!
|
230
250
|
check_validity_of_inverse!
|
251
|
+
|
252
|
+
if has_and_belongs_to_many? && association_foreign_key == foreign_key
|
253
|
+
raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(self)
|
254
|
+
end
|
231
255
|
end
|
232
256
|
|
233
257
|
def check_validity_of_inverse!
|
@@ -239,18 +263,33 @@ module ActiveRecord
|
|
239
263
|
end
|
240
264
|
|
241
265
|
def through_reflection
|
242
|
-
|
243
|
-
end
|
244
|
-
|
245
|
-
def through_reflection_primary_key_name
|
266
|
+
nil
|
246
267
|
end
|
247
268
|
|
248
269
|
def source_reflection
|
249
270
|
nil
|
250
271
|
end
|
251
272
|
|
273
|
+
# A chain of reflections from this one back to the owner. For more see the explanation in
|
274
|
+
# ThroughReflection.
|
275
|
+
def chain
|
276
|
+
[self]
|
277
|
+
end
|
278
|
+
|
279
|
+
def nested?
|
280
|
+
false
|
281
|
+
end
|
282
|
+
|
283
|
+
# An array of arrays of scopes. Each item in the outside array corresponds to a reflection
|
284
|
+
# in the #chain.
|
285
|
+
def scope_chain
|
286
|
+
scope ? [[scope]] : [[]]
|
287
|
+
end
|
288
|
+
|
289
|
+
alias :source_macro :macro
|
290
|
+
|
252
291
|
def has_inverse?
|
253
|
-
|
292
|
+
@options[:inverse_of]
|
254
293
|
end
|
255
294
|
|
256
295
|
def inverse_of
|
@@ -280,31 +319,53 @@ module ActiveRecord
|
|
280
319
|
# the parent's validation.
|
281
320
|
#
|
282
321
|
# Unless you explicitly disable validation with
|
283
|
-
# <tt
|
322
|
+
# <tt>validate: false</tt>, validation will take place when:
|
284
323
|
#
|
285
|
-
# * you explicitly enable validation; <tt
|
286
|
-
# * you use autosave; <tt
|
324
|
+
# * you explicitly enable validation; <tt>validate: true</tt>
|
325
|
+
# * you use autosave; <tt>autosave: true</tt>
|
287
326
|
# * the association is a +has_many+ association
|
288
327
|
def validate?
|
289
328
|
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
|
290
329
|
end
|
291
330
|
|
292
|
-
def dependent_conditions(record, base_class, extra_conditions)
|
293
|
-
dependent_conditions = []
|
294
|
-
dependent_conditions << "#{primary_key_name} = #{record.send(name).send(:owner_quoted_id)}"
|
295
|
-
dependent_conditions << "#{options[:as]}_type = '#{base_class.name}'" if options[:as]
|
296
|
-
dependent_conditions << klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
|
297
|
-
dependent_conditions << extra_conditions if extra_conditions
|
298
|
-
dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
|
299
|
-
dependent_conditions = dependent_conditions.gsub('@', '\@')
|
300
|
-
dependent_conditions
|
301
|
-
end
|
302
|
-
|
303
331
|
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
304
332
|
def belongs_to?
|
305
333
|
macro == :belongs_to
|
306
334
|
end
|
307
335
|
|
336
|
+
def has_and_belongs_to_many?
|
337
|
+
macro == :has_and_belongs_to_many
|
338
|
+
end
|
339
|
+
|
340
|
+
def association_class
|
341
|
+
case macro
|
342
|
+
when :belongs_to
|
343
|
+
if options[:polymorphic]
|
344
|
+
Associations::BelongsToPolymorphicAssociation
|
345
|
+
else
|
346
|
+
Associations::BelongsToAssociation
|
347
|
+
end
|
348
|
+
when :has_and_belongs_to_many
|
349
|
+
Associations::HasAndBelongsToManyAssociation
|
350
|
+
when :has_many
|
351
|
+
if options[:through]
|
352
|
+
Associations::HasManyThroughAssociation
|
353
|
+
else
|
354
|
+
Associations::HasManyAssociation
|
355
|
+
end
|
356
|
+
when :has_one
|
357
|
+
if options[:through]
|
358
|
+
Associations::HasOneThroughAssociation
|
359
|
+
else
|
360
|
+
Associations::HasOneAssociation
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def polymorphic?
|
366
|
+
options.key? :polymorphic
|
367
|
+
end
|
368
|
+
|
308
369
|
private
|
309
370
|
def derive_class_name
|
310
371
|
class_name = name.to_s.camelize
|
@@ -312,7 +373,7 @@ module ActiveRecord
|
|
312
373
|
class_name
|
313
374
|
end
|
314
375
|
|
315
|
-
def
|
376
|
+
def derive_foreign_key
|
316
377
|
if belongs_to?
|
317
378
|
"#{name}_id"
|
318
379
|
elsif options[:as]
|
@@ -321,19 +382,40 @@ module ActiveRecord
|
|
321
382
|
active_record.name.foreign_key
|
322
383
|
end
|
323
384
|
end
|
385
|
+
|
386
|
+
def derive_join_table
|
387
|
+
[active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
|
388
|
+
end
|
389
|
+
|
390
|
+
def primary_key(klass)
|
391
|
+
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
392
|
+
end
|
324
393
|
end
|
325
394
|
|
326
395
|
# Holds all the meta-data about a :through association as it was specified
|
327
396
|
# in the Active Record class.
|
328
397
|
class ThroughReflection < AssociationReflection #:nodoc:
|
329
|
-
|
398
|
+
delegate :foreign_key, :foreign_type, :association_foreign_key,
|
399
|
+
:active_record_primary_key, :type, :to => :source_reflection
|
400
|
+
|
401
|
+
# Gets the source of the through reflection. It checks both a singularized
|
330
402
|
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
|
331
403
|
#
|
332
404
|
# class Post < ActiveRecord::Base
|
333
405
|
# has_many :taggings
|
334
|
-
# has_many :tags, :
|
406
|
+
# has_many :tags, through: :taggings
|
407
|
+
# end
|
408
|
+
#
|
409
|
+
# class Tagging < ActiveRecord::Base
|
410
|
+
# belongs_to :post
|
411
|
+
# belongs_to :tag
|
335
412
|
# end
|
336
413
|
#
|
414
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
415
|
+
#
|
416
|
+
# taggings_reflection = tags_reflection.source_reflection
|
417
|
+
# # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
|
418
|
+
#
|
337
419
|
def source_reflection
|
338
420
|
@source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
|
339
421
|
end
|
@@ -343,7 +425,7 @@ module ActiveRecord
|
|
343
425
|
#
|
344
426
|
# class Post < ActiveRecord::Base
|
345
427
|
# has_many :taggings
|
346
|
-
# has_many :tags, :
|
428
|
+
# has_many :tags, through: :taggings
|
347
429
|
# end
|
348
430
|
#
|
349
431
|
# tags_reflection = Post.reflect_on_association(:tags)
|
@@ -353,19 +435,125 @@ module ActiveRecord
|
|
353
435
|
@through_reflection ||= active_record.reflect_on_association(options[:through])
|
354
436
|
end
|
355
437
|
|
356
|
-
#
|
438
|
+
# Returns an array of reflections which are involved in this association. Each item in the
|
439
|
+
# array corresponds to a table which will be part of the query for this association.
|
440
|
+
#
|
441
|
+
# The chain is built by recursively calling #chain on the source reflection and the through
|
442
|
+
# reflection. The base case for the recursion is a normal association, which just returns
|
443
|
+
# [self] as its #chain.
|
444
|
+
#
|
445
|
+
# class Post < ActiveRecord::Base
|
446
|
+
# has_many :taggings
|
447
|
+
# has_many :tags, through: :taggings
|
448
|
+
# end
|
449
|
+
#
|
450
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
451
|
+
# tags_reflection.chain
|
452
|
+
# # => [<ActiveRecord::Reflection::ThroughReflection: @macro=:has_many, @name=:tags, @options={:through=>:taggings}, @active_record=Post>,
|
453
|
+
# <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @options={}, @active_record=Post>]
|
454
|
+
#
|
455
|
+
def chain
|
456
|
+
@chain ||= begin
|
457
|
+
chain = source_reflection.chain + through_reflection.chain
|
458
|
+
chain[0] = self # Use self so we don't lose the information from :source_type
|
459
|
+
chain
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
# Consider the following example:
|
464
|
+
#
|
465
|
+
# class Person
|
466
|
+
# has_many :articles
|
467
|
+
# has_many :comment_tags, through: :articles
|
468
|
+
# end
|
469
|
+
#
|
470
|
+
# class Article
|
471
|
+
# has_many :comments
|
472
|
+
# has_many :comment_tags, through: :comments, source: :tags
|
473
|
+
# end
|
357
474
|
#
|
358
|
-
#
|
475
|
+
# class Comment
|
476
|
+
# has_many :tags
|
477
|
+
# end
|
478
|
+
#
|
479
|
+
# There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
|
480
|
+
# but only Comment.tags will be represented in the #chain. So this method creates an array
|
481
|
+
# of scopes corresponding to the chain.
|
482
|
+
def scope_chain
|
483
|
+
@scope_chain ||= begin
|
484
|
+
scope_chain = source_reflection.scope_chain.map(&:dup)
|
485
|
+
|
486
|
+
# Add to it the scope from this reflection (if any)
|
487
|
+
scope_chain.first << scope if scope
|
488
|
+
|
489
|
+
through_scope_chain = through_reflection.scope_chain
|
490
|
+
|
491
|
+
if options[:source_type]
|
492
|
+
through_scope_chain.first <<
|
493
|
+
through_reflection.klass.where(foreign_type => options[:source_type])
|
494
|
+
end
|
495
|
+
|
496
|
+
# Recursively fill out the rest of the array from the through reflection
|
497
|
+
scope_chain + through_scope_chain
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
# The macro used by the source association
|
502
|
+
def source_macro
|
503
|
+
source_reflection.source_macro
|
504
|
+
end
|
505
|
+
|
506
|
+
# A through association is nested if there would be more than one join table
|
507
|
+
def nested?
|
508
|
+
chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
|
509
|
+
end
|
510
|
+
|
511
|
+
# We want to use the klass from this reflection, rather than just delegate straight to
|
512
|
+
# the source_reflection, because the source_reflection may be polymorphic. We still
|
513
|
+
# need to respect the source_reflection's :primary_key option, though.
|
514
|
+
def association_primary_key(klass = nil)
|
515
|
+
# Get the "actual" source reflection if the immediate source reflection has a
|
516
|
+
# source reflection itself
|
517
|
+
source_reflection = self.source_reflection
|
518
|
+
while source_reflection.source_reflection
|
519
|
+
source_reflection = source_reflection.source_reflection
|
520
|
+
end
|
521
|
+
|
522
|
+
source_reflection.options[:primary_key] || primary_key(klass || self.klass)
|
523
|
+
end
|
524
|
+
|
525
|
+
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
|
526
|
+
#
|
527
|
+
# class Post < ActiveRecord::Base
|
528
|
+
# has_many :taggings
|
529
|
+
# has_many :tags, through: :taggings
|
530
|
+
# end
|
531
|
+
#
|
532
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
533
|
+
# tags_reflection.source_reflection_names
|
534
|
+
# # => [:tag, :tags]
|
359
535
|
#
|
360
536
|
def source_reflection_names
|
361
537
|
@source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
|
362
538
|
end
|
363
539
|
|
540
|
+
def source_options
|
541
|
+
source_reflection.options
|
542
|
+
end
|
543
|
+
|
544
|
+
def through_options
|
545
|
+
through_reflection.options
|
546
|
+
end
|
547
|
+
|
364
548
|
def check_validity!
|
365
549
|
if through_reflection.nil?
|
366
550
|
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
|
367
551
|
end
|
368
552
|
|
553
|
+
if through_reflection.options[:polymorphic]
|
554
|
+
raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
|
555
|
+
end
|
556
|
+
|
369
557
|
if source_reflection.nil?
|
370
558
|
raise HasManyThroughSourceAssociationNotFoundError.new(self)
|
371
559
|
end
|
@@ -375,24 +563,16 @@ module ActiveRecord
|
|
375
563
|
end
|
376
564
|
|
377
565
|
if source_reflection.options[:polymorphic] && options[:source_type].nil?
|
378
|
-
raise
|
566
|
+
raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
|
379
567
|
end
|
380
568
|
|
381
|
-
|
382
|
-
raise
|
569
|
+
if macro == :has_one && through_reflection.collection?
|
570
|
+
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
|
383
571
|
end
|
384
572
|
|
385
573
|
check_validity_of_inverse!
|
386
574
|
end
|
387
575
|
|
388
|
-
def through_reflection_primary_key
|
389
|
-
through_reflection.belongs_to? ? through_reflection.klass.primary_key : through_reflection.primary_key_name
|
390
|
-
end
|
391
|
-
|
392
|
-
def through_reflection_primary_key_name
|
393
|
-
through_reflection.primary_key_name if through_reflection.belongs_to?
|
394
|
-
end
|
395
|
-
|
396
576
|
private
|
397
577
|
def derive_class_name
|
398
578
|
# get the class_name of the belongs_to association of the through reflection
|