activerecord 3.2.22.5 → 4.0.0.beta1
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 +4 -4
- data/CHANGELOG.md +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -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/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -8,7 +8,7 @@ module ActiveRecord
|
|
8
8
|
attr_reader :aliases, :table_joins, :connection
|
9
9
|
|
10
10
|
# table_joins is an array of arel joins which might conflict with the aliases we assign here
|
11
|
-
def initialize(connection =
|
11
|
+
def initialize(connection = Base.connection, table_joins = [])
|
12
12
|
@aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
|
13
13
|
@table_joins = table_joins
|
14
14
|
@connection = connection
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'active_support/core_ext/array/wrap'
|
2
|
-
require 'active_support/core_ext/object/inclusion'
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
4
|
module Associations
|
@@ -25,9 +24,7 @@ module ActiveRecord
|
|
25
24
|
def initialize(owner, reflection)
|
26
25
|
reflection.check_validity!
|
27
26
|
|
28
|
-
@target = nil
|
29
27
|
@owner, @reflection = owner, reflection
|
30
|
-
@updated = false
|
31
28
|
|
32
29
|
reset
|
33
30
|
reset_scope
|
@@ -38,13 +35,12 @@ module ActiveRecord
|
|
38
35
|
# post.comments.aliased_table_name # => "comments"
|
39
36
|
#
|
40
37
|
def aliased_table_name
|
41
|
-
|
38
|
+
klass.table_name
|
42
39
|
end
|
43
40
|
|
44
41
|
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
45
42
|
def reset
|
46
43
|
@loaded = false
|
47
|
-
IdentityMap.remove(target) if IdentityMap.enabled? && target
|
48
44
|
@target = nil
|
49
45
|
@stale_state = nil
|
50
46
|
end
|
@@ -84,10 +80,15 @@ module ActiveRecord
|
|
84
80
|
loaded!
|
85
81
|
end
|
86
82
|
|
87
|
-
def
|
83
|
+
def scope
|
88
84
|
target_scope.merge(association_scope)
|
89
85
|
end
|
90
86
|
|
87
|
+
def scoped
|
88
|
+
ActiveSupport::Deprecation.warn "#scoped is deprecated. use #scope instead."
|
89
|
+
scope
|
90
|
+
end
|
91
|
+
|
91
92
|
# The scope for this association.
|
92
93
|
#
|
93
94
|
# Note that the association_scope is merged into the target_scope only when the
|
@@ -121,7 +122,7 @@ module ActiveRecord
|
|
121
122
|
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
|
122
123
|
# through association's scope)
|
123
124
|
def target_scope
|
124
|
-
klass.
|
125
|
+
klass.all
|
125
126
|
end
|
126
127
|
|
127
128
|
# Loads the \target if needed and returns it.
|
@@ -129,28 +130,14 @@ module ActiveRecord
|
|
129
130
|
# This method is abstract in the sense that it relies on +find_target+,
|
130
131
|
# which is expected to be provided by descendants.
|
131
132
|
#
|
132
|
-
# If the \target is
|
133
|
-
#
|
134
|
-
#
|
135
|
-
# Otherwise if the \target is already \loaded it is just returned. Thus, you can
|
136
|
-
# call +load_target+ unconditionally to get the \target.
|
133
|
+
# If the \target is already \loaded it is just returned. Thus, you can call
|
134
|
+
# +load_target+ unconditionally to get the \target.
|
137
135
|
#
|
138
136
|
# ActiveRecord::RecordNotFound is rescued within the method, and it is
|
139
137
|
# not reraised. The proxy is \reset and +nil+ is the return value.
|
140
138
|
def load_target
|
141
|
-
if (@stale_state && stale_target?) || find_target?
|
142
|
-
|
143
|
-
if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
|
144
|
-
@target = IdentityMap.get(association_class, owner[reflection.foreign_key])
|
145
|
-
elsif @stale_state && stale_target?
|
146
|
-
@target = find_target
|
147
|
-
end
|
148
|
-
rescue NameError
|
149
|
-
nil
|
150
|
-
ensure
|
151
|
-
@target ||= find_target
|
152
|
-
end
|
153
|
-
end
|
139
|
+
@target = find_target if (@stale_state && stale_target?) || find_target?
|
140
|
+
|
154
141
|
loaded! unless loaded?
|
155
142
|
target
|
156
143
|
rescue ActiveRecord::RecordNotFound
|
@@ -158,13 +145,25 @@ module ActiveRecord
|
|
158
145
|
end
|
159
146
|
|
160
147
|
def interpolate(sql, record = nil)
|
161
|
-
if sql.respond_to?(:to_proc)
|
148
|
+
if sql.respond_to?(:to_proc)
|
162
149
|
owner.send(:instance_exec, record, &sql)
|
163
150
|
else
|
164
151
|
sql
|
165
152
|
end
|
166
153
|
end
|
167
154
|
|
155
|
+
# We can't dump @reflection since it contains the scope proc
|
156
|
+
def marshal_dump
|
157
|
+
ivars = (instance_variables - [:@reflection]).map { |name| [name, instance_variable_get(name)] }
|
158
|
+
[@reflection.name, ivars]
|
159
|
+
end
|
160
|
+
|
161
|
+
def marshal_load(data)
|
162
|
+
reflection_name, ivars = data
|
163
|
+
ivars.each { |name, val| instance_variable_set(name, val) }
|
164
|
+
@reflection = @owner.class.reflect_on_association(reflection_name)
|
165
|
+
end
|
166
|
+
|
168
167
|
private
|
169
168
|
|
170
169
|
def find_target?
|
@@ -174,7 +173,7 @@ module ActiveRecord
|
|
174
173
|
def creation_attributes
|
175
174
|
attributes = {}
|
176
175
|
|
177
|
-
if reflection.macro
|
176
|
+
if (reflection.macro == :has_one || reflection.macro == :has_many) && !options[:through]
|
178
177
|
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
|
179
178
|
|
180
179
|
if reflection.options[:as]
|
@@ -224,22 +223,18 @@ module ActiveRecord
|
|
224
223
|
end
|
225
224
|
|
226
225
|
# This should be implemented to return the values of the relevant key(s) on the owner,
|
227
|
-
# so that when
|
226
|
+
# so that when stale_state is different from the value stored on the last find_target,
|
228
227
|
# the target is stale.
|
229
228
|
#
|
230
229
|
# This is only relevant to certain associations, which is why it returns nil by default.
|
231
230
|
def stale_state
|
232
231
|
end
|
233
232
|
|
234
|
-
def
|
235
|
-
|
236
|
-
end
|
237
|
-
|
238
|
-
def build_record(attributes, options)
|
239
|
-
reflection.build_association(attributes, options) do |record|
|
233
|
+
def build_record(attributes)
|
234
|
+
reflection.build_association(attributes) do |record|
|
240
235
|
skip_assign = [reflection.foreign_key, reflection.type].compact
|
241
236
|
attributes = create_scope.except(*(record.changed - skip_assign))
|
242
|
-
record.assign_attributes(attributes
|
237
|
+
record.assign_attributes(attributes)
|
243
238
|
end
|
244
239
|
end
|
245
240
|
end
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
attr_reader :association, :alias_tracker
|
7
7
|
|
8
8
|
delegate :klass, :owner, :reflection, :interpolate, :to => :association
|
9
|
-
delegate :chain, :
|
9
|
+
delegate :chain, :scope_chain, :options, :source_options, :active_record, :to => :reflection
|
10
10
|
|
11
11
|
def initialize(association)
|
12
12
|
@association = association
|
@@ -15,23 +15,28 @@ module ActiveRecord
|
|
15
15
|
|
16
16
|
def scope
|
17
17
|
scope = klass.unscoped
|
18
|
-
scope
|
19
|
-
|
20
|
-
|
21
|
-
# association supports that option; this is enforced by the association builder.
|
22
|
-
scope = scope.apply_finder_options(options.slice(
|
23
|
-
:readonly, :include, :order, :limit, :joins, :group, :having, :offset, :select))
|
18
|
+
scope.extending! Array(options[:extend])
|
19
|
+
add_constraints(scope)
|
20
|
+
end
|
24
21
|
|
25
|
-
|
26
|
-
scope = scope.includes(source_options[:include])
|
27
|
-
end
|
22
|
+
private
|
28
23
|
|
29
|
-
|
24
|
+
def column_for(table_name, column_name)
|
25
|
+
columns = alias_tracker.connection.schema_cache.columns_hash[table_name]
|
26
|
+
columns[column_name]
|
27
|
+
end
|
30
28
|
|
31
|
-
|
29
|
+
def bind_value(scope, column, value)
|
30
|
+
substitute = alias_tracker.connection.substitute_at(
|
31
|
+
column, scope.bind_values.length)
|
32
|
+
scope.bind_values += [[column, value]]
|
33
|
+
substitute
|
32
34
|
end
|
33
35
|
|
34
|
-
|
36
|
+
def bind(scope, table_name, column_name, value)
|
37
|
+
column = column_for table_name, column_name
|
38
|
+
bind_value scope, column, value
|
39
|
+
end
|
35
40
|
|
36
41
|
def add_constraints(scope)
|
37
42
|
tables = construct_tables
|
@@ -53,7 +58,7 @@ module ActiveRecord
|
|
53
58
|
|
54
59
|
if reflection.source_macro == :belongs_to
|
55
60
|
if reflection.options[:polymorphic]
|
56
|
-
key = reflection.association_primary_key(klass)
|
61
|
+
key = reflection.association_primary_key(self.klass)
|
57
62
|
else
|
58
63
|
key = reflection.association_primary_key
|
59
64
|
end
|
@@ -64,21 +69,14 @@ module ActiveRecord
|
|
64
69
|
foreign_key = reflection.active_record_primary_key
|
65
70
|
end
|
66
71
|
|
67
|
-
conditions = self.conditions[i]
|
68
|
-
|
69
72
|
if reflection == chain.last
|
70
|
-
|
73
|
+
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key]
|
74
|
+
scope = scope.where(table[key].eq(bind_val))
|
71
75
|
|
72
76
|
if reflection.type
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
conditions.each do |condition|
|
77
|
-
if options[:through] && condition.is_a?(Hash)
|
78
|
-
condition = disambiguate_condition(table, condition)
|
79
|
-
end
|
80
|
-
|
81
|
-
scope = scope.where(interpolate(condition))
|
77
|
+
value = owner.class.base_class.name
|
78
|
+
bind_val = bind scope, table.table_name, reflection.type.to_s, value
|
79
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
82
80
|
end
|
83
81
|
else
|
84
82
|
constraint = table[key].eq(foreign_table[foreign_key])
|
@@ -89,10 +87,20 @@ module ActiveRecord
|
|
89
87
|
end
|
90
88
|
|
91
89
|
scope = scope.joins(join(foreign_table, constraint))
|
90
|
+
end
|
91
|
+
|
92
|
+
# Exclude the scope of the association itself, because that
|
93
|
+
# was already merged in the #scope method.
|
94
|
+
scope_chain[i].each do |scope_chain_item|
|
95
|
+
klass = i == 0 ? self.klass : reflection.klass
|
96
|
+
item = eval_scope(klass, scope_chain_item)
|
92
97
|
|
93
|
-
|
94
|
-
scope
|
98
|
+
if scope_chain_item == self.reflection.scope
|
99
|
+
scope.merge! item.except(:where, :includes)
|
95
100
|
end
|
101
|
+
|
102
|
+
scope.includes! item.includes_values
|
103
|
+
scope.where_values += item.where_values
|
96
104
|
end
|
97
105
|
end
|
98
106
|
|
@@ -114,19 +122,11 @@ module ActiveRecord
|
|
114
122
|
end
|
115
123
|
end
|
116
124
|
|
117
|
-
def
|
118
|
-
if
|
119
|
-
|
120
|
-
condition.map do |k, v|
|
121
|
-
if v.is_a?(Hash)
|
122
|
-
[k, v]
|
123
|
-
else
|
124
|
-
[table.table_alias || table.name, { k => v }]
|
125
|
-
end
|
126
|
-
end
|
127
|
-
]
|
125
|
+
def eval_scope(klass, scope)
|
126
|
+
if scope.is_a?(Relation)
|
127
|
+
scope
|
128
128
|
else
|
129
|
-
|
129
|
+
klass.unscoped.instance_exec(owner, &scope)
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
@@ -2,6 +2,11 @@ module ActiveRecord
|
|
2
2
|
# = Active Record Belongs To Associations
|
3
3
|
module Associations
|
4
4
|
class BelongsToAssociation < SingularAssociation #:nodoc:
|
5
|
+
|
6
|
+
def handle_dependency
|
7
|
+
target.send(options[:dependent]) if load_target
|
8
|
+
end
|
9
|
+
|
5
10
|
def replace(record)
|
6
11
|
raise_on_type_mismatch(record) if record
|
7
12
|
|
@@ -14,6 +19,11 @@ module ActiveRecord
|
|
14
19
|
self.target = record
|
15
20
|
end
|
16
21
|
|
22
|
+
def reset
|
23
|
+
super
|
24
|
+
@updated = false
|
25
|
+
end
|
26
|
+
|
17
27
|
def updated?
|
18
28
|
@updated
|
19
29
|
end
|
@@ -40,8 +50,11 @@ module ActiveRecord
|
|
40
50
|
|
41
51
|
# Checks whether record is different to the current target, without loading it
|
42
52
|
def different_target?(record)
|
43
|
-
record.nil?
|
44
|
-
|
53
|
+
if record.nil?
|
54
|
+
owner[reflection.foreign_key]
|
55
|
+
else
|
56
|
+
record.id != owner[reflection.foreign_key]
|
57
|
+
end
|
45
58
|
end
|
46
59
|
|
47
60
|
def replace_keys(record)
|
@@ -1,55 +1,108 @@
|
|
1
1
|
module ActiveRecord::Associations::Builder
|
2
2
|
class Association #:nodoc:
|
3
|
-
|
4
|
-
|
3
|
+
class << self
|
4
|
+
attr_accessor :valid_options
|
5
|
+
end
|
5
6
|
|
6
|
-
|
7
|
-
class_attribute :macro
|
7
|
+
self.valid_options = [:class_name, :foreign_key, :validate]
|
8
8
|
|
9
|
-
attr_reader :model, :name, :options, :reflection
|
9
|
+
attr_reader :model, :name, :scope, :options, :reflection
|
10
10
|
|
11
|
-
def self.build(
|
12
|
-
new(
|
11
|
+
def self.build(*args, &block)
|
12
|
+
new(*args, &block).build
|
13
13
|
end
|
14
14
|
|
15
|
-
def initialize(model, name, options)
|
16
|
-
|
15
|
+
def initialize(model, name, scope, options)
|
16
|
+
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
|
17
|
+
|
18
|
+
@model = model
|
19
|
+
@name = name
|
20
|
+
|
21
|
+
if scope.is_a?(Hash)
|
22
|
+
@scope = nil
|
23
|
+
@options = scope
|
24
|
+
else
|
25
|
+
@scope = scope
|
26
|
+
@options = options
|
27
|
+
end
|
28
|
+
|
29
|
+
if @scope && @scope.arity == 0
|
30
|
+
prev_scope = @scope
|
31
|
+
@scope = proc { instance_exec(&prev_scope) }
|
32
|
+
end
|
17
33
|
end
|
18
34
|
|
19
35
|
def mixin
|
20
36
|
@model.generated_feature_methods
|
21
37
|
end
|
22
38
|
|
39
|
+
include Module.new { def build; end }
|
40
|
+
|
23
41
|
def build
|
24
42
|
validate_options
|
25
|
-
reflection = model.create_reflection(self.class.macro, name, options, model)
|
26
43
|
define_accessors
|
27
|
-
|
44
|
+
configure_dependency if options[:dependent]
|
45
|
+
@reflection = model.create_reflection(macro, name, scope, options, model)
|
46
|
+
super # provides an extension point
|
47
|
+
@reflection
|
28
48
|
end
|
29
49
|
|
30
|
-
|
50
|
+
def macro
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
31
53
|
|
32
|
-
|
33
|
-
|
34
|
-
|
54
|
+
def valid_options
|
55
|
+
Association.valid_options
|
56
|
+
end
|
35
57
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
58
|
+
def validate_options
|
59
|
+
options.assert_valid_keys(valid_options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_accessors
|
63
|
+
define_readers
|
64
|
+
define_writers
|
65
|
+
end
|
40
66
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
association(name).reader(*
|
67
|
+
def define_readers
|
68
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
69
|
+
def #{name}(*args)
|
70
|
+
association(:#{name}).reader(*args)
|
45
71
|
end
|
46
|
-
|
72
|
+
CODE
|
73
|
+
end
|
47
74
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
association(name).writer(value)
|
75
|
+
def define_writers
|
76
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
77
|
+
def #{name}=(value)
|
78
|
+
association(:#{name}).writer(value)
|
52
79
|
end
|
80
|
+
CODE
|
81
|
+
end
|
82
|
+
|
83
|
+
def configure_dependency
|
84
|
+
unless valid_dependent_options.include? options[:dependent]
|
85
|
+
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{options[:dependent]}"
|
86
|
+
end
|
87
|
+
|
88
|
+
if options[:dependent] == :restrict
|
89
|
+
ActiveSupport::Deprecation.warn(
|
90
|
+
"The :restrict option is deprecated. Please use :restrict_with_exception instead, which " \
|
91
|
+
"provides the same functionality."
|
92
|
+
)
|
53
93
|
end
|
94
|
+
|
95
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
96
|
+
def #{macro}_dependent_for_#{name}
|
97
|
+
association(:#{name}).handle_dependency
|
98
|
+
end
|
99
|
+
CODE
|
100
|
+
|
101
|
+
model.before_destroy "#{macro}_dependent_for_#{name}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def valid_dependent_options
|
105
|
+
raise NotImplementedError
|
106
|
+
end
|
54
107
|
end
|
55
108
|
end
|