activerecord 2.3.9 → 2.3.10

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 CHANGED
@@ -1,3 +1,7 @@
1
+ *2.3.10 (October 15, 2010)*
2
+
3
+ * Security Release to fix CVE-2010-3933
4
+
1
5
  *2.3.9 (September 4, 2010)*
2
6
  *2.3.8 (May 24, 2010)*
3
7
  *2.3.7 (May 24, 2010)*
data/Rakefile CHANGED
@@ -192,7 +192,7 @@ spec = Gem::Specification.new do |s|
192
192
  s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
193
193
  end
194
194
 
195
- s.add_dependency('activesupport', '= 2.3.9' + PKG_BUILD)
195
+ s.add_dependency('activesupport', '= 2.3.10' + PKG_BUILD)
196
196
 
197
197
  s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
198
198
  s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
@@ -27,7 +27,7 @@ conn[:socket] = Pathname.glob(%w[
27
27
  /tmp/mysql.sock
28
28
  /var/mysql/mysql.sock
29
29
  /var/run/mysqld/mysqld.sock
30
- ]).find { |path| path.socket? }
30
+ ]).find { |path| path.socket? }.to_s
31
31
 
32
32
  ActiveRecord::Base.establish_connection(conn)
33
33
 
@@ -60,7 +60,7 @@ end
60
60
  sqlfile = "#{__DIR__}/performance.sql"
61
61
 
62
62
  if File.exists?(sqlfile)
63
- mysql_bin = %w[mysql mysql5].select { |bin| `which #{bin}`.length > 0 }
63
+ mysql_bin = %w[mysql mysql5].detect { |bin| `which #{bin}`.length > 0 }
64
64
  `#{mysql_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} < #{sqlfile}`
65
65
  else
66
66
  puts 'Generating data...'
@@ -90,7 +90,7 @@ else
90
90
  )
91
91
  end
92
92
 
93
- mysqldump_bin = %w[mysqldump mysqldump5].select { |bin| `which #{bin}`.length > 0 }
93
+ mysqldump_bin = %w[mysqldump mysqldump5].detect { |bin| `which #{bin}`.length > 0 }
94
94
  `#{mysqldump_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} exhibits users > #{sqlfile}`
95
95
  end
96
96
 
@@ -157,6 +157,40 @@ RBench.run(TIMES) do
157
157
  ar { Exhibit.transaction { Exhibit.new } }
158
158
  end
159
159
 
