activerecord 2.1.0 → 2.1.1
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.
- data/CHANGELOG +34 -0
- data/README +0 -0
- data/Rakefile +6 -5
- data/lib/active_record.rb +8 -10
- data/lib/active_record/association_preload.rb +17 -12
- data/lib/active_record/associations.rb +45 -27
- data/lib/active_record/associations/association_collection.rb +8 -5
- data/lib/active_record/associations/association_proxy.rb +2 -6
- data/lib/active_record/associations/belongs_to_association.rb +0 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -3
- data/lib/active_record/associations/has_many_association.rb +16 -4
- data/lib/active_record/associations/has_many_through_association.rb +1 -1
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/has_one_through_association.rb +4 -0
- data/lib/active_record/base.rb +33 -15
- data/lib/active_record/calculations.rb +20 -7
- data/lib/active_record/callbacks.rb +0 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +17 -10
- data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +53 -24
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -20
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +12 -0
- data/lib/active_record/dirty.rb +10 -3
- data/lib/active_record/fixtures.rb +0 -0
- data/lib/active_record/locking/optimistic.rb +1 -0
- data/lib/active_record/migration.rb +35 -8
- data/lib/active_record/named_scope.rb +6 -1
- data/lib/active_record/observer.rb +7 -5
- data/lib/active_record/test_case.rb +13 -2
- data/lib/active_record/validations.rb +19 -9
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_postgresql.rb +2 -2
- data/test/cases/adapter_test.rb +1 -1
- data/test/cases/associations/belongs_to_associations_test.rb +19 -0
- data/test/cases/associations/cascaded_eager_loading_test.rb +13 -1
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
- data/test/cases/associations/eager_test.rb +25 -1
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +27 -5
- data/test/cases/associations/has_many_associations_test.rb +106 -4
- data/test/cases/associations/has_many_through_associations_test.rb +10 -0
- data/test/cases/associations/has_one_associations_test.rb +22 -0
- data/test/cases/associations/has_one_through_associations_test.rb +44 -5
- data/test/cases/associations/join_model_test.rb +7 -0
- data/test/cases/associations_test.rb +2 -2
- data/test/cases/attribute_methods_test.rb +10 -10
- data/test/cases/base_test.rb +39 -16
- data/test/cases/calculations_test.rb +53 -1
- data/test/cases/column_definition_test.rb +36 -0
- data/test/cases/database_statements_test.rb +12 -0
- data/test/cases/defaults_test.rb +1 -1
- data/test/cases/deprecated_finder_test.rb +0 -0
- data/test/cases/dirty_test.rb +94 -0
- data/test/cases/finder_test.rb +7 -0
- data/test/cases/fixtures_test.rb +0 -0
- data/test/cases/helper.rb +5 -5
- data/test/cases/inheritance_test.rb +9 -2
- data/test/cases/lifecycle_test.rb +54 -1
- data/test/cases/locking_test.rb +20 -0
- data/test/cases/method_scoping_test.rb +11 -1
- data/test/cases/migration_test.rb +147 -22
- data/test/cases/multiple_db_test.rb +1 -1
- data/test/cases/named_scope_test.rb +50 -1
- data/test/cases/query_cache_test.rb +4 -3
- data/test/cases/readonly_test.rb +0 -0
- data/test/cases/reflection_test.rb +3 -3
- data/test/cases/schema_dumper_test.rb +46 -0
- data/test/cases/unconnected_test.rb +0 -0
- data/test/cases/validations_test.rb +30 -5
- data/test/debug.log +358 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/models/author.rb +4 -0
- data/test/models/category.rb +1 -0
- data/test/models/company.rb +10 -1
- data/test/models/developer.rb +4 -1
- data/test/models/person.rb +1 -1
- data/test/models/post.rb +6 -1
- data/test/models/project.rb +1 -1
- data/test/models/reply.rb +0 -0
- data/test/models/topic.rb +1 -0
- data/test/schema/mysql_specific_schema.rb +2 -2
- data/test/schema/schema.rb +8 -0
- metadata +11 -5
- data/lib/active_record/vendor/db2.rb +0 -362
data/CHANGELOG
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
*2.1.1 (September 4th, 2008)*
|
2
|
+
|
3
|
+
* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
|
4
|
+
|
5
|
+
* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]
|
6
|
+
|
7
|
+
* change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]
|
8
|
+
|
9
|
+
* Add :tokenizer option to validates_length_of to specify how to split up the attribute string. #507. [David Lowenfels] Example :
|
10
|
+
|
11
|
+
# Ensure essay contains at least 100 words.
|
12
|
+
validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
|
13
|
+
|
14
|
+
* Always treat integer :limit as byte length. #420 [Tarmo Tänav]
|
15
|
+
|
16
|
+
* Partial updates don't update lock_version if nothing changed. #426 [Daniel Morrison]
|
17
|
+
|
18
|
+
* Fix column collision with named_scope and :joins. #46 [Duncan Beevers, Mark Catley]
|
19
|
+
|
20
|
+
* db:migrate:down and :up update schema_migrations. #369 [Michael Raidel, RaceCondition]
|
21
|
+
|
22
|
+
* PostgreSQL: support :conditions => [':foo::integer', { :foo => 1 }] without treating the ::integer typecast as a bind variable. [Tarmo Tänav]
|
23
|
+
|
24
|
+
* MySQL: rename_column preserves column defaults. #466 [Diego Algorta]
|
25
|
+
|
26
|
+
* Add :from option to calculations. #397 [Ben Munat]
|
27
|
+
|
28
|
+
* Add :validate option to associations to enable/disable the automatic validation of associated models. Resolves #301. [Jan De Poorter]
|
29
|
+
|
30
|
+
* PostgreSQL: use 'INSERT ... RETURNING id' for 8.2 and later. [Jeremy Kemper]
|
31
|
+
|
32
|
+
* Added SQL escaping for :limit and :offset in MySQL [Jonathan Wiess]
|
33
|
+
|
34
|
+
|
1
35
|
*2.1.0 (May 31st, 2008)*
|
2
36
|
|
3
37
|
* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [rick]
|
data/README
CHANGED
File without changes
|
data/Rakefile
CHANGED
@@ -5,6 +5,7 @@ require 'rake/rdoctask'
|
|
5
5
|
require 'rake/packagetask'
|
6
6
|
require 'rake/gempackagetask'
|
7
7
|
require 'rake/contrib/sshpublisher'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
8
9
|
|
9
10
|
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
|
10
11
|
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
|
@@ -141,7 +142,7 @@ Rake::RDocTask.new { |rdoc|
|
|
141
142
|
rdoc.title = "Active Record -- Object-relation mapping put on rails"
|
142
143
|
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
143
144
|
rdoc.options << '--charset' << 'utf-8'
|
144
|
-
rdoc.template = "#{ENV['template']}.rb"
|
145
|
+
rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
|
145
146
|
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
|
146
147
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
147
148
|
rdoc.rdoc_files.exclude('lib/active_record/vendor/*')
|
@@ -171,7 +172,7 @@ spec = Gem::Specification.new do |s|
|
|
171
172
|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
172
173
|
end
|
173
174
|
|
174
|
-
s.add_dependency('activesupport', '= 2.1.
|
175
|
+
s.add_dependency('activesupport', '= 2.1.1' + PKG_BUILD)
|
175
176
|
|
176
177
|
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
|
177
178
|
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
|
@@ -225,13 +226,13 @@ end
|
|
225
226
|
|
226
227
|
desc "Publish the beta gem"
|
227
228
|
task :pgem => [:package] do
|
228
|
-
Rake::SshFilePublisher.new("
|
229
|
-
`ssh
|
229
|
+
Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
230
|
+
`ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
|
230
231
|
end
|
231
232
|
|
232
233
|
desc "Publish the API documentation"
|
233
234
|
task :pdoc => [:rdoc] do
|
234
|
-
Rake::SshDirPublisher.new("
|
235
|
+
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
|
235
236
|
end
|
236
237
|
|
237
238
|
desc "Publish the release files to RubyForge."
|
data/lib/active_record.rb
CHANGED
@@ -24,16 +24,14 @@
|
|
24
24
|
$:.unshift(File.dirname(__FILE__)) unless
|
25
25
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
require 'active_support'
|
36
|
-
end
|
27
|
+
active_support_path = File.dirname(__FILE__) + "/../../activesupport/lib"
|
28
|
+
if File.exist?(active_support_path)
|
29
|
+
$:.unshift active_support_path
|
30
|
+
require 'active_support'
|
31
|
+
else
|
32
|
+
require 'rubygems'
|
33
|
+
gem 'activesupport'
|
34
|
+
require 'active_support'
|
37
35
|
end
|
38
36
|
|
39
37
|
require 'active_record/base'
|
@@ -51,9 +51,7 @@ module ActiveRecord
|
|
51
51
|
|
52
52
|
def add_preloaded_record_to_collection(parent_records, reflection_name, associated_record)
|
53
53
|
parent_records.each do |parent_record|
|
54
|
-
|
55
|
-
association_proxy.loaded
|
56
|
-
association_proxy.target = associated_record
|
54
|
+
parent_record.send("set_#{reflection_name}_target", associated_record)
|
57
55
|
end
|
58
56
|
end
|
59
57
|
|
@@ -103,17 +101,17 @@ module ActiveRecord
|
|
103
101
|
associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
|
104
102
|
:include => options[:include],
|
105
103
|
:joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} as t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}",
|
106
|
-
:select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as
|
104
|
+
:select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id",
|
107
105
|
:order => options[:order])
|
108
106
|
|
109
|
-
set_association_collection_records(id_to_record_map, reflection.name, associated_records, '
|
107
|
+
set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id')
|
110
108
|
end
|
111
109
|
|
112
110
|
def preload_has_one_association(records, reflection, preload_options={})
|
113
111
|
id_to_record_map, ids = construct_id_map(records)
|
114
112
|
options = reflection.options
|
113
|
+
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
|
115
114
|
if options[:through]
|
116
|
-
records.each {|record| record.send(reflection.name) && record.send(reflection.name).loaded}
|
117
115
|
through_records = preload_through_records(records, reflection, options[:through])
|
118
116
|
through_reflection = reflections[options[:through]]
|
119
117
|
through_primary_key = through_reflection.primary_key_name
|
@@ -126,8 +124,6 @@ module ActiveRecord
|
|
126
124
|
end
|
127
125
|
end
|
128
126
|
else
|
129
|
-
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
|
130
|
-
|
131
127
|
set_association_single_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options), reflection.primary_key_name)
|
132
128
|
end
|
133
129
|
end
|
@@ -188,7 +184,6 @@ module ActiveRecord
|
|
188
184
|
through_records
|
189
185
|
end
|
190
186
|
|
191
|
-
# FIXME: quoting
|
192
187
|
def preload_belongs_to_association(records, reflection, preload_options={})
|
193
188
|
options = reflection.options
|
194
189
|
primary_key_name = reflection.primary_key_name
|
@@ -227,9 +222,19 @@ module ActiveRecord
|
|
227
222
|
|
228
223
|
table_name = klass.quoted_table_name
|
229
224
|
primary_key = klass.primary_key
|
230
|
-
conditions = "#{table_name}.#{primary_key} IN (?)"
|
225
|
+
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} IN (?)"
|
231
226
|
conditions << append_conditions(options, preload_options)
|
232
|
-
|
227
|
+
column_type = klass.columns.detect{|c| c.name == primary_key}.type
|
228
|
+
ids = id_map.keys.uniq.map do |id|
|
229
|
+
if column_type == :integer
|
230
|
+
id.to_i
|
231
|
+
elsif column_type == :float
|
232
|
+
id.to_f
|
233
|
+
else
|
234
|
+
id
|
235
|
+
end
|
236
|
+
end
|
237
|
+
associated_records = klass.find(:all, :conditions => [conditions, ids],
|
233
238
|
:include => options[:include],
|
234
239
|
:select => options[:select],
|
235
240
|
:joins => options[:joins],
|
@@ -243,7 +248,7 @@ module ActiveRecord
|
|
243
248
|
table_name = reflection.klass.quoted_table_name
|
244
249
|
|
245
250
|
if interface = reflection.options[:as]
|
246
|
-
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.
|
251
|
+
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
|
247
252
|
else
|
248
253
|
foreign_key = reflection.primary_key_name
|
249
254
|
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} IN (?)"
|
@@ -690,6 +690,7 @@ module ActiveRecord
|
|
690
690
|
# association is a polymorphic +belongs_to+.
|
691
691
|
# * <tt>:uniq</tt> - If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
|
692
692
|
# * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
|
693
|
+
# * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. true by default.
|
693
694
|
#
|
694
695
|
# Option examples:
|
695
696
|
# has_many :comments, :order => "posted_on"
|
@@ -710,6 +711,7 @@ module ActiveRecord
|
|
710
711
|
|
711
712
|
configure_dependency_for_has_many(reflection)
|
712
713
|
|
714
|
+
add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
|
713
715
|
add_multiple_associated_save_callbacks(reflection.name)
|
714
716
|
add_association_callbacks(reflection.name, reflection.options)
|
715
717
|
|
@@ -769,6 +771,7 @@ module ActiveRecord
|
|
769
771
|
# * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
|
770
772
|
# association is a polymorphic +belongs_to+.
|
771
773
|
# * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
|
774
|
+
# * <tt>:validate</tt> - If false, don't validate the associated object when saving the parent object. +false+ by default.
|
772
775
|
#
|
773
776
|
# Option examples:
|
774
777
|
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
|
@@ -799,7 +802,7 @@ module ActiveRecord
|
|
799
802
|
end
|
800
803
|
after_save method_name
|
801
804
|
|
802
|
-
|
805
|
+
add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
|
803
806
|
association_accessor_methods(reflection, HasOneAssociation)
|
804
807
|
association_constructor_method(:build, reflection, HasOneAssociation)
|
805
808
|
association_constructor_method(:create, reflection, HasOneAssociation)
|
@@ -857,6 +860,7 @@ module ActiveRecord
|
|
857
860
|
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
|
858
861
|
# to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
|
859
862
|
# * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
|
863
|
+
# * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +false+ by default.
|
860
864
|
#
|
861
865
|
# Option examples:
|
862
866
|
# belongs_to :firm, :foreign_key => "client_of"
|
@@ -937,6 +941,8 @@ module ActiveRecord
|
|
937
941
|
)
|
938
942
|
end
|
939
943
|
|
944
|
+
add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
|
945
|
+
|
940
946
|
configure_dependency_for_belongs_to(reflection)
|
941
947
|
end
|
942
948
|
|
@@ -1025,6 +1031,7 @@ module ActiveRecord
|
|
1025
1031
|
# * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
|
1026
1032
|
# but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
|
1027
1033
|
# * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
|
1034
|
+
# * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +true+ by default.
|
1028
1035
|
#
|
1029
1036
|
# Option examples:
|
1030
1037
|
# has_and_belongs_to_many :projects
|
@@ -1037,6 +1044,7 @@ module ActiveRecord
|
|
1037
1044
|
def has_and_belongs_to_many(association_id, options = {}, &extension)
|
1038
1045
|
reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
|
1039
1046
|
|
1047
|
+
add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false
|
1040
1048
|
add_multiple_associated_save_callbacks(reflection.name)
|
1041
1049
|
collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
|
1042
1050
|
|
@@ -1103,10 +1111,9 @@ module ActiveRecord
|
|
1103
1111
|
association.create_through_record(new_value)
|
1104
1112
|
self.send(reflection.name, new_value)
|
1105
1113
|
else
|
1106
|
-
association.replace(new_value)
|
1114
|
+
association.replace(new_value)
|
1115
|
+
instance_variable_set(ivar, new_value.nil? ? nil : association)
|
1107
1116
|
end
|
1108
|
-
|
1109
|
-
instance_variable_set(ivar, new_value.nil? ? nil : association)
|
1110
1117
|
end
|
1111
1118
|
|
1112
1119
|
define_method("set_#{reflection.name}_target") do |target|
|
@@ -1157,7 +1164,7 @@ module ActiveRecord
|
|
1157
1164
|
end
|
1158
1165
|
end
|
1159
1166
|
|
1160
|
-
def
|
1167
|
+
def add_single_associated_validation_callbacks(association_name)
|
1161
1168
|
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
1162
1169
|
define_method(method_name) do
|
1163
1170
|
association = instance_variable_get("@#{association_name}")
|
@@ -1169,7 +1176,7 @@ module ActiveRecord
|
|
1169
1176
|
validate method_name
|
1170
1177
|
end
|
1171
1178
|
|
1172
|
-
def
|
1179
|
+
def add_multiple_associated_validation_callbacks(association_name)
|
1173
1180
|
method_name = "validate_associated_records_for_#{association_name}".to_sym
|
1174
1181
|
ivar = "@#{association_name}"
|
1175
1182
|
|
@@ -1190,6 +1197,10 @@ module ActiveRecord
|
|
1190
1197
|
end
|
1191
1198
|
|
1192
1199
|
validate method_name
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
def add_multiple_associated_save_callbacks(association_name)
|
1203
|
+
ivar = "@#{association_name}"
|
1193
1204
|
|
1194
1205
|
method_name = "before_save_associated_records_for_#{association_name}".to_sym
|
1195
1206
|
define_method(method_name) do
|
@@ -1211,7 +1222,6 @@ module ActiveRecord
|
|
1211
1222
|
else
|
1212
1223
|
[]
|
1213
1224
|
end
|
1214
|
-
|
1215
1225
|
records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank?
|
1216
1226
|
|
1217
1227
|
# reconstruct the SQL queries now that we know the owner's id
|
@@ -1343,7 +1353,8 @@ module ActiveRecord
|
|
1343
1353
|
:uniq,
|
1344
1354
|
:finder_sql, :counter_sql,
|
1345
1355
|
:before_add, :after_add, :before_remove, :after_remove,
|
1346
|
-
:extend, :readonly
|
1356
|
+
:extend, :readonly,
|
1357
|
+
:validate
|
1347
1358
|
)
|
1348
1359
|
|
1349
1360
|
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
|
@@ -1353,7 +1364,7 @@ module ActiveRecord
|
|
1353
1364
|
|
1354
1365
|
def create_has_one_reflection(association_id, options)
|
1355
1366
|
options.assert_valid_keys(
|
1356
|
-
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly
|
1367
|
+
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly, :validate
|
1357
1368
|
)
|
1358
1369
|
|
1359
1370
|
create_reflection(:has_one, association_id, options, self)
|
@@ -1361,7 +1372,7 @@ module ActiveRecord
|
|
1361
1372
|
|
1362
1373
|
def create_has_one_through_reflection(association_id, options)
|
1363
1374
|
options.assert_valid_keys(
|
1364
|
-
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type
|
1375
|
+
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type, :validate
|
1365
1376
|
)
|
1366
1377
|
create_reflection(:has_one, association_id, options, self)
|
1367
1378
|
end
|
@@ -1369,7 +1380,7 @@ module ActiveRecord
|
|
1369
1380
|
def create_belongs_to_reflection(association_id, options)
|
1370
1381
|
options.assert_valid_keys(
|
1371
1382
|
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent,
|
1372
|
-
:counter_cache, :extend, :polymorphic, :readonly
|
1383
|
+
:counter_cache, :extend, :polymorphic, :readonly, :validate
|
1373
1384
|
)
|
1374
1385
|
|
1375
1386
|
reflection = create_reflection(:belongs_to, association_id, options, self)
|
@@ -1388,7 +1399,8 @@ module ActiveRecord
|
|
1388
1399
|
:uniq,
|
1389
1400
|
:finder_sql, :delete_sql, :insert_sql,
|
1390
1401
|
:before_add, :after_add, :before_remove, :after_remove,
|
1391
|
-
:extend, :readonly
|
1402
|
+
:extend, :readonly,
|
1403
|
+
:validate
|
1392
1404
|
)
|
1393
1405
|
|
1394
1406
|
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
|
@@ -1465,10 +1477,15 @@ module ActiveRecord
|
|
1465
1477
|
join_dependency.joins_for_table_name(table)
|
1466
1478
|
}.flatten.compact.uniq
|
1467
1479
|
|
1480
|
+
order = options[:order]
|
1481
|
+
if scoped_order = (scope && scope[:order])
|
1482
|
+
order = order ? "#{order}, #{scoped_order}" : scoped_order
|
1483
|
+
end
|
1484
|
+
|
1468
1485
|
is_distinct = !options[:joins].blank? || include_eager_conditions?(options, tables_from_conditions) || include_eager_order?(options, tables_from_order)
|
1469
1486
|
sql = "SELECT "
|
1470
1487
|
if is_distinct
|
1471
|
-
sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}",
|
1488
|
+
sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", order)
|
1472
1489
|
else
|
1473
1490
|
sql << primary_key
|
1474
1491
|
end
|
@@ -1482,8 +1499,8 @@ module ActiveRecord
|
|
1482
1499
|
add_conditions!(sql, options[:conditions], scope)
|
1483
1500
|
add_group!(sql, options[:group], scope)
|
1484
1501
|
|
1485
|
-
if
|
1486
|
-
connection.add_order_by_for_association_limiting!(sql,
|
1502
|
+
if order && is_distinct
|
1503
|
+
connection.add_order_by_for_association_limiting!(sql, :order => order)
|
1487
1504
|
else
|
1488
1505
|
add_order!(sql, options[:order], scope)
|
1489
1506
|
end
|
@@ -1502,19 +1519,19 @@ module ActiveRecord
|
|
1502
1519
|
else all << cond
|
1503
1520
|
end
|
1504
1521
|
end
|
1505
|
-
conditions.join(' ').scan(/([
|
1522
|
+
conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten
|
1506
1523
|
end
|
1507
1524
|
|
1508
1525
|
def order_tables(options)
|
1509
|
-
order = options[:order]
|
1526
|
+
order = [options[:order], scope(:find, :order) ].join(", ")
|
1510
1527
|
return [] unless order && order.is_a?(String)
|
1511
|
-
order.scan(/([
|
1528
|
+
order.scan(/([\.a-zA-Z_]+).?\./).flatten
|
1512
1529
|
end
|
1513
1530
|
|
1514
1531
|
def selects_tables(options)
|
1515
1532
|
select = options[:select]
|
1516
1533
|
return [] unless select && select.is_a?(String)
|
1517
|
-
select.scan(/"?([
|
1534
|
+
select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten
|
1518
1535
|
end
|
1519
1536
|
|
1520
1537
|
# Checks if the conditions reference a table other than the current model table
|
@@ -1638,7 +1655,9 @@ module ActiveRecord
|
|
1638
1655
|
end
|
1639
1656
|
|
1640
1657
|
def join_for_table_name(table_name)
|
1641
|
-
@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil
|
1658
|
+
join = (@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first) rescue nil
|
1659
|
+
return join unless join.nil?
|
1660
|
+
@joins.select{|j|j.is_a?(JoinAssociation) && j.aliased_join_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil
|
1642
1661
|
end
|
1643
1662
|
|
1644
1663
|
def joins_for_table_name(table_name)
|
@@ -1714,6 +1733,7 @@ module ActiveRecord
|
|
1714
1733
|
collection.target.push(association)
|
1715
1734
|
when :has_one
|
1716
1735
|
return if record.id.to_s != join.parent.record_id(row).to_s
|
1736
|
+
return if record.instance_variable_defined?("@#{join.reflection.name}")
|
1717
1737
|
association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
|
1718
1738
|
record.send("set_#{join.reflection.name}_target", association)
|
1719
1739
|
when :belongs_to
|
@@ -1795,7 +1815,7 @@ module ActiveRecord
|
|
1795
1815
|
@aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
|
1796
1816
|
end
|
1797
1817
|
|
1798
|
-
if reflection.macro
|
1818
|
+
if [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through]
|
1799
1819
|
@aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
|
1800
1820
|
end
|
1801
1821
|
end
|
@@ -1819,7 +1839,7 @@ module ActiveRecord
|
|
1819
1839
|
]
|
1820
1840
|
when :has_many, :has_one
|
1821
1841
|
case
|
1822
|
-
when reflection.
|
1842
|
+
when reflection.options[:through]
|
1823
1843
|
through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
|
1824
1844
|
|
1825
1845
|
jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
|
@@ -1855,7 +1875,7 @@ module ActiveRecord
|
|
1855
1875
|
jt_sti_extra = " AND %s.%s = %s" % [
|
1856
1876
|
connection.quote_table_name(aliased_join_table_name),
|
1857
1877
|
connection.quote_column_name(through_reflection.active_record.inheritance_column),
|
1858
|
-
through_reflection.klass.quote_value(through_reflection.klass.
|
1878
|
+
through_reflection.klass.quote_value(through_reflection.klass.sti_name)]
|
1859
1879
|
end
|
1860
1880
|
when :belongs_to
|
1861
1881
|
first_key = primary_key
|
@@ -1920,10 +1940,8 @@ module ActiveRecord
|
|
1920
1940
|
else
|
1921
1941
|
""
|
1922
1942
|
end || ''
|
1923
|
-
join << %(AND %s
|
1924
|
-
|
1925
|
-
connection.quote_column_name(klass.inheritance_column),
|
1926
|
-
klass.quote_value(klass.name.demodulize)] unless klass.descends_from_active_record?
|
1943
|
+
join << %(AND %s) % [
|
1944
|
+
klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record?
|
1927
1945
|
|
1928
1946
|
[through_reflection, reflection].each do |ref|
|
1929
1947
|
join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions]
|
@@ -78,11 +78,14 @@ module ActiveRecord
|
|
78
78
|
@loaded = false
|
79
79
|
end
|
80
80
|
|
81
|
-
def build(attributes = {})
|
81
|
+
def build(attributes = {}, &block)
|
82
82
|
if attributes.is_a?(Array)
|
83
|
-
attributes.collect { |attr| build(attr) }
|
83
|
+
attributes.collect { |attr| build(attr, &block) }
|
84
84
|
else
|
85
|
-
build_record(attributes)
|
85
|
+
build_record(attributes) do |record|
|
86
|
+
block.call(record) if block_given?
|
87
|
+
set_belongs_to_association_for(record)
|
88
|
+
end
|
86
89
|
end
|
87
90
|
end
|
88
91
|
|
@@ -187,7 +190,7 @@ module ActiveRecord
|
|
187
190
|
if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
|
188
191
|
@target.size
|
189
192
|
elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
|
190
|
-
unsaved_records =
|
193
|
+
unsaved_records = @target.select { |r| r.new_record? }
|
191
194
|
unsaved_records.size + count_records
|
192
195
|
else
|
193
196
|
count_records
|
@@ -335,7 +338,7 @@ module ActiveRecord
|
|
335
338
|
callback(:before_add, record)
|
336
339
|
yield(record) if block_given?
|
337
340
|
@target ||= [] unless loaded?
|
338
|
-
@target << record
|
341
|
+
@target << record unless @reflection.options[:uniq] && @target.include?(record)
|
339
342
|
callback(:after_add, record)
|
340
343
|
record
|
341
344
|
end
|