activerecord 1.13.2 → 1.14.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 (144) hide show
  1. data/CHANGELOG +452 -10
  2. data/RUNNING_UNIT_TESTS +1 -1
  3. data/lib/active_record.rb +5 -2
  4. data/lib/active_record/acts/list.rb +1 -1
  5. data/lib/active_record/acts/tree.rb +29 -25
  6. data/lib/active_record/aggregations.rb +3 -2
  7. data/lib/active_record/associations.rb +783 -337
  8. data/lib/active_record/associations/association_collection.rb +7 -12
  9. data/lib/active_record/associations/association_proxy.rb +62 -24
  10. data/lib/active_record/associations/belongs_to_association.rb +27 -46
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  12. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +38 -38
  13. data/lib/active_record/associations/has_many_association.rb +61 -56
  14. data/lib/active_record/associations/has_many_through_association.rb +144 -0
  15. data/lib/active_record/associations/has_one_association.rb +22 -16
  16. data/lib/active_record/base.rb +482 -182
  17. data/lib/active_record/calculations.rb +225 -0
  18. data/lib/active_record/callbacks.rb +7 -7
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +162 -47
  20. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -1
  22. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -1
  23. data/lib/active_record/connection_adapters/abstract_adapter.rb +34 -2
  24. data/lib/active_record/connection_adapters/db2_adapter.rb +107 -61
  25. data/lib/active_record/connection_adapters/mysql_adapter.rb +29 -6
  26. data/lib/active_record/connection_adapters/openbase_adapter.rb +349 -0
  27. data/lib/active_record/connection_adapters/{oci_adapter.rb → oracle_adapter.rb} +125 -59
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +24 -21
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +47 -8
  30. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +36 -16
  31. data/lib/active_record/connection_adapters/sybase_adapter.rb +684 -0
  32. data/lib/active_record/fixtures.rb +42 -17
  33. data/lib/active_record/locking.rb +36 -15
  34. data/lib/active_record/migration.rb +111 -8
  35. data/lib/active_record/observer.rb +25 -1
  36. data/lib/active_record/reflection.rb +103 -41
  37. data/lib/active_record/schema.rb +2 -2
  38. data/lib/active_record/schema_dumper.rb +55 -18
  39. data/lib/active_record/timestamp.rb +6 -6
  40. data/lib/active_record/validations.rb +65 -40
  41. data/lib/active_record/vendor/db2.rb +10 -5
  42. data/lib/active_record/vendor/simple.rb +693 -702
  43. data/lib/active_record/version.rb +2 -2
  44. data/rakefile +4 -4
  45. data/test/aaa_create_tables_test.rb +25 -6
  46. data/test/abstract_unit.rb +39 -1
  47. data/test/adapter_test.rb +31 -4
  48. data/test/associations_cascaded_eager_loading_test.rb +106 -0
  49. data/test/associations_go_eager_test.rb +85 -16
  50. data/test/associations_join_model_test.rb +338 -0
  51. data/test/associations_test.rb +129 -50
  52. data/test/base_test.rb +204 -49
  53. data/test/binary_test.rb +1 -1
  54. data/test/calculations_test.rb +169 -0
  55. data/test/callbacks_test.rb +5 -23
  56. data/test/class_inheritable_attributes_test.rb +1 -1
  57. data/test/column_alias_test.rb +1 -1
  58. data/test/connections/native_mysql/connection.rb +1 -0
  59. data/test/connections/native_openbase/connection.rb +22 -0
  60. data/test/connections/{native_oci → native_oracle}/connection.rb +7 -9
  61. data/test/connections/native_sqlite/connection.rb +1 -1
  62. data/test/connections/native_sqlite3/connection.rb +1 -0
  63. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -0
  64. data/test/connections/native_sybase/connection.rb +24 -0
  65. data/test/defaults_test.rb +18 -0
  66. data/test/deprecated_associations_test.rb +2 -2
  67. data/test/deprecated_finder_test.rb +0 -6
  68. data/test/finder_test.rb +26 -23
  69. data/test/fixtures/accounts.yml +10 -0
  70. data/test/fixtures/author.rb +31 -6
  71. data/test/fixtures/author_favorites.yml +4 -0
  72. data/test/fixtures/categories/special_categories.yml +9 -0
  73. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  74. data/test/fixtures/categories_posts.yml +4 -0
  75. data/test/fixtures/categorization.rb +5 -0
  76. data/test/fixtures/categorizations.yml +11 -0
  77. data/test/fixtures/category.rb +6 -0
  78. data/test/fixtures/company.rb +17 -5
  79. data/test/fixtures/company_in_module.rb +19 -5
  80. data/test/fixtures/db_definitions/db2.drop.sql +3 -0
  81. data/test/fixtures/db_definitions/db2.sql +121 -100
  82. data/test/fixtures/db_definitions/db22.sql +2 -2
  83. data/test/fixtures/db_definitions/firebird.drop.sql +4 -0
  84. data/test/fixtures/db_definitions/firebird.sql +26 -0
  85. data/test/fixtures/db_definitions/mysql.drop.sql +3 -0
  86. data/test/fixtures/db_definitions/mysql.sql +21 -1
  87. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  88. data/test/fixtures/db_definitions/openbase.sql +282 -0
  89. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  90. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  91. data/test/fixtures/db_definitions/{oci.drop.sql → oracle.drop.sql} +6 -0
  92. data/test/fixtures/db_definitions/{oci.sql → oracle.sql} +25 -4
  93. data/test/fixtures/db_definitions/{oci2.drop.sql → oracle2.drop.sql} +0 -0
  94. data/test/fixtures/db_definitions/{oci2.sql → oracle2.sql} +0 -0
  95. data/test/fixtures/db_definitions/postgresql.drop.sql +4 -0
  96. data/test/fixtures/db_definitions/postgresql.sql +22 -1
  97. data/test/fixtures/db_definitions/schema.rb +32 -0
  98. data/test/fixtures/db_definitions/sqlite.drop.sql +3 -0
  99. data/test/fixtures/db_definitions/sqlite.sql +18 -0
  100. data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
  101. data/test/fixtures/db_definitions/sqlserver.sql +23 -3
  102. data/test/fixtures/db_definitions/sybase.drop.sql +31 -0
  103. data/test/fixtures/db_definitions/sybase.sql +204 -0
  104. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  106. data/test/fixtures/developers.yml +6 -1
  107. data/test/fixtures/developers_projects.yml +4 -0
  108. data/test/fixtures/funny_jokes.yml +14 -0
  109. data/test/fixtures/joke.rb +6 -0
  110. data/test/fixtures/legacy_thing.rb +3 -0
  111. data/test/fixtures/legacy_things.yml +3 -0
  112. data/test/fixtures/mixin.rb +1 -1
  113. data/test/fixtures/person.rb +4 -1
  114. data/test/fixtures/post.rb +26 -1
  115. data/test/fixtures/project.rb +1 -0
  116. data/test/fixtures/reader.rb +4 -0
  117. data/test/fixtures/readers.yml +4 -0
  118. data/test/fixtures/reply.rb +2 -1
  119. data/test/fixtures/tag.rb +5 -0
  120. data/test/fixtures/tagging.rb +6 -0
  121. data/test/fixtures/taggings.yml +18 -0
  122. data/test/fixtures/tags.yml +7 -0
  123. data/test/fixtures/tasks.yml +2 -2
  124. data/test/fixtures/topic.rb +2 -2
  125. data/test/fixtures/topics.yml +1 -0
  126. data/test/fixtures_test.rb +47 -13
  127. data/test/inheritance_test.rb +2 -2
  128. data/test/locking_test.rb +15 -1
  129. data/test/method_scoping_test.rb +248 -13
  130. data/test/migration_test.rb +68 -11
  131. data/test/mixin_nested_set_test.rb +1 -1
  132. data/test/modules_test.rb +6 -1
  133. data/test/readonly_test.rb +1 -1
  134. data/test/reflection_test.rb +63 -9
  135. data/test/schema_dumper_test.rb +41 -0
  136. data/test/{synonym_test_oci.rb → synonym_test_oracle.rb} +1 -1
  137. data/test/threaded_connections_test.rb +10 -0
  138. data/test/unconnected_test.rb +12 -5
  139. data/test/validations_test.rb +197 -10
  140. metadata +295 -260
  141. data/test/fixtures/db_definitions/create_oracle_db.bat +0 -0
  142. data/test/fixtures/db_definitions/create_oracle_db.sh +0 -0
  143. data/test/fixtures/fixture_database.sqlite +0 -0
  144. data/test/fixtures/fixture_database_2.sqlite +0 -0
