activerecord 3.0.20 → 3.1.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.
- data/CHANGELOG +220 -91
- data/README.rdoc +3 -3
- data/examples/performance.rb +88 -109
- data/lib/active_record.rb +6 -2
- data/lib/active_record/aggregations.rb +22 -45
- data/lib/active_record/associations.rb +264 -991
- data/lib/active_record/associations/alias_tracker.rb +85 -0
- data/lib/active_record/associations/association.rb +231 -0
- data/lib/active_record/associations/association_scope.rb +120 -0
- data/lib/active_record/associations/belongs_to_association.rb +40 -60
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +15 -63
- data/lib/active_record/associations/builder/association.rb +53 -0
- data/lib/active_record/associations/builder/belongs_to.rb +85 -0
- data/lib/active_record/associations/builder/collection_association.rb +75 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +63 -0
- data/lib/active_record/associations/builder/has_many.rb +65 -0
- data/lib/active_record/associations/builder/has_one.rb +63 -0
- data/lib/active_record/associations/builder/singular_association.rb +32 -0
- data/lib/active_record/associations/collection_association.rb +524 -0
- data/lib/active_record/associations/collection_proxy.rb +125 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +27 -118
- data/lib/active_record/associations/has_many_association.rb +50 -79
- data/lib/active_record/associations/has_many_through_association.rb +98 -67
- data/lib/active_record/associations/has_one_association.rb +45 -115
- data/lib/active_record/associations/has_one_through_association.rb +21 -25
- data/lib/active_record/associations/join_dependency.rb +215 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +150 -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_helper.rb +56 -0
- data/lib/active_record/associations/preloader.rb +177 -0
- data/lib/active_record/associations/preloader/association.rb +126 -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 +15 -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 +67 -0
- data/lib/active_record/associations/singular_association.rb +55 -0
- data/lib/active_record/associations/through_association.rb +80 -0
- data/lib/active_record/attribute_methods.rb +19 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +9 -8
- data/lib/active_record/attribute_methods/dirty.rb +8 -2
- data/lib/active_record/attribute_methods/primary_key.rb +33 -13
- data/lib/active_record/attribute_methods/read.rb +17 -17
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -4
- data/lib/active_record/attribute_methods/write.rb +2 -1
- data/lib/active_record/autosave_association.rb +66 -45
- data/lib/active_record/base.rb +445 -273
- data/lib/active_record/callbacks.rb +24 -33
- data/lib/active_record/coders/yaml_column.rb +41 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +106 -13
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -12
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +16 -16
- data/lib/active_record/connection_adapters/abstract/quoting.rb +61 -22
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -273
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -42
- data/lib/active_record/connection_adapters/abstract_adapter.rb +44 -25
- data/lib/active_record/connection_adapters/column.rb +268 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +686 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +331 -88
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +295 -267
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +3 -7
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +108 -26
- data/lib/active_record/counter_cache.rb +7 -4
- data/lib/active_record/fixtures.rb +174 -192
- data/lib/active_record/identity_map.rb +131 -0
- data/lib/active_record/locking/optimistic.rb +20 -14
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +24 -4
- data/lib/active_record/migration.rb +265 -144
- data/lib/active_record/migration/command_recorder.rb +103 -0
- data/lib/active_record/named_scope.rb +68 -25
- data/lib/active_record/nested_attributes.rb +58 -15
- data/lib/active_record/observer.rb +3 -7
- data/lib/active_record/persistence.rb +58 -38
- data/lib/active_record/query_cache.rb +25 -3
- data/lib/active_record/railtie.rb +21 -12
- data/lib/active_record/railties/console_sandbox.rb +6 -0
- data/lib/active_record/railties/databases.rake +147 -116
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/reflection.rb +176 -44
- data/lib/active_record/relation.rb +125 -49
- data/lib/active_record/relation/batches.rb +7 -5
- data/lib/active_record/relation/calculations.rb +50 -18
- data/lib/active_record/relation/finder_methods.rb +47 -26
- data/lib/active_record/relation/predicate_builder.rb +24 -21
- data/lib/active_record/relation/query_methods.rb +117 -101
- data/lib/active_record/relation/spawn_methods.rb +27 -20
- data/lib/active_record/result.rb +34 -0
- data/lib/active_record/schema.rb +5 -6
- data/lib/active_record/schema_dumper.rb +11 -13
- data/lib/active_record/serialization.rb +2 -2
- data/lib/active_record/serializers/xml_serializer.rb +10 -10
- data/lib/active_record/session_store.rb +8 -2
- data/lib/active_record/test_case.rb +9 -20
- data/lib/active_record/timestamp.rb +21 -9
- data/lib/active_record/transactions.rb +16 -15
- data/lib/active_record/validations.rb +21 -22
- data/lib/active_record/validations/associated.rb +3 -1
- data/lib/active_record/validations/uniqueness.rb +48 -58
- data/lib/active_record/version.rb +3 -3
- data/lib/rails/generators/active_record.rb +6 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +10 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +6 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +2 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +2 -0
- data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +2 -1
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +2 -2
- metadata +106 -77
- checksums.yaml +0 -7
- data/lib/active_record/association_preload.rb +0 -431
- data/lib/active_record/associations/association_collection.rb +0 -572
- data/lib/active_record/associations/association_proxy.rb +0 -304
- data/lib/active_record/associations/through_association_scope.rb +0 -160
@@ -1,81 +1,33 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
# = Active Record Belongs To Polymorphic Association
|
3
3
|
module Associations
|
4
|
-
class BelongsToPolymorphicAssociation <
|
5
|
-
def replace(record)
|
6
|
-
if record.nil?
|
7
|
-
@target = @owner[@reflection.primary_key_name] = @owner[@reflection.options[:foreign_type]] = nil
|
8
|
-
else
|
9
|
-
@target = (AssociationProxy === record ? record.target : record)
|
10
|
-
|
11
|
-
@owner[@reflection.primary_key_name] = record_id(record)
|
12
|
-
@owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
|
13
|
-
|
14
|
-
@updated = true
|
15
|
-
end
|
16
|
-
|
17
|
-
set_inverse_instance(record, @owner)
|
18
|
-
loaded
|
19
|
-
record
|
20
|
-
end
|
21
|
-
|
22
|
-
def updated?
|
23
|
-
@updated
|
24
|
-
end
|
25
|
-
|
26
|
-
def conditions
|
27
|
-
@conditions ||= @reflection.options[:conditions] && interpolate_and_sanitize_sql(@reflection.options[:conditions], nil, association_class)
|
28
|
-
end
|
29
|
-
|
4
|
+
class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
|
30
5
|
private
|
31
6
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
if @reflection.has_inverse?
|
36
|
-
inverse_association = @reflection.polymorphic_inverse_of(record.class)
|
37
|
-
inverse_association && inverse_association.macro == :has_one
|
38
|
-
else
|
39
|
-
false
|
40
|
-
end
|
7
|
+
def replace_keys(record)
|
8
|
+
super
|
9
|
+
owner[reflection.foreign_type] = record && record.class.base_class.name
|
41
10
|
end
|
42
11
|
|
43
|
-
def
|
44
|
-
|
45
|
-
inverse_relationship = @reflection.polymorphic_inverse_of(record.class)
|
46
|
-
unless inverse_relationship.nil?
|
47
|
-
record.send(:"set_#{inverse_relationship.name}_target", instance)
|
48
|
-
end
|
12
|
+
def different_target?(record)
|
13
|
+
super || record.class != klass
|
49
14
|
end
|
50
15
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
target =
|
55
|
-
if @reflection.options[:conditions]
|
56
|
-
association_class.find(
|
57
|
-
@owner[@reflection.primary_key_name],
|
58
|
-
:select => @reflection.options[:select],
|
59
|
-
:conditions => conditions,
|
60
|
-
:include => @reflection.options[:include]
|
61
|
-
)
|
62
|
-
else
|
63
|
-
association_class.find(@owner[@reflection.primary_key_name], :select => @reflection.options[:select], :include => @reflection.options[:include])
|
64
|
-
end
|
65
|
-
set_inverse_instance(target, @owner)
|
66
|
-
target
|
16
|
+
def inverse_reflection_for(record)
|
17
|
+
reflection.polymorphic_inverse_of(record.class)
|
67
18
|
end
|
68
19
|
|
69
|
-
def
|
70
|
-
|
20
|
+
def klass
|
21
|
+
type = owner[reflection.foreign_type]
|
22
|
+
type && type.constantize
|
71
23
|
end
|
72
24
|
|
73
|
-
def
|
74
|
-
|
25
|
+
def raise_on_type_mismatch(record)
|
26
|
+
# A polymorphic association cannot have a type mismatch, by definition
|
75
27
|
end
|
76
28
|
|
77
|
-
def
|
78
|
-
|
29
|
+
def stale_state
|
30
|
+
[super, owner[reflection.foreign_type].to_s]
|
79
31
|
end
|
80
32
|
end
|
81
33
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ActiveRecord::Associations::Builder
|
2
|
+
class Association #:nodoc:
|
3
|
+
class_attribute :valid_options
|
4
|
+
self.valid_options = [:class_name, :foreign_key, :select, :conditions, :include, :extend, :readonly, :validate]
|
5
|
+
|
6
|
+
# Set by subclasses
|
7
|
+
class_attribute :macro
|
8
|
+
|
9
|
+
attr_reader :model, :name, :options, :reflection
|
10
|
+
|
11
|
+
def self.build(model, name, options)
|
12
|
+
new(model, name, options).build
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(model, name, options)
|
16
|
+
@model, @name, @options = model, name, options
|
17
|
+
end
|
18
|
+
|
19
|
+
def build
|
20
|
+
validate_options
|
21
|
+
reflection = model.create_reflection(self.class.macro, name, options, model)
|
22
|
+
define_accessors
|
23
|
+
reflection
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def validate_options
|
29
|
+
options.assert_valid_keys(self.class.valid_options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_accessors
|
33
|
+
define_readers
|
34
|
+
define_writers
|
35
|
+
end
|
36
|
+
|
37
|
+
def define_readers
|
38
|
+
name = self.name
|
39
|
+
|
40
|
+
model.redefine_method(name) do |*params|
|
41
|
+
association(name).reader(*params)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def define_writers
|
46
|
+
name = self.name
|
47
|
+
|
48
|
+
model.redefine_method("#{name}=") do |value|
|
49
|
+
association(name).writer(value)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'active_support/core_ext/object/inclusion'
|
2
|
+
|
3
|
+
module ActiveRecord::Associations::Builder
|
4
|
+
class BelongsTo < SingularAssociation #:nodoc:
|
5
|
+
self.macro = :belongs_to
|
6
|
+
|
7
|
+
self.valid_options += [:foreign_type, :polymorphic, :touch]
|
8
|
+
|
9
|
+
def constructable?
|
10
|
+
!options[:polymorphic]
|
11
|
+
end
|
12
|
+
|
13
|
+
def build
|
14
|
+
reflection = super
|
15
|
+
add_counter_cache_callbacks(reflection) if options[:counter_cache]
|
16
|
+
add_touch_callbacks(reflection) if options[:touch]
|
17
|
+
configure_dependency
|
18
|
+
reflection
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def add_counter_cache_callbacks(reflection)
|
24
|
+
cache_column = reflection.counter_cache_column
|
25
|
+
name = self.name
|
26
|
+
|
27
|
+
method_name = "belongs_to_counter_cache_after_create_for_#{name}"
|
28
|
+
model.redefine_method(method_name) do
|
29
|
+
record = send(name)
|
30
|
+
record.class.increment_counter(cache_column, record.id) unless record.nil?
|
31
|
+
end
|
32
|
+
model.after_create(method_name)
|
33
|
+
|
34
|
+
method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
|
35
|
+
model.redefine_method(method_name) do
|
36
|
+
record = send(name)
|
37
|
+
record.class.decrement_counter(cache_column, record.id) unless record.nil?
|
38
|
+
end
|
39
|
+
model.before_destroy(method_name)
|
40
|
+
|
41
|
+
model.send(:module_eval,
|
42
|
+
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_touch_callbacks(reflection)
|
47
|
+
name = self.name
|
48
|
+
method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
|
49
|
+
touch = options[:touch]
|
50
|
+
|
51
|
+
model.redefine_method(method_name) do
|
52
|
+
record = send(name)
|
53
|
+
|
54
|
+
unless record.nil?
|
55
|
+
if touch == true
|
56
|
+
record.touch
|
57
|
+
else
|
58
|
+
record.touch(touch)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
model.after_save(method_name)
|
64
|
+
model.after_touch(method_name)
|
65
|
+
model.after_destroy(method_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def configure_dependency
|
69
|
+
if options[:dependent]
|
70
|
+
unless options[:dependent].in?([:destroy, :delete])
|
71
|
+
raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{options[:dependent].inspect})"
|
72
|
+
end
|
73
|
+
|
74
|
+
method_name = "belongs_to_dependent_#{options[:dependent]}_for_#{name}"
|
75
|
+
model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
|
76
|
+
def #{method_name}
|
77
|
+
association = #{name}
|
78
|
+
association.#{options[:dependent]} if association
|
79
|
+
end
|
80
|
+
eoruby
|
81
|
+
model.after_destroy method_name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module ActiveRecord::Associations::Builder
|
2
|
+
class CollectionAssociation < Association #:nodoc:
|
3
|
+
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
4
|
+
|
5
|
+
self.valid_options += [
|
6
|
+
:table_name, :order, :group, :having, :limit, :offset, :uniq, :finder_sql,
|
7
|
+
:counter_sql, :before_add, :after_add, :before_remove, :after_remove
|
8
|
+
]
|
9
|
+
|
10
|
+
attr_reader :block_extension
|
11
|
+
|
12
|
+
def self.build(model, name, options, &extension)
|
13
|
+
new(model, name, options, &extension).build
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(model, name, options, &extension)
|
17
|
+
super(model, name, options)
|
18
|
+
@block_extension = extension
|
19
|
+
end
|
20
|
+
|
21
|
+
def build
|
22
|
+
wrap_block_extension
|
23
|
+
reflection = super
|
24
|
+
CALLBACKS.each { |callback_name| define_callback(callback_name) }
|
25
|
+
reflection
|
26
|
+
end
|
27
|
+
|
28
|
+
def writable?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def wrap_block_extension
|
35
|
+
options[:extend] = Array.wrap(options[:extend])
|
36
|
+
|
37
|
+
if block_extension
|
38
|
+
silence_warnings do
|
39
|
+
model.parent.const_set(extension_module_name, Module.new(&block_extension))
|
40
|
+
end
|
41
|
+
options[:extend].push("#{model.parent}::#{extension_module_name}".constantize)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def extension_module_name
|
46
|
+
@extension_module_name ||= "#{model.to_s.demodulize}#{name.to_s.camelize}AssociationExtension"
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_callback(callback_name)
|
50
|
+
full_callback_name = "#{callback_name}_for_#{name}"
|
51
|
+
|
52
|
+
# TODO : why do i need method_defined? I think its because of the inheritance chain
|
53
|
+
model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
|
54
|
+
model.send("#{full_callback_name}=", Array.wrap(options[callback_name.to_sym]))
|
55
|
+
end
|
56
|
+
|
57
|
+
def define_readers
|
58
|
+
super
|
59
|
+
|
60
|
+
name = self.name
|
61
|
+
model.redefine_method("#{name.to_s.singularize}_ids") do
|
62
|
+
association(name).ids_reader
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def define_writers
|
67
|
+
super
|
68
|
+
|
69
|
+
name = self.name
|
70
|
+
model.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
|
71
|
+
association(name).ids_writer(ids)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ActiveRecord::Associations::Builder
|
2
|
+
class HasAndBelongsToMany < CollectionAssociation #:nodoc:
|
3
|
+
self.macro = :has_and_belongs_to_many
|
4
|
+
|
5
|
+
self.valid_options += [:join_table, :association_foreign_key, :delete_sql, :insert_sql]
|
6
|
+
|
7
|
+
def build
|
8
|
+
reflection = super
|
9
|
+
check_validity(reflection)
|
10
|
+
define_after_destroy_method
|
11
|
+
reflection
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def define_after_destroy_method
|
17
|
+
name = self.name
|
18
|
+
model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
|
19
|
+
def #{after_destroy_method_name}
|
20
|
+
association(#{name.to_sym.inspect}).delete_all
|
21
|
+
end
|
22
|
+
eoruby
|
23
|
+
model.after_destroy after_destroy_method_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def after_destroy_method_name
|
27
|
+
"has_and_belongs_to_many_after_destroy_for_#{name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# TODO: These checks should probably be moved into the Reflection, and we should not be
|
31
|
+
# redefining the options[:join_table] value - instead we should define a
|
32
|
+
# reflection.join_table method.
|
33
|
+
def check_validity(reflection)
|
34
|
+
if reflection.association_foreign_key == reflection.foreign_key
|
35
|
+
raise ActiveRecord::HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
|
36
|
+
end
|
37
|
+
|
38
|
+
reflection.options[:join_table] ||= join_table_name(
|
39
|
+
model.send(:undecorated_table_name, model.to_s),
|
40
|
+
model.send(:undecorated_table_name, reflection.class_name)
|
41
|
+
)
|
42
|
+
|
43
|
+
if model.connection.supports_primary_key? && (model.connection.primary_key(reflection.options[:join_table]) rescue false)
|
44
|
+
raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generates a join table name from two provided table names.
|
49
|
+
# The names in the join table names end up in lexicographic order.
|
50
|
+
#
|
51
|
+
# join_table_name("members", "clubs") # => "clubs_members"
|
52
|
+
# join_table_name("members", "special_clubs") # => "members_special_clubs"
|
53
|
+
def join_table_name(first_table_name, second_table_name)
|
54
|
+
if first_table_name < second_table_name
|
55
|
+
join_table = "#{first_table_name}_#{second_table_name}"
|
56
|
+
else
|
57
|
+
join_table = "#{second_table_name}_#{first_table_name}"
|
58
|
+
end
|
59
|
+
|
60
|
+
model.table_name_prefix + join_table + model.table_name_suffix
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'active_support/core_ext/object/inclusion'
|
2
|
+
|
3
|
+
module ActiveRecord::Associations::Builder
|
4
|
+
class HasMany < CollectionAssociation #:nodoc:
|
5
|
+
self.macro = :has_many
|
6
|
+
|
7
|
+
self.valid_options += [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of]
|
8
|
+
|
9
|
+
def build
|
10
|
+
reflection = super
|
11
|
+
configure_dependency
|
12
|
+
reflection
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def configure_dependency
|
18
|
+
if options[:dependent]
|
19
|
+
unless options[:dependent].in?([:destroy, :delete_all, :nullify, :restrict])
|
20
|
+
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, " \
|
21
|
+
":nullify or :restrict (#{options[:dependent].inspect})"
|
22
|
+
end
|
23
|
+
|
24
|
+
send("define_#{options[:dependent]}_dependency_method")
|
25
|
+
model.before_destroy dependency_method_name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_destroy_dependency_method
|
30
|
+
name = self.name
|
31
|
+
model.send(:define_method, dependency_method_name) do
|
32
|
+
send(name).each do |o|
|
33
|
+
# No point in executing the counter update since we're going to destroy the parent anyway
|
34
|
+
counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
|
35
|
+
if o.respond_to?(counter_method)
|
36
|
+
class << o
|
37
|
+
self
|
38
|
+
end.send(:define_method, counter_method, Proc.new {})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
send(name).delete_all
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def define_delete_all_dependency_method
|
47
|
+
name = self.name
|
48
|
+
model.send(:define_method, dependency_method_name) do
|
49
|
+
send(name).delete_all
|
50
|
+
end
|
51
|
+
end
|
52
|
+
alias :define_nullify_dependency_method :define_delete_all_dependency_method
|
53
|
+
|
54
|
+
def define_restrict_dependency_method
|
55
|
+
name = self.name
|
56
|
+
model.send(:define_method, dependency_method_name) do
|
57
|
+
raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).empty?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def dependency_method_name
|
62
|
+
"has_many_dependent_for_#{name}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'active_support/core_ext/object/inclusion'
|
2
|
+
|
3
|
+
module ActiveRecord::Associations::Builder
|
4
|
+
class HasOne < SingularAssociation #:nodoc:
|
5
|
+
self.macro = :has_one
|
6
|
+
|
7
|
+
self.valid_options += [:order, :as]
|
8
|
+
|
9
|
+
class_attribute :through_options
|
10
|
+
self.through_options = [:through, :source, :source_type]
|
11
|
+
|
12
|
+
def constructable?
|
13
|
+
!options[:through]
|
14
|
+
end
|
15
|
+
|
16
|
+
def build
|
17
|
+
reflection = super
|
18
|
+
configure_dependency unless options[:through]
|
19
|
+
reflection
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def validate_options
|
25
|
+
valid_options = self.class.valid_options
|
26
|
+
valid_options += self.class.through_options if options[:through]
|
27
|
+
options.assert_valid_keys(valid_options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def configure_dependency
|
31
|
+
if options[:dependent]
|
32
|
+
unless options[:dependent].in?([:destroy, :delete, :nullify, :restrict])
|
33
|
+
raise ArgumentError, "The :dependent option expects either :destroy, :delete, " \
|
34
|
+
":nullify or :restrict (#{options[:dependent].inspect})"
|
35
|
+
end
|
36
|
+
|
37
|
+
send("define_#{options[:dependent]}_dependency_method")
|
38
|
+
model.before_destroy dependency_method_name
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def dependency_method_name
|
43
|
+
"has_one_dependent_#{options[:dependent]}_for_#{name}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def define_destroy_dependency_method
|
47
|
+
model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
|
48
|
+
def #{dependency_method_name}
|
49
|
+
association(#{name.to_sym.inspect}).delete
|
50
|
+
end
|
51
|
+
eoruby
|
52
|
+
end
|
53
|
+
alias :define_delete_dependency_method :define_destroy_dependency_method
|
54
|
+
alias :define_nullify_dependency_method :define_destroy_dependency_method
|
55
|
+
|
56
|
+
def define_restrict_dependency_method
|
57
|
+
name = self.name
|
58
|
+
model.redefine_method(dependency_method_name) do
|
59
|
+
raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).nil?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|