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.

Files changed (122) hide show
  1. data/CHANGELOG +220 -91
  2. data/README.rdoc +3 -3
  3. data/examples/performance.rb +88 -109
  4. data/lib/active_record.rb +6 -2
  5. data/lib/active_record/aggregations.rb +22 -45
  6. data/lib/active_record/associations.rb +264 -991
  7. data/lib/active_record/associations/alias_tracker.rb +85 -0
  8. data/lib/active_record/associations/association.rb +231 -0
  9. data/lib/active_record/associations/association_scope.rb +120 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +40 -60
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +15 -63
  12. data/lib/active_record/associations/builder/association.rb +53 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +85 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +63 -0
  16. data/lib/active_record/associations/builder/has_many.rb +65 -0
  17. data/lib/active_record/associations/builder/has_one.rb +63 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +524 -0
  20. data/lib/active_record/associations/collection_proxy.rb +125 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +27 -118
  22. data/lib/active_record/associations/has_many_association.rb +50 -79
  23. data/lib/active_record/associations/has_many_through_association.rb +98 -67
  24. data/lib/active_record/associations/has_one_association.rb +45 -115
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency.rb +215 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +150 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_helper.rb +56 -0
  31. data/lib/active_record/associations/preloader.rb +177 -0
  32. data/lib/active_record/associations/preloader/association.rb +126 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  42. data/lib/active_record/associations/singular_association.rb +55 -0
  43. data/lib/active_record/associations/through_association.rb +80 -0
  44. data/lib/active_record/attribute_methods.rb +19 -5
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +9 -8
  46. data/lib/active_record/attribute_methods/dirty.rb +8 -2
  47. data/lib/active_record/attribute_methods/primary_key.rb +33 -13
  48. data/lib/active_record/attribute_methods/read.rb +17 -17
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -4
  50. data/lib/active_record/attribute_methods/write.rb +2 -1
  51. data/lib/active_record/autosave_association.rb +66 -45
  52. data/lib/active_record/base.rb +445 -273
  53. data/lib/active_record/callbacks.rb +24 -33
  54. data/lib/active_record/coders/yaml_column.rb +41 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +106 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +16 -2
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -12
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +16 -16
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +61 -22
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -273
  62. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -42
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +44 -25
  64. data/lib/active_record/connection_adapters/column.rb +268 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +686 -0
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +331 -88
  67. data/lib/active_record/connection_adapters/postgresql_adapter.rb +295 -267
  68. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +3 -7
  69. data/lib/active_record/connection_adapters/sqlite_adapter.rb +108 -26
  70. data/lib/active_record/counter_cache.rb +7 -4
  71. data/lib/active_record/fixtures.rb +174 -192
  72. data/lib/active_record/identity_map.rb +131 -0
  73. data/lib/active_record/locking/optimistic.rb +20 -14
  74. data/lib/active_record/locking/pessimistic.rb +4 -4
  75. data/lib/active_record/log_subscriber.rb +24 -4
  76. data/lib/active_record/migration.rb +265 -144
  77. data/lib/active_record/migration/command_recorder.rb +103 -0
  78. data/lib/active_record/named_scope.rb +68 -25
  79. data/lib/active_record/nested_attributes.rb +58 -15
  80. data/lib/active_record/observer.rb +3 -7
  81. data/lib/active_record/persistence.rb +58 -38
  82. data/lib/active_record/query_cache.rb +25 -3
  83. data/lib/active_record/railtie.rb +21 -12
  84. data/lib/active_record/railties/console_sandbox.rb +6 -0
  85. data/lib/active_record/railties/databases.rake +147 -116
  86. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  87. data/lib/active_record/reflection.rb +176 -44
  88. data/lib/active_record/relation.rb +125 -49
  89. data/lib/active_record/relation/batches.rb +7 -5
  90. data/lib/active_record/relation/calculations.rb +50 -18
  91. data/lib/active_record/relation/finder_methods.rb +47 -26
  92. data/lib/active_record/relation/predicate_builder.rb +24 -21
  93. data/lib/active_record/relation/query_methods.rb +117 -101
  94. data/lib/active_record/relation/spawn_methods.rb +27 -20
  95. data/lib/active_record/result.rb +34 -0
  96. data/lib/active_record/schema.rb +5 -6
  97. data/lib/active_record/schema_dumper.rb +11 -13
  98. data/lib/active_record/serialization.rb +2 -2
  99. data/lib/active_record/serializers/xml_serializer.rb +10 -10
  100. data/lib/active_record/session_store.rb +8 -2
  101. data/lib/active_record/test_case.rb +9 -20
  102. data/lib/active_record/timestamp.rb +21 -9
  103. data/lib/active_record/transactions.rb +16 -15
  104. data/lib/active_record/validations.rb +21 -22
  105. data/lib/active_record/validations/associated.rb +3 -1
  106. data/lib/active_record/validations/uniqueness.rb +48 -58
  107. data/lib/active_record/version.rb +3 -3
  108. data/lib/rails/generators/active_record.rb +6 -0
  109. data/lib/rails/generators/active_record/migration/templates/migration.rb +10 -2
  110. data/lib/rails/generators/active_record/model/model_generator.rb +2 -1
  111. data/lib/rails/generators/active_record/model/templates/migration.rb +6 -5
  112. data/lib/rails/generators/active_record/model/templates/model.rb +2 -0
  113. data/lib/rails/generators/active_record/model/templates/module.rb +2 -0
  114. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  115. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +2 -1
  116. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +2 -2
  117. metadata +106 -77
  118. checksums.yaml +0 -7
  119. data/lib/active_record/association_preload.rb +0 -431
  120. data/lib/active_record/associations/association_collection.rb +0 -572
  121. data/lib/active_record/associations/association_proxy.rb +0 -304
  122. 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 < AssociationProxy #:nodoc:
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
- # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
33
- # has_one associations.
34
- def we_can_set_the_inverse_on_this?(record)
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 set_inverse_instance(record, instance)
44
- return if record.nil? || !we_can_set_the_inverse_on_this?(record)
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 find_target
52
- return nil if association_class.nil?
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 foreign_key_present
70
- !@owner[@reflection.primary_key_name].nil?
20
+ def klass
21
+ type = owner[reflection.foreign_type]
22
+ type && type.constantize
71
23
  end
72
24
 
73
- def record_id(record)
74
- record.send(@reflection.options[:primary_key] || :id)
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 association_class
78
- @owner[@reflection.options[:foreign_type]].present? ? @owner[@reflection.options[:foreign_type]].constantize : nil
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