@@ -18,6 +18,7 @@ module ActiveRecord
18
18
  def <<(*records)
19
19
  result = true
20
20
  load_target
21
+
21
22
  @owner.transaction do
22
23
  flatten_deeper(records).each do |record|
23
24
  raise_on_type_mismatch(record)
@@ -28,7 +29,7 @@ module ActiveRecord
28
29
  end
29
30
  end
30
31
 
31
- result and self
32
+ result && self
32
33
  end
33
34
 
34
35
  alias_method :push, :<<
@@ -60,11 +61,13 @@ module ActiveRecord
60
61
  # Removes all records from this association. Returns +self+ so method calls may be chained.
61
62
  def clear
62
63
  return self if length.zero? # forces load_target if hasn't happened already
63
- if @options[:exclusively_dependent]
64
+
65
+ if @reflection.options[:dependent] && @reflection.options[:dependent] == :delete_all
64
66
  destroy_all
65
67
  else
66
68
  delete_all
67
69
  end
70
+
68
71
  self
69
72
  end
70
73
 
@@ -124,14 +127,6 @@ module ActiveRecord
124
127
  end
125
128
 
126
129
  private
127
- def raise_on_type_mismatch(record)
128
- raise ActiveRecord::AssociationTypeMismatch, "#{@association_class} expected, got #{record.class}" unless record.is_a?(@association_class)
129
- end
130
-
131
- def target_obsolete?
132
- false
133
- end
134
-
135
130
  # Array#flatten has problems with recursive arrays. Going one level deeper solves the majority of the problems.
