activerecord 3.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.

Files changed (93) hide show
  1. data/CHANGELOG +6023 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +162 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +403 -0
  9. data/lib/active_record/associations.rb +2254 -0
  10. data/lib/active_record/associations/association_collection.rb +562 -0
  11. data/lib/active_record/associations/association_proxy.rb +295 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +116 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +116 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  27. data/lib/active_record/attribute_methods/write.rb +37 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1867 -0
  30. data/lib/active_record/callbacks.rb +288 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +53 -0
  46. data/lib/active_record/dynamic_scope_match.rb +32 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1008 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +417 -0
  56. data/lib/active_record/observer.rb +140 -0
  57. data/lib/active_record/persistence.rb +291 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +403 -0
  63. data/lib/active_record/relation.rb +393 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +286 -0
  66. data/lib/active_record/relation/finder_methods.rb +355 -0
  67. data/lib/active_record/relation/predicate_builder.rb +41 -0
  68. data/lib/active_record/relation/query_methods.rb +261 -0
  69. data/lib/active_record/relation/spawn_methods.rb +112 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +356 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +185 -0
  81. data/lib/active_record/version.rb +9 -0
  82. data/lib/rails/generators/active_record.rb +27 -0
  83. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  84. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  85. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  86. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  87. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  88. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  89. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  90. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  91. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  92. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  93. metadata +224 -0
