square-activerecord 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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