136
131
  def flatten_deeper(array)
137
132
  array.collect { |element| element.respond_to?(:flatten) ? element.flatten : element }.flatten
@@ -155,8 +150,8 @@ module ActiveRecord
155
150
  end
156
151
 
157
152
  def callbacks_for(callback_name)
158
- full_callback_name = "#{callback_name.to_s}_for_#{@association_name.to_s}"
159
- @owner.class.read_inheritable_attribute(full_callback_name.to_sym) or []
153
+ full_callback_name = "#{callback_name}_for_#{@reflection.name}"
154
+ @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
160
155
  end
161
156
 
162
157
  end
@@ -1,29 +1,45 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class AssociationProxy #:nodoc:
4
+ attr_reader :reflection
4
5
  alias_method :proxy_respond_to?, :respond_to?
5
6
  alias_method :proxy_extend, :extend
6
7
  instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?|^proxy_respond_to\?|^proxy_extend|^send)/ }
7
8
 
8
- def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options)
9
- @owner = owner
10
- @options = options
11
- @association_name = association_name
12
- @association_class = eval(association_class_name, nil, __FILE__, __LINE__)
13
- @association_class_primary_key_name = association_class_primary_key_name
14
-
15
- proxy_extend(options[:extend]) if options[:extend]
16
-
9
+ def initialize(owner, reflection)
10
+ @owner, @reflection = owner, reflection
11
+ proxy_extend(reflection.options[:extend]) if reflection.options[:extend]
17
12
  reset