160
+ report 'Model.find(id)' do
161
+ id = Exhibit.first.id
162
+ ar { Exhibit.find(id) }
163
+ end
164
+
165
+ report 'Model.find_by_sql' do
166
+ ar { Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first }
167
+ end
168
+
169
+ report 'Model.log', (TIMES * 10) do
170
+ ar { Exhibit.connection.send(:log, "hello", "world") {} }
171
+ end
172
+
173
+ report 'AR.execute(query)', (TIMES / 2) do
174
+ ar { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
175
+ end
176
+
177
+ report 'Model.find(id)' do
178
+ id = Exhibit.first.id
179
+ ar { Exhibit.find(id) }
180
+ end
181
+
182
+ report 'Model.find_by_sql' do
183
+ ar { Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first }
184
+ end
185
+
186
+ report 'Model.log', (TIMES * 10) do
187
+ ar { Exhibit.connection.send(:log, "hello", "world") {} }
188
+ end
189
+
190
+ report 'AR.execute(query)', (TIMES / 2) do
191
+ ar { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
192
+ end
193
+
160
194
  summary 'Total'
161
195
  end
162
196
 
@@ -283,7 +283,7 @@ module ActiveRecord
283
283
  through_records.flatten!
284
284
  else
285
285
  options = {}
286
- options[:include] = reflection.options[:include] || reflection.options[:source] if reflection.options[:conditions]
286
+ options[:include] = reflection.options[:include] || reflection.options[:source] if reflection.options[:conditions] || reflection.options[:order]
287
287
  options[:order] = reflection.options[:order]
288
288
  options[:conditions] = reflection.options[:conditions]
289
289
  records.first.class.preload_associations(records, through_association, options)
@@ -332,6 +332,7 @@ module ActiveRecord
332
332
 
333
333
  def include?(record)
334
334
  return false unless record.is_a?(@reflection.klass)
335
+ return include_in_memory?(record) if record.new_record?
335
336
  load_target if @reflection.options[:finder_sql] && !loaded?
336
337
  return @target.include?(record) if loaded?
337
338
  exists?(record)
@@ -491,8 +492,8 @@ module ActiveRecord
491
492
  def callbacks_for(callback_name)
492
493
  full_callback_name = "#{callback_name}_for_#{@reflection.name}"
493
494
  @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
494
- end
495
-
495
+ end
496
+
496
497
  def ensure_owner_is_not_new
497
498
  if @owner.new_record?
498
499
  raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
@@ -503,6 +504,18 @@ module ActiveRecord
503
504
  args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
504
505
  @target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
505
506
  end
507
+
508
+ def include_in_memory?(record)
509
+ if @reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
510
+ @owner.send(proxy_reflection.through_reflection.name.to_sym).each do |source|
511
+ source_reflection_target = source.send(proxy_reflection.source_reflection.name)
512
+ return true if source_reflection_target.respond_to?(:include?) ? source_reflection_target.include?(record) : source_reflection_target == record
513
+ end
514
+ false
515
+ else
516
+ @target.include?(record)
517
+ end
518
+ end
506
519
  end
507
520
  end
508
521
  end
@@ -274,7 +274,7 @@ module ActiveRecord
274
274
 
275
275
  if Hash === options # legacy support, since this param was a string
276
276
  index_type = options[:unique] ? "UNIQUE" : ""
277
- index_name = options[:name] || index_name
277
+ index_name = options[:name].to_s if options[:name]
278
278
  else
279
279
  index_type = options
280
280
  end
@@ -347,6 +347,7 @@ module ActiveRecord
347
347
  # as there's no way to determine the correct answer in that case.
348
348
  def index_exists?(table_name, index_name, default)
349
349
  return default unless respond_to?(:indexes)
350
+ index_name = index_name.to_s
350
351
  indexes(table_name).detect { |i| i.name == index_name }
351
352
  end
352
353
 
@@ -120,7 +120,7 @@ module ActiveRecord
120
120
  options ||= {}
121
121
  [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
122
122
  extend Module.new(&block) if block_given?
123
- unless Scope === proxy_scope
123
+ unless (Scope === proxy_scope || ActiveRecord::Associations::AssociationCollection === proxy_scope)
124
124
  @current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods)
125
125
  end
126
126
  @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
@@ -286,9 +286,7 @@ module ActiveRecord
286
286
  assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy])
287
287
 
288
288
  elsif attributes['id']
289
- existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
290
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
291
- self.send(association_name.to_s+'=', existing_record)
289
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
292
290
 
293
291
  elsif !reject_new_record?(association_name, attributes)
294
292
  method = "build_#{association_name}"
@@ -358,16 +356,11 @@ module ActiveRecord
358
356
  unless reject_new_record?(association_name, attributes)
359
357
  association.build(attributes.except(*UNASSIGNABLE_KEYS))
360
358
  end
361
-
362
- elsif existing_records.size == 0 # Existing record but not yet associated
363
- existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
364
- association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
365
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
366
-
367
359
  elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
368
360
  association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
369
361
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
370
-
362
+ else
363
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
371
364
  end
372
365
  end
373
366
  end
@@ -387,7 +380,7 @@ module ActiveRecord
387
380
  ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
388
381
  end
389
382
 
390
- # Determines if a new record should be built by checking for
383
+ # Determines if a new record should be build by checking for
391
384
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
392
385
  # association and evaluates to +true+.
393
386
  def reject_new_record?(association_name, attributes)
@@ -403,5 +396,9 @@ module ActiveRecord
403
396
  end
404
397
  end
405
398
 
399
+ def raise_nested_attributes_record_not_found(association_name, record_id)
400
+ reflection = self.class.reflect_on_association(association_name)
401
+ raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
402
+ end
406
403
  end
