square-activerecord 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -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 +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -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 +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -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 +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -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 +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -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 +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -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 +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -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 +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -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 +359 -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 +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,143 @@
1
+ require 'active_support/deprecation'
2
+
3
+ module ActiveRecord
4
+ # = Active Record Has And Belongs To Many Association
5
+ module Associations
6
+ class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
7
+ def initialize(owner, reflection)
8
+ super
9
+ if columns.size > 2
10
+ ActiveSupport::Deprecation.warn "Having additional attributes on the join table of a has_and_belongs_to_many association is deprecated and will be removed in Rails 3.1. Please use a has_many :through association instead."
11
+ end
12
+ end
13
+
14
+ def create(attributes = {})
15
+ create_record(attributes) { |record| insert_record(record) }
16
+ end
17
+
18
+ def create!(attributes = {})
19
+ create_record(attributes) { |record| insert_record(record, true) }
20
+ end
21
+
22
+ def columns
23
+ @reflection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
24
+ end
25
+
26
+ def reset_column_information
27
+ @reflection.reset_column_information
28
+ end
29
+
30
+ def has_primary_key?
31
+ @has_primary_key ||= @owner.connection.supports_primary_key? && @owner.connection.primary_key(@reflection.options[:join_table])
32
+ end
33
+
34
+ protected
35
+ def construct_find_options!(options)
36
+ options[:joins] = Arel::SqlLiteral.new @join_sql
37
+ options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
38
+ options[:select] ||= (@reflection.options[:select] || Arel::SqlLiteral.new('*'))
39
+ end
40
+
41
+ def count_records
42
+ load_target.size
43
+ end
44
+
45
+ def insert_record(record, force = true, validate = true)
46
+ if record.new_record?
47
+ if force
48
+ record.save!
49
+ else
50
+ return false unless record.save(:validate => validate)
51
+ end
52
+ end
53
+
54
+ if @reflection.options[:insert_sql]
55
+ @owner.connection.insert(interpolate_and_sanitize_sql(@reflection.options[:insert_sql], record))
56
+ else
57
+ relation = Arel::Table.new(@reflection.options[:join_table])
58
+ timestamps = record_timestamp_columns(record)
59
+ timezone = record.send(:current_time_from_proper_timezone) if timestamps.any?
60
+
61
+ attributes = Hash[columns.map do |column|
62
+ name = column.name
63
+ value = case name.to_s
64
+ when @reflection.primary_key_name.to_s
65
+ @owner.id
66
+ when @reflection.association_foreign_key.to_s
67
+ record.id
68
+ when *timestamps
69
+ timezone
70
+ else
71
+ @owner.send(:quote_value, record[name], column) if record.has_attribute?(name)
72
+ end
73
+ [relation[name], value] unless value.nil?
74
+ end]
75
+
76
+ relation.insert(attributes)
77
+ end
78
+
79
+ return true
80
+ end
81
+
82
+ def delete_records(records)
83
+ if sql = @reflection.options[:delete_sql]
84
+ records.each { |record| @owner.connection.delete(interpolate_and_sanitize_sql(sql, record)) }
85
+ else
86
+ relation = Arel::Table.new(@reflection.options[:join_table])
87
+ relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
88
+ and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
89
+ ).delete
90
+ end
91
+ end
92
+
93
+ def construct_sql
94
+ if @reflection.options[:finder_sql]
95
+ @finder_sql = interpolate_and_sanitize_sql(@reflection.options[:finder_sql])
96
+ else
97
+ @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} "
98
+ @finder_sql << " AND (#{conditions})" if conditions
99
+ end
100
+
101
+ @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}"
102
+
103
+ construct_counter_sql
104
+ end
105
+
106
+ def construct_scope
107
+ { :find => { :conditions => @finder_sql,
108
+ :joins => @join_sql,
109
+ :readonly => false,
110
+ :order => @reflection.options[:order],
111
+ :include => @reflection.options[:include],
112
+ :limit => @reflection.options[:limit] } }
113
+ end
114
+
115
+ # Join tables with additional columns on top of the two foreign keys must be considered
116
+ # ambiguous unless a select clause has been explicitly defined. Otherwise you can get
117
+ # broken records back, if, for example, the join column also has an id column. This will
118
+ # then overwrite the id column of the records coming back.
119
+ def finding_with_ambiguous_select?(select_clause)
120
+ !select_clause && columns.size != 2
121
+ end
122
+
123
+ private
124
+ def create_record(attributes, &block)
125
+ # Can't use Base.create because the foreign key may be a protected attribute.
126
+ ensure_owner_is_not_new
127
+ if attributes.is_a?(Array)
128
+ attributes.collect { |attr| create(attr) }
129
+ else
130
+ build_record(attributes, &block)
131
+ end
132
+ end
133
+
134
+ def record_timestamp_columns(record)
135
+ if record.record_timestamps
136
+ record.send(:all_timestamp_attributes).map { |x| x.to_s }
137
+ else
138
+ []
139
+ end
140
+ end
141
+ end
142
+ end
143
+ 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[:finder_sql] || @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_and_sanitize_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,115 @@
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_association.create!(construct_join_attributes(record))
72
+ end
73
+
74
+ # TODO - add dependent option support
75
+ def delete_records(records)
76
+ klass = @reflection.through_reflection.klass
77
+ records.each do |associate|
78
+ klass.delete_all(construct_join_attributes(associate))
79
+ end
80
+ end
81
+
82
+ def find_target
83
+ return [] unless target_reflection_has_associated_record?
84
+ with_scope(construct_scope) { @reflection.klass.find(:all) }
85
+ end
86
+
87
+ def construct_sql
88
+ case
89
+ when @reflection.options[:finder_sql]
90
+ @finder_sql = interpolate_and_sanitize_sql(@reflection.options[:finder_sql])
91
+
92
+ @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
93
+ @finder_sql << " AND (#{conditions})" if conditions
94
+ else
95
+ @finder_sql = construct_conditions
96
+ end
97
+
98
+ construct_counter_sql
99
+ end
100
+
101
+ def has_cached_counter?
102
+ @owner.attribute_present?(cached_counter_attribute_name)
103
+ end
104
+
105
+ def cached_counter_attribute_name
106
+ "#{@reflection.name}_count"
107
+ end
108
+
109
+ # NOTE - not sure that we can actually cope with inverses here
110
+ def we_can_set_the_inverse_on_this?(record)
111
+ false
112
+ end
113
+ end
114
+ end
115
+ 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