18
13
  end
19
14
 
20
- def reload
21
- reset
15
+ def respond_to?(symbol, include_priv = false)
16
+ proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
17
+ end
18
+
19
+ # Explicitly proxy === because the instance method removal above
20
+ # doesn't catch it.
21
+ def ===(other)
22
22
  load_target
23
+ other === @target
24
+ end
25
+
26
+ def aliased_table_name
27
+ @reflection.klass.table_name
28
+ end
29
+
30
+ def conditions
31
+ @conditions ||= eval("%(#{@reflection.active_record.send :sanitize_sql, @reflection.options[:conditions]})") if @reflection.options[:conditions]
32
+ end
33
+ alias :sql_conditions :conditions
34
+
35
+ def reset
36
+ @target = nil
37
+ @loaded = false
23
38
  end
24
39
 
25
- def respond_to?(symbol, include_priv = false)
26
- proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
40
+ def reload
41
+ reset
42
+ load_target
27
43
  end
28
44
 
29
45
  def loaded?
@@ -38,14 +54,14 @@ module ActiveRecord
38
54
  @target
39
55
  end
40
56
 
41
- def target=(t)
42
- @target = t
43
- @loaded = true
57
+ def target=(target)
58
+ @target = target
59
+ loaded
44
60
  end
45
61
 
46
62
  protected
47
63
  def dependent?
48
- @options[:dependent] || false
64
+ @reflection.options[:dependent] || false
49
65
  end
50
66
 
51
67
  def quoted_record_ids(records)
@@ -61,15 +77,34 @@ module ActiveRecord
61
77
  end
62
78
 
63
79
  def sanitize_sql(sql)
64
- @association_class.send(:sanitize_sql, sql)
80
+ @reflection.klass.send(:sanitize_sql, sql)
65
81
  end
66
82
 
67
83
  def extract_options_from_args!(args)
68
84
  @owner.send(:extract_options_from_args!, args)
69
85
  end
86
+
87
+ def set_belongs_to_association_for(record)
88
+ if @reflection.options[:as]
89
+ record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
90
+ record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
91
+ else
92
+ record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
93
+ end
94
+ end
95
+
96
+ def merge_options_from_reflection!(options)
97
+ options.reverse_merge!(
98
+ :group => @reflection.options[:group],
99
+ :limit => @reflection.options[:limit],
100
+ :offset => @reflection.options[:offset],
101
+ :joins => @reflection.options[:joins],
102
+ :include => @reflection.options[:include],
103
+ :select => @reflection.options[:select]
104
+ )
105
+ end
70
106
 
71
107
  private
72
-
73
108
  def method_missing(method, *args, &block)
74
109
  load_target
75
110
  @target.send(method, *args, &block)
@@ -78,13 +113,14 @@ module ActiveRecord
78
113
  def load_target
79
114
  if !@owner.new_record? || foreign_key_present
80
115
  begin
81
- @target = find_target if not loaded?
116
+ @target = find_target if !loaded?
82
117
  rescue ActiveRecord::RecordNotFound
83
118
  reset
84
119
  end
85
120
  end
86
- @loaded = true if @target
87
- @target
121
+
122
+ loaded if target
123
+ target
88
124
  end
89
125
 
90
126
  # Can be overwritten by associations that might have the foreign key available for an association without
@@ -94,8 +130,10 @@ module ActiveRecord
94
130
  end
95
131
 
96
132
  def raise_on_type_mismatch(record)
97
- raise ActiveRecord::AssociationTypeMismatch, "#{@association_class} expected, got #{record.class}" unless record.is_a?(@association_class)
133
+ unless record.is_a?(@reflection.klass)
134
+ raise ActiveRecord::AssociationTypeMismatch, "#{@reflection.class_name} expected, got #{record.class}"
135
+ end
98
136
  end