@@ -0,0 +1,137 @@
1
+ module ActiveRecord
2
+ # = Active Record Has And Belongs To Many Association
3
+ module Associations
4
+ class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
5
+ def create(attributes = {})
6
+ create_record(attributes) { |record| insert_record(record) }
7
+ end
8
+
9
+ def create!(attributes = {})
10
+ create_record(attributes) { |record| insert_record(record, true) }
11
+ end
12
+
13
+ def columns
14
+ @reflection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
15
+ end
16
+
17
+ def reset_column_information
18
+ @reflection.reset_column_information
19
+ end
20
+
21
+ def has_primary_key?
22
+ @has_primary_key ||= @owner.connection.supports_primary_key? && @owner.connection.primary_key(@reflection.options[:join_table])
23
+ end
24
+
25
+ protected
26
+ def construct_find_options!(options)
27
+ options[:joins] = Arel::SqlLiteral.new @join_sql
28
+ options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
29
+ options[:select] ||= (@reflection.options[:select] || Arel::SqlLiteral.new('*'))
30
+ end
31
+
32
+ def count_records
33
+ load_target.size
34
+ end
35
+
36
+ def insert_record(record, force = true, validate = true)
37
+ if record.new_record?
38
+ if force
39
+ record.save!
40
+ else
41
+ return false unless record.save(:validate => validate)
42
+ end
43
+ end
44
+
45
+ if @reflection.options[:insert_sql]
46
+ @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
47
+ else
48
+ relation = Arel::Table.new(@reflection.options[:join_table])
49
+ timestamps = record_timestamp_columns(record)
50
+ timezone = record.send(:current_time_from_proper_timezone) if timestamps.any?
51
+
52
+ attributes = columns.inject({}) do |attrs, column|
53
+ name = column.name
54
+ case name.to_s
55
+ when @reflection.primary_key_name.to_s
56
+ attrs[relation[name]] = @owner.id
57
+ when @reflection.association_foreign_key.to_s
58
+ attrs[relation[name]] = record.id
59
+ when *timestamps
60
+ attrs[relation[name]] = timezone
61
+ else
62
+ if record.has_attribute?(name)
63
+ value = @owner.send(:quote_value, record[name], column)
64
+ attrs[relation[name]] = value unless value.nil?
65
+ end
66
+ end
67
+ attrs
68
+ end
69
+
70
+ relation.insert(attributes)
71
+ end
72
+
73
+ return true
74
+ end
75
+
76
+ def delete_records(records)
77
+ if sql = @reflection.options[:delete_sql]
78
+ records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) }
79
+ else
80
+ relation = Arel::Table.new(@reflection.options[:join_table])
81
+ relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
82
+ and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }))
83
+ ).delete
84
+ end
85
+ end
86
+
87
+ def construct_sql
88
+ if @reflection.options[:finder_sql]
89
+ @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
90
+ else
91
+ @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} "
92
+ @finder_sql << " AND (#{conditions})" if conditions
93
+ end
94
+
95
+ @join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
96
+
97
+ construct_counter_sql
98
+ end
99
+
100
+ def construct_scope
101
+ { :find => { :conditions => @finder_sql,
102
+ :joins => @join_sql,
103
+ :readonly => false,
104
+ :order => @reflection.options[:order],
105
+ :include => @reflection.options[:include],
106
+ :limit => @reflection.options[:limit] } }
107
+ end
108
+
109
+ # Join tables with additional columns on top of the two foreign keys must be considered
110
+ # ambiguous unless a select clause has been explicitly defined. Otherwise you can get
111
+ # broken records back, if, for example, the join column also has an id column. This will
112
+ # then overwrite the id column of the records coming back.
113
+ def finding_with_ambiguous_select?(select_clause)
114
+ !select_clause && columns.size != 2
115
+ end
116
+
117
+ private
118
+ def create_record(attributes, &block)
119
+ # Can't use Base.create because the foreign key may be a protected attribute.
120
+ ensure_owner_is_not_new
121
+ if attributes.is_a?(Array)
122
+ attributes.collect { |attr| create(attr) }
123
+ else
124
+ build_record(attributes, &block)
125
+ end
126
+ end
127
+
128
+ def record_timestamp_columns(record)
129
+ if record.record_timestamps
130
+ record.send(:all_timestamp_attributes).map { |x| x.to_s }
131
+ else
132
+ []
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,128 @@
1
+ module ActiveRecord
2
+ # = Active Record Has Many Association
3
+ module Associations
4
+ # This is the proxy that handles a has many association.
5
+ #
6
+ # If the association has a <tt>:through</tt> option further specialization
7
+ # is provided by its child HasManyThroughAssociation.
8
+ class HasManyAssociation < AssociationCollection #:nodoc:
9
+ def initialize(owner, reflection)
10
+ @finder_sql = nil
11
+ super
12
+ end
13
+ protected
14
+ def owner_quoted_id
15
+ if @reflection.options[:primary_key]
16
+ quote_value(@owner.send(@reflection.options[:primary_key]))
17
+ else
18
+ @owner.quoted_id
19
+ end
20
+ end
21
+
22
+ # Returns the number of records in this collection.
23
+ #
24
+ # If the association has a counter cache it gets that value. Otherwise
25
+ # it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
26
+ # there's one. Some configuration options like :group make it impossible
27
+ # to do an SQL count, in those cases the array count will be used.
28
+ #
29
+ # That does not depend on whether the collection has already been loaded
30
+ # or not. The +size+ method is the one that takes the loaded flag into
31
+ # account and delegates to +count_records+ if needed.
32
+ #
33
+ # If the collection is empty the target is set to an empty array and
34
+ # the loaded flag is set to true as well.
35
+ def count_records
36
+ count = if has_cached_counter?
37
+ @owner.send(:read_attribute, cached_counter_attribute_name)
38
+ elsif @reflection.options[:counter_sql]
39
+ @reflection.klass.count_by_sql(@counter_sql)
40
+ else
41
+ @reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
42
+ end
43
+
44
+ # If there's nothing in the database and @target has no new records
45
+ # we are certain the current target is an empty array. This is a
46
+ # documented side-effect of the method that may avoid an extra SELECT.
47
+ @target ||= [] and loaded if count == 0
48
+
49
+ if @reflection.options[:limit]
50
+ count = [ @reflection.options[:limit], count ].min
51
+ end
52
+
53
+ return count
54
+ end
55
+
56
+ def has_cached_counter?
57
+ @owner.attribute_present?(cached_counter_attribute_name)
58
+ end
59
+
60
+ def cached_counter_attribute_name
61
+ "#{@reflection.name}_count"
62
+ end
63
+
64
+ def insert_record(record, force = false, validate = true)
65
+ set_belongs_to_association_for(record)
66
+ force ? record.save! : record.save(:validate => validate)
67
+ end
68
+
69
+ # Deletes the records according to the <tt>:dependent</tt> option.
70
+ def delete_records(records)
71
+ case @reflection.options[:dependent]
72
+ when :destroy
73
+ records.each { |r| r.destroy }
74
+ when :delete_all
75
+ @reflection.klass.delete(records.map { |record| record.id })
76
+ else
77
+ relation = Arel::Table.new(@reflection.table_name)
78
+ relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
79
+ and(relation[@reflection.klass.primary_key].in(records.map { |r| r.id }))
80
+ ).update(relation[@reflection.primary_key_name] => nil)
81
+
82
+ @owner.class.update_counters(@owner.id, cached_counter_attribute_name => -records.size) if has_cached_counter?
83
+ end
84
+ end
85
+
86
+ def target_obsolete?
87
+ false
88
+ end
89
+
90
+ def construct_sql
91
+ case
92
+ when @reflection.options[:finder_sql]
93
+ @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
94
+
95
+ when @reflection.options[:as]
96
+ @finder_sql =
97
+ "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
98
+ "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
99
+ @finder_sql << " AND (#{conditions})" if conditions
100
+
101
+ else
102
+ @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
103
+ @finder_sql << " AND (#{conditions})" if conditions
104
+ end
105
+
106
+ construct_counter_sql
107
+ end
108
+
109
+ def construct_scope
110
+ create_scoping = {}
111
+ set_belongs_to_association_for(create_scoping)
112
+ {
113
+ :find => { :conditions => @finder_sql,
114
+ :readonly => false,
115
+ :order => @reflection.options[:order],
116
+ :limit => @reflection.options[:limit],
117
+ :include => @reflection.options[:include]},
118
+ :create => create_scoping
119
+ }
120
+ end
121
+
122
+ def we_can_set_the_inverse_on_this?(record)
123
+ inverse = @reflection.inverse_of
124
+ return !inverse.nil?
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,116 @@
1
+ require "active_record/associations/through_association_scope"
2
+ require 'active_support/core_ext/object/blank'
3
+
4
+ module ActiveRecord
5
+ # = Active Record Has Many Through Association
6
+ module Associations
7
+ class HasManyThroughAssociation < HasManyAssociation #:nodoc:
8
+ include ThroughAssociationScope
9
+
10
+ alias_method :new, :build
11
+
12
+ def create!(attrs = nil)
13
+ create_record(attrs, true)
14
+ end
15
+
16
+ def create(attrs = nil)
17
+ create_record(attrs, false)
18
+ end
19
+
20
+ def destroy(*records)
21
+ transaction do
22
+ delete_records(flatten_deeper(records))
23
+ super
24
+ end
25
+ end
26
+
27
+ # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been
28
+ # loaded and calling collection.size if it has. If it's more likely than not that the collection does
29
+ # have a size larger than zero, and you need to fetch that collection afterwards, it'll take one fewer
30
+ # SELECT query if you use #length.
31
+ def size
32
+ return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter?
33
+ return @target.size if loaded?
34
+ return count
35
+ end
36
+
37
+ protected
38
+ def create_record(attrs, force = true)
39
+ ensure_owner_is_not_new
40
+
41
+ transaction do
42
+ object = @reflection.klass.new(attrs)
43
+ add_record_to_target_with_callbacks(object) {|r| insert_record(object, force) }
44
+ object
45
+ end
46
+ end
47
+
48
+ def target_reflection_has_associated_record?
49
+ if @reflection.through_reflection.macro == :belongs_to && @owner[@reflection.through_reflection.primary_key_name].blank?
50
+ false
51
+ else
52
+ true
53
+ end
54
+ end
55
+
56
+ def construct_find_options!(options)
57
+ options[:joins] = construct_joins(options[:joins])
58
+ options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? && @reflection.source_reflection.options[:include]
59
+ end
60
+
61
+ def insert_record(record, force = true, validate = true)
62
+ if record.new_record?
63
+ if force
64
+ record.save!
65
+ else
66
+ return false unless record.save(:validate => validate)
67
+ end
68
+ end
69
+
70
+ through_association = @owner.send(@reflection.through_reflection.name)
71
+ through_record = through_association.create!(construct_join_attributes(record))
72
+ through_association.proxy_target << through_record
73
+ end
74
+
75
+ # TODO - add dependent option support
76
+ def delete_records(records)
77
+ klass = @reflection.through_reflection.klass
78
+ records.each do |associate|
79
+ klass.delete_all(construct_join_attributes(associate))
80
+ end
81
+ end
82
+
83
+ def find_target
84
+ return [] unless target_reflection_has_associated_record?
85
+ with_scope(construct_scope) { @reflection.klass.find(:all) }
86
+ end
87
+
88
+ def construct_sql
89
+ case
90
+ when @reflection.options[:finder_sql]
91
+ @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
92
+
93
+ @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
94
+ @finder_sql << " AND (#{conditions})" if conditions
95
+ else
96
+ @finder_sql = construct_conditions
97
+ end
98
+
99
+ construct_counter_sql
100
+ end
101
+
102
+ def has_cached_counter?
103
+ @owner.attribute_present?(cached_counter_attribute_name)
104
+ end
105
+
106
+ def cached_counter_attribute_name
107
+ "#{@reflection.name}_count"
108
+ end
109
+
110
+ # NOTE - not sure that we can actually cope with inverses here
111
+ def we_can_set_the_inverse_on_this?(record)
112
+ false
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,143 @@
1
+ module ActiveRecord
2
+ # = Active Record Belongs To Has One Association
3
+ module Associations
4
+ class HasOneAssociation < AssociationProxy #:nodoc:
5
+ def initialize(owner, reflection)
6
+ super
7
+ construct_sql
8
+ end
9
+
10
+ def create(attrs = {}, replace_existing = true)
11
+ new_record(replace_existing) do |reflection|
12
+ attrs = merge_with_conditions(attrs)
13
+ reflection.create_association(attrs)
14
+ end
15
+ end
16
+
17
+ def create!(attrs = {}, replace_existing = true)
18
+ new_record(replace_existing) do |reflection|
19
+ attrs = merge_with_conditions(attrs)
20
+ reflection.create_association!(attrs)
21
+ end
22
+ end
23
+
24
+ def build(attrs = {}, replace_existing = true)
25
+ new_record(replace_existing) do |reflection|
26
+ attrs = merge_with_conditions(attrs)
27
+ reflection.build_association(attrs)
28
+ end
29
+ end
30
+
31
+ def replace(obj, dont_save = false)
32
+ load_target
33
+
34
+ unless @target.nil? || @target == obj
35
+ if dependent? && !dont_save
36
+ case @reflection.options[:dependent]
37
+ when :delete
38
+ @target.delete unless @target.new_record?
39
+ @owner.clear_association_cache
40
+ when :destroy
41
+ @target.destroy unless @target.new_record?
42
+ @owner.clear_association_cache
43
+ when :nullify
44
+ @target[@reflection.primary_key_name] = nil
45
+ @target.save unless @owner.new_record? || @target.new_record?
46
+ end
47
+ else
48
+ @target[@reflection.primary_key_name] = nil
49
+ @target.save unless @owner.new_record? || @target.new_record?
50
+ end
51
+ end
52
+
53
+ if obj.nil?
54
+ @target = nil
55
+ else
56
+ raise_on_type_mismatch(obj)
57
+ set_belongs_to_association_for(obj)
58
+ @target = (AssociationProxy === obj ? obj.target : obj)
59
+ end
60
+
61
+ set_inverse_instance(obj, @owner)
62
+ @loaded = true
63
+
64
+ unless @owner.new_record? or obj.nil? or dont_save
65
+ return (obj.save ? self : false)
66
+ else
67
+ return (obj.nil? ? nil : self)
68
+ end
69
+ end
70
+
71
+ protected
72
+ def owner_quoted_id
73
+ if @reflection.options[:primary_key]
74
+ @owner.class.quote_value(@owner.send(@reflection.options[:primary_key]))
75
+ else
76
+ @owner.quoted_id
77
+ end
78
+ end
79
+
80
+ private
81
+ def find_target
82
+ options = @reflection.options.dup
83
+ (options.keys - [:select, :order, :include, :readonly]).each do |key|
84
+ options.delete key
85
+ end
86
+ options[:conditions] = @finder_sql
87
+
88
+ the_target = @reflection.klass.find(:first, options)
89
+ set_inverse_instance(the_target, @owner)
90
+ the_target
91
+ end
92
+
93
+ def construct_sql
94
+ case
95
+ when @reflection.options[:as]
96
+ @finder_sql =
97
+ "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
98
+ "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
99
+ else
100
+ @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
101
+ end
102
+ @finder_sql << " AND (#{conditions})" if conditions
103
+ end
104
+
105
+ def construct_scope
106
+ create_scoping = {}
107
+ set_belongs_to_association_for(create_scoping)
108
+ { :create => create_scoping }
109
+ end
110
+
111
+ def new_record(replace_existing)
112
+ # Make sure we load the target first, if we plan on replacing the existing
113
+ # instance. Otherwise, if the target has not previously been loaded
114
+ # elsewhere, the instance we create will get orphaned.
115
+ load_target if replace_existing
116
+ record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
117
+ yield @reflection
118
+ end
119
+
120
+ if replace_existing
121
+ replace(record, true)
122
+ else
123
+ record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
124
+ self.target = record
125
+ set_inverse_instance(record, @owner)
126
+ end
127
+
128
+ record
129
+ end
130
+
131
+ def we_can_set_the_inverse_on_this?(record)
132
+ inverse = @reflection.inverse_of
133
+ return !inverse.nil?
134
+ end
135
+
136
+ def merge_with_conditions(attrs={})
137
+ attrs ||= {}
138
+ attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
139
+ attrs
140
+ end
141
+ end
142
+ end
143
+ end