407
404
  end
@@ -2,7 +2,7 @@ module ActiveRecord
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 2
4
4
  MINOR = 3
5
- TINY = 9
5
+ TINY = 10
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -363,7 +363,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
363
363
  assert_equal post_tags, eager_post_tags
364
364
  end
365
365
 
366
-
366
+ def test_eager_with_has_many_through_association_with_order
367
+ author_comments = Author.find(authors(:david).id).comments_desc
368
+ eager_author_comments = Author.find(authors(:david).id, :include => :comments_desc).comments_desc
369
+ assert_equal eager_author_comments, author_comments
370
+ end
371
+
367
372
  def test_eager_with_has_many_through_join_model_with_include
368
373
  author_comments = Author.find(authors(:david).id, :include => :comments_with_include).comments_with_include.to_a
369
374
  assert_no_queries do
@@ -819,4 +819,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
819
819
  assert_queries(0) { david.projects.columns; david.projects.columns }
820
820
  end
821
821
 
822
+ def test_include_method_in_has_and_belongs_to_many_association_should_return_true_for_instance_added_with_build
823
+ project = Project.new
824
+ developer = project.developers.build
825
+ assert project.developers.include?(developer)
826
+ end
822
827
  end
@@ -1221,5 +1221,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
1221
1221
  end
1222
1222
  EOF
1223
1223
  end
1224
- end
1225
1224
 
1225
+ def test_include_method_in_has_many_association_should_return_true_for_instance_added_with_build
1226
+ post = Post.new
1227
+ comment = post.comments.build
1228
+ assert post.comments.include?(comment)
1229
+ end
1230
+ end
@@ -343,4 +343,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
343
343
  lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
344
344
  ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
345
345
  end
346
+
347
+ def test_include_method_in_association_through_should_return_true_for_instance_added_with_build
348
+ person = Person.new
349
+ reference = person.references.build
350
+ job = reference.build_job
351
+ assert person.jobs.include?(job)
352
+ end
353
+
354
+ def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
355
+ author = Author.new
356
+ post = author.posts.build
357
+ comment = post.comments.build
358
+ assert author.comments.include?(comment)
359
+ end
346
360
  end
@@ -119,6 +119,13 @@ if ActiveRecord::Base.connection.supports_migrations?
119
119
  end
120
120
  end
121
121
 
122
+ def test_index_symbol_names
123
+ assert_nothing_raised { Person.connection.add_index :people, :primary_contact_id, :name => :symbol_index_name }
124
+ assert Person.connection.index_exists?(:people, :symbol_index_name, true)
125
+ assert_nothing_raised { Person.connection.remove_index :people, :name => :symbol_index_name }
126
+ assert_equal true, !Person.connection.index_exists?(:people, :symbol_index_name, false)
127
+ end
128
+
122
129
  def test_add_index_length_limit
123
130
  good_index_name = 'x' * Person.connection.index_name_length
124
131
  too_long_index_name = good_index_name + 'x'
@@ -146,6 +146,12 @@ class NamedScopeTest < ActiveRecord::TestCase
146
146
  assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
147
147
  end
148
148
 
149
+ def test_nested_named_scopes_doesnt_duplicate_conditions_on_child_scopes
150
+ comments_scope = posts(:welcome).comments.send(:construct_sql)
151
+ named_scope_sql_conditions = posts(:welcome).comments.containing_the_letter_e.send(:current_scoped_methods)[:find][:conditions]
152
+ assert_no_match /#{comments_scope}.*#{comments_scope}/i, named_scope_sql_conditions
153
+ end
154
+
149
155
  def test_has_many_through_associations_have_access_to_named_scopes
150
156
  assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
151
157
  assert !Comment.containing_the_letter_e.empty?
@@ -175,6 +175,12 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
175
175
  assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
176
176
  end
177
177
 
178
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
179
+ assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
180
+ @pirate.ship_attributes = { :id => 1234567890 }
181
+ end
182
+ end
183
+
178
184
  def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
179
185
  @pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
180
186
 
@@ -324,13 +330,10 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
324
330
  assert_equal 'Arr', @ship.pirate.catchphrase