99
137
  end
100
138
  end
101
- end
139
+ end
@@ -1,74 +1,55 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class BelongsToAssociation < AssociationProxy #:nodoc:
4
-
5
- def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options)
6
- super
7
- construct_sql
8
- end
9
-
10
- def reset
11
- @target = nil
12
- @loaded = false
13
- end
14
-
15
4
  def create(attributes = {})
16
- record = @association_class.create(attributes)
17
- replace(record, true)
18
- record
5
+ replace(@reflection.klass.create(attributes))
19
6
  end
20
7
 
21
8
  def build(attributes = {})
22
- record = @association_class.new(attributes)
23
- replace(record, true)
24
- record
9
+ replace(@reflection.klass.new(attributes))
25
10
  end
26
11
 
27
- def replace(obj, dont_save = false)
28
- if obj.nil?
29
- @target = @owner[@association_class_primary_key_name] = nil
12
+ def replace(record)
13
+ counter_cache_name = @reflection.counter_cache_column
14
+
15
+ if record.nil?
16
+ if counter_cache_name && @owner[counter_cache_name] && !@owner.new_record?
17
+ @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
18
+ end
19
+
20
+ @target = @owner[@reflection.primary_key_name] = nil
30
21
  else
31
- raise_on_type_mismatch(obj) unless obj.nil?
22
+ raise_on_type_mismatch(record)
32
23
 
33
- @target = (AssociationProxy === obj ? obj.target : obj)
34
- @owner[@association_class_primary_key_name] = obj.id unless obj.new_record?
24
+ if counter_cache_name && !@owner.new_record?
25
+ @reflection.klass.increment_counter(counter_cache_name, record.id)
26
+ @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
27
+ end
28
+
29
+ @target = (AssociationProxy === record ? record.target : record)
30
+ @owner[@reflection.primary_key_name] = record.id unless record.new_record?
35
31
  @updated = true
36
32
  end
37
- @loaded = true
38
33
 
39
- return (@target.nil? ? nil : self)
34
+ loaded
35
+ record
40
36
  end
41
37
 
42
38
  def updated?
43
39
  @updated
44
40
  end
45
41
 
46
- protected
47
-
48
-
49
42
  private
50
43
  def find_target
51
- if @options[:conditions]
52
- @association_class.find(
53
- @owner[@association_class_primary_key_name],
54
- :conditions => interpolate_sql(@options[:conditions]),
55
- :include => @options[:include]
56
- )
57
- else
58
- @association_class.find(@owner[@association_class_primary_key_name], :include => @options[:include])
59
- end
44
+ @reflection.klass.find(
45
+ @owner[@reflection.primary_key_name],
46
+ :conditions => conditions,
47
+ :include => @reflection.options[:include]
48
+ )
60
49
  end
61
50
 
62
51
  def foreign_key_present
63
- !@owner[@association_class_primary_key_name].nil?
64
- end
65
-
66
- def target_obsolete?
67
- @owner[@association_class_primary_key_name] != @target.id
68
- end
69
-
70
- def construct_sql
71
- @finder_sql = "#{@association_class.table_name}.#{@association_class.primary_key} = #{@owner.id}"
52
+ !@owner[@reflection.primary_key_name].nil?
72
53
  end
73
54
  end
74
55
  end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class BelongsToPolymorphicAssociation < AssociationProxy #:nodoc:
4
+ def replace(record)
5
+ if record.nil?
6
+ @target = @owner[@reflection.primary_key_name] = @owner[@reflection.options[:foreign_type]] = nil
7
+ else
8
+ @target = (AssociationProxy === record ? record.target : record)
9
+
10
+ unless record.new_record?
11
+ @owner[@reflection.primary_key_name] = record.id
12
+ @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
13
+ end
14
+
15
+ @updated = true
16
+ end
17
+
18
+ loaded
19
+ record
20
+ end
21
+
22
+ def updated?
23
+ @updated
24
+ end
25
+
26
+ private
27
+ def find_target
28
+ return nil if association_class.nil?
29
+
30
+ if @reflection.options[:conditions]
31
+ association_class.find(
32
+ @owner[@reflection.primary_key_name],
33
+ :conditions => conditions,
34
+ :include => @reflection.options[:include]
35
+ )
36
+ else
37
+ association_class.find(@owner[@reflection.primary_key_name], :include => @reflection.options[:include])
38
+ end
39
+ end
40
+
41
+ def foreign_key_present
42
+ !@owner[@reflection.primary_key_name].nil?
43
+ end
44
+
45
+ def association_class
46
+ @owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,20 +1,14 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
4
- def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options)
4
+ def initialize(owner, reflection)
5
5
  super
6
-
7
- @association_foreign_key = options[:association_foreign_key] || association_class_name.foreign_key
8
- @association_table_name = options[:table_name] || @association_class.table_name
9
- @join_table = options[:join_table]
10
- @order = options[:order]
11
-
12
6
  construct_sql
13
7
  end
14
8
 
15
9
  def build(attributes = {})
16
10
  load_target
17
- record = @association_class.new(attributes)
11
+ record = @reflection.klass.new(attributes)
18
12
  @target << record
19
13
  record
20
14
  end
@@ -27,7 +21,7 @@ module ActiveRecord
27
21
  options = Base.send(:extract_options_from_args!, args)
28
22
 
29
23
  # If using a custom finder_sql, scan the entire collection.
30
- if @options[:finder_sql]
24
+ if @reflection.options[:finder_sql]
31
25
  expects_array = args.first.kind_of?(Array)
32
26
  ids = args.flatten.compact.uniq
33
27
 
@@ -40,60 +34,66 @@ module ActiveRecord
40
34
  end
41
35
  else
42
36
  conditions = "#{@finder_sql}"
37
+
43
38
  if sanitized_conditions = sanitize_sql(options[:conditions])
44
39
  conditions << " AND (#{sanitized_conditions})"
45
40
  end
41
+
46
42
  options[:conditions] = conditions
47
43
  options[:joins] = @join_sql
48
44
  options[:readonly] ||= false
49
45
 
50
- if options[:order] && @options[:order]
51
- options[:order] = "#{options[:order]}, #{@options[:order]}"
52
- elsif @options[:order]
53
- options[:order] = @options[:order]
46
+ if options[:order] && @reflection.options[:order]
47
+ options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
48
+ elsif @reflection.options[:order]
49
+ options[:order] = @reflection.options[:order]
54
50
  end
55
51
 
52
+ merge_options_from_reflection!(options)
53
+
56
54
  # Pass through args exactly as we received them.
57
55
  args << options
58
- @association_class.find(*args)
56
+ @reflection.klass.find(*args)
59
57
  end
60
58
  end
61
59
 
62
60
  def push_with_attributes(record, join_attributes = {})
63
61
  raise_on_type_mismatch(record)
64
62
  join_attributes.each { |key, value| record[key.to_s] = value }
63
+
65
64
  callback(:before_add, record)
66
65
  insert_record(record) unless @owner.new_record?
67
66
  @target << record
68
67
  callback(:after_add, record)
68
+
69
69
  self
70
70
  end
71
71
 
72
72
  alias :concat_with_attributes :push_with_attributes
73
73
 
74
74
  def size
75
- @options[:uniq] ? count_records : super
75
+ @reflection.options[:uniq] ? count_records : super
76
76
  end
77
77
 
78
78
  protected
79
79
  def method_missing(method, *args, &block)
80
- if @target.respond_to?(method) || (!@association_class.respond_to?(method) && Class.respond_to?(method))
80
+ if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
81
81
  super
82
82
  else
83
- @association_class.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
84
- @association_class.send(method, *args, &block)
83
+ @reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
84
+ @reflection.klass.send(method, *args, &block)
85
85
  end