325
331
  end
326
332
 
327
- def test_should_associate_with_record_if_parent_record_is_not_saved
328
- @ship.destroy
329
- @pirate = Pirate.create(:catchphrase => 'Arr')
330
- @ship = Ship.new(:name => 'Nights Dirty Lightning', :pirate_attributes => { :id => @pirate.id, :catchphrase => @pirate.catchphrase})
331
-
332
- assert_equal @ship.name, 'Nights Dirty Lightning'
333
- assert_equal @pirate, @ship.pirate
333
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
334
+ assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}" do
335
+ @ship.pirate_attributes = { :id => 1234567890 }
336
+ end
334
337
  end
335
338
 
336
339
  def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
@@ -434,11 +437,6 @@ module NestedAttributesOnACollectionAssociationTests
434
437
  assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
435
438
  end
436
439
 
437
- def test_should_assign_existing_children_if_parent_is_new
438
- @pirate = Pirate.new({:catchphrase => "Don' botharr talkin' like one, savvy?"}.merge(@alternate_params))
439
- assert_equal ['Grace OMalley', 'Privateers Greed'], [@pirate.send(@association_name)[0].name, @pirate.send(@association_name)[1].name]
440
- end
441
-
442
440
  def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
443
441
  @pirate.send(association_setter, @alternate_params[association_getter].values)
444
442
  @pirate.save
@@ -508,8 +506,8 @@ module NestedAttributesOnACollectionAssociationTests
508
506
  assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
509
507
  end
510
508
 
511
- def test_should_not_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
512
- assert_nothing_raised ActiveRecord::RecordNotFound do
509
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
510
+ assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
513
511
  @pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
514
512
  end
515
513
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 23
4
5
  prerelease: false
5
6
  segments:
6
7
  - 2
7
8
  - 3
8
- - 9
9
- version: 2.3.9
9
+ - 10
10
+ version: 2.3.10
10
11
  platform: ruby
11
12
  authors:
12
13
  - David Heinemeier Hansson
@@ -14,21 +15,23 @@ autorequire: active_record
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-09-04 00:00:00 -07:00
18
+ date: 2010-10-15 00:00:00 +13:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: activesupport
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - "="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 23
27
30
  segments:
28
31
  - 2
29
32
  - 3
30
- - 9
31
- version: 2.3.9
33
+ - 10
34
+ version: 2.3.10
32
35
  type: :runtime
33
36
  version_requirements: *id001
34
37
  description: Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.
@@ -236,7 +239,9 @@ files:
236
239
  - test/fixtures/edges.yml
237
240
  - test/fixtures/entrants.yml
238
241
  - test/fixtures/faces.yml
242
+ - test/fixtures/fixture_database.sqlite
239
243
  - test/fixtures/fixture_database.sqlite3
244
+ - test/fixtures/fixture_database_2.sqlite
240
245
  - test/fixtures/fixture_database_2.sqlite3
241
246
  - test/fixtures/fk_test_has_fk.yml
242
247
  - test/fixtures/fk_test_has_pk.yml
@@ -407,23 +412,27 @@ rdoc_options:
407
412
  require_paths:
408
413
  - lib
409
414
  required_ruby_version: !ruby/object:Gem::Requirement
415
+ none: false
410
416
  requirements:
411
417
  - - ">="
412
418
  - !ruby/object:Gem::Version
419
+ hash: 3
413
420
  segments:
414
421
  - 0
415
422
  version: "0"
416
423
  required_rubygems_version: !ruby/object:Gem::Requirement
424
+ none: false
417
425
  requirements:
418
426
  - - ">="
419
427
  - !ruby/object:Gem::Version
428
+ hash: 3
420
429
  segments:
421
430
  - 0
422
431
  version: "0"
423
432
  requirements: []
424
433
 
425
434
  rubyforge_project: activerecord
426
- rubygems_version: 1.3.6
435
+ rubygems_version: 1.3.7
427
436
  signing_key:
428
437
  specification_version: 3
429
438
  summary: Implements the ActiveRecord pattern for ORM.