86
86
  end
87
87
  end
88
88
 
89
89
  def find_target
90
- if @options[:finder_sql]
91
- records = @association_class.find_by_sql(@finder_sql)
90
+ if @reflection.options[:finder_sql]
91
+ records = @reflection.klass.find_by_sql(@finder_sql)
92
92
  else
93
- records = find(:all, :include => @options[:include])
93
+ records = find(:all)
94
94
  end
95
95
 
96
- @options[:uniq] ? uniq(records) : records
96
+ @reflection.options[:uniq] ? uniq(records) : records
97
97
  end
98
98
 
99
99
  def count_records
@@ -105,16 +105,16 @@ module ActiveRecord
105
105
  return false unless record.save
106
106
  end
107
107
 
108
- if @options[:insert_sql]
109
- @owner.connection.execute(interpolate_sql(@options[:insert_sql], record))
108
+ if @reflection.options[:insert_sql]
109
+ @owner.connection.execute(interpolate_sql(@reflection.options[:insert_sql], record))
110
110
  else
111
- columns = @owner.connection.columns(@join_table, "#{@join_table} Columns")
111
+ columns = @owner.connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
112
112
 
113
113
  attributes = columns.inject({}) do |attributes, column|
114
114
  case column.name
115
- when @association_class_primary_key_name
115
+ when @reflection.primary_key_name
116
116
  attributes[column.name] = @owner.quoted_id
117
- when @association_foreign_key
117
+ when @reflection.association_foreign_key
118
118
  attributes[column.name] = record.quoted_id
119
119
  else
120
120
  if record.attributes.has_key?(column.name)
@@ -126,7 +126,7 @@ module ActiveRecord
126
126
  end
127
127
 
128
128
  sql =
129
- "INSERT INTO #{@join_table} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
129
+ "INSERT INTO #{@reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
130
130
  "VALUES (#{attributes.values.join(', ')})"
131
131
 
132
132
  @owner.connection.execute(sql)
@@ -136,26 +136,26 @@ module ActiveRecord
136
136
  end
137
137
 
138
138
  def delete_records(records)
139
- if sql = @options[:delete_sql]
139
+ if sql = @reflection.options[:delete_sql]
140
140
  records.each { |record| @owner.connection.execute(interpolate_sql(sql, record)) }
141
141
  else
142
142
  ids = quoted_record_ids(records)
143
- sql = "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} = #{@owner.quoted_id} AND #{@association_foreign_key} IN (#{ids})"
143
+ sql = "DELETE FROM #{@reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
144
144
  @owner.connection.execute(sql)
145
145
  end
146
146
  end
147
-
147
+
148
148
  def construct_sql
149
- interpolate_sql_options!(@options, :finder_sql)
149
+ interpolate_sql_options!(@reflection.options, :finder_sql)
150
150
 
151
- if @options[:finder_sql]
152
- @finder_sql = @options[:finder_sql]
151
+ if @reflection.options[:finder_sql]
152
+ @finder_sql = @reflection.options[:finder_sql]
153
153
  else
154
- @finder_sql = "#{@join_table}.#{@association_class_primary_key_name} = #{@owner.quoted_id} "
155
- @finder_sql << " AND (#{interpolate_sql(@options[:conditions])})" if @options[:conditions]
154
+ @finder_sql = "#{@reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} "
155
+ @finder_sql << " AND (#{conditions})" if conditions
156
156
  end
157
-
158
- @join_sql = "LEFT JOIN #{@join_table} ON #{@association_class.table_name}.#{@association_class.primary_key} = #{@join_table}.#{@association_foreign_key}"
157
+
158
+ @join_sql = "INNER JOIN #{@reflection.options[:join_table]} ON #{@reflection.klass.table_name}.#{@reflection.klass.primary_key} = #{@reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
159
159
  end
160
160
 
161
161
  end