activerecord 2.3.5 → 2.3.6

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 (90) hide show
  1. data/CHANGELOG +33 -0
  2. data/Rakefile +1 -1
  3. data/examples/performance.sql +85 -0
  4. data/lib/active_record.rb +1 -2
  5. data/lib/active_record/association_preload.rb +9 -2
  6. data/lib/active_record/associations.rb +48 -38
  7. data/lib/active_record/associations/association_collection.rb +15 -11
  8. data/lib/active_record/associations/association_proxy.rb +16 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +11 -1
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -10
  11. data/lib/active_record/associations/has_many_association.rb +5 -0
  12. data/lib/active_record/associations/has_many_through_association.rb +5 -5
  13. data/lib/active_record/associations/has_one_association.rb +10 -1
  14. data/lib/active_record/attribute_methods.rb +5 -1
  15. data/lib/active_record/autosave_association.rb +66 -35
  16. data/lib/active_record/base.rb +77 -36
  17. data/lib/active_record/batches.rb +13 -9
  18. data/lib/active_record/calculations.rb +6 -3
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -3
  20. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -7
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +64 -10
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -0
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +31 -1
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +31 -66
  28. data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
  29. data/lib/active_record/dirty.rb +2 -2
  30. data/lib/active_record/fixtures.rb +1 -0
  31. data/lib/active_record/locking/optimistic.rb +34 -1
  32. data/lib/active_record/migration.rb +5 -0
  33. data/lib/active_record/nested_attributes.rb +64 -52
  34. data/lib/active_record/reflection.rb +66 -1
  35. data/lib/active_record/schema.rb +5 -1
  36. data/lib/active_record/schema_dumper.rb +3 -0
  37. data/lib/active_record/serializers/json_serializer.rb +1 -1
  38. data/lib/active_record/validations.rb +13 -1
  39. data/lib/active_record/version.rb +1 -1
  40. data/test/cases/active_schema_test_mysql.rb +22 -0
  41. data/test/cases/associations/belongs_to_associations_test.rb +13 -0
  42. data/test/cases/associations/eager_load_nested_include_test.rb +8 -7
  43. data/test/cases/associations/eager_test.rb +7 -1
  44. data/test/cases/associations/has_many_associations_test.rb +26 -0
  45. data/test/cases/associations/inverse_associations_test.rb +566 -0
  46. data/test/cases/associations_test.rb +10 -0
  47. data/test/cases/autosave_association_test.rb +86 -10
  48. data/test/cases/base_test.rb +29 -0
  49. data/test/cases/batches_test.rb +20 -0
  50. data/test/cases/calculations_test.rb +2 -3
  51. data/test/cases/encoding_test.rb +6 -0
  52. data/test/cases/finder_test.rb +19 -3
  53. data/test/cases/fixtures_test.rb +5 -0
  54. data/test/cases/json_serialization_test.rb +14 -0
  55. data/test/cases/locking_test.rb +48 -3
  56. data/test/cases/migration_test.rb +115 -0
  57. data/test/cases/modules_test.rb +28 -0
  58. data/test/cases/named_scope_test.rb +1 -1
  59. data/test/cases/nested_attributes_test.rb +239 -7
  60. data/test/cases/query_cache_test.rb +7 -1
  61. data/test/cases/reflection_test.rb +47 -7
  62. data/test/cases/schema_test_postgresql.rb +2 -2
  63. data/test/cases/validations_i18n_test.rb +6 -36
  64. data/test/cases/validations_test.rb +33 -1
  65. data/test/cases/yaml_serialization_test.rb +11 -0
  66. data/test/fixtures/faces.yml +11 -0
  67. data/test/fixtures/fixture_database.sqlite +0 -0
  68. data/test/fixtures/fixture_database.sqlite3 +0 -0
  69. data/test/fixtures/fixture_database_2.sqlite +0 -0
  70. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  71. data/test/fixtures/interests.yml +33 -0
  72. data/test/fixtures/men.yml +5 -0
  73. data/test/fixtures/zines.yml +5 -0
  74. data/test/models/author.rb +3 -0
  75. data/test/models/bird.rb +6 -0
  76. data/test/models/company_in_module.rb +17 -0
  77. data/test/models/event_author.rb +5 -0
  78. data/test/models/face.rb +7 -0
  79. data/test/models/interest.rb +5 -0
  80. data/test/models/invoice.rb +4 -0
  81. data/test/models/line_item.rb +3 -0
  82. data/test/models/man.rb +9 -0
  83. data/test/models/parrot.rb +6 -0
  84. data/test/models/pirate.rb +10 -0
  85. data/test/models/ship.rb +10 -1
  86. data/test/models/ship_part.rb +3 -1
  87. data/test/models/zine.rb +3 -0
  88. data/test/schema/schema.rb +41 -0
  89. metadata +37 -11
  90. data/lib/active_record/i18n_interpolation_deprecation.rb +0 -26
@@ -137,11 +137,11 @@ class SchemaTest < ActiveRecord::TestCase
137
137
 
138
138
  def test_with_uppercase_index_name
139
139
  ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
140
- assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "#{SCHEMA_NAME}.things_Index"}
140
+ assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"}
141
141
 
142
142
  ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
143
143
  ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME
144
- assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "things_Index"}
144
+ assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "things_Index"}
145
145
  ActiveRecord::Base.connection.schema_search_path = "public"
146
146
  end
147
147
 
@@ -27,42 +27,6 @@ module ActiveRecordValidationsI18nTestHelper
27
27
  end
28
28
  end
29
29
 
30
- # DEPRECATIONS
31
-
32
- class ActiveRecordValidationsI18nDeprecationsTests < ActiveSupport::TestCase
33
- test "default_error_messages is deprecated and can be removed in Rails 3 / ActiveModel" do
34
- assert_deprecated('ActiveRecord::Errors.default_error_messages') do
35
- ActiveRecord::Errors.default_error_messages
36
- end
37
- end
38
-
39
- test "%s interpolation syntax in error messages still works" do
40
- ActiveSupport::Deprecation.silence do
41
- result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this'
42
- assert_equal result, "this interpolation syntax is deprecated"
43
- end
44
- end
45
-
46
- test "%s interpolation syntax in error messages is deprecated" do
47
- assert_deprecated('using %s in messages') do
48
- I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this'
49
- end
50
- end
51
-
52
- test "%d interpolation syntax in error messages still works" do
53
- ActiveSupport::Deprecation.silence do
54
- result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2
55
- assert_equal result, "2 interpolation syntaxes are deprecated"
56
- end
57
- end
58
-
59
- test "%d interpolation syntax in error messages is deprecated" do
60
- assert_deprecated('using %d in messages') do
61
- I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2
62
- end
63
- end
64
- end
65
-
66
30
 
67
31
  # ACTIVERECORD VALIDATIONS
68
32
  #
@@ -521,6 +485,12 @@ class ActiveRecordErrorI18nTests < ActiveSupport::TestCase
521
485
  assert_equal message, ActiveRecord::Error.new(@reply, *args).full_message
522
486
  end
523
487
 
488
+ test ":default is only given to message if a symbol is supplied" do
489
+ store_translations(:errors => { :messages => { :"foo bar" => "You fooed: {{value}}." } })
490
+ @reply.errors.add(:title, :inexistent, :default => "foo bar")
491
+ assert_equal "foo bar", @reply.errors[:title]
492
+ end
493
+
524
494
  test "#generate_message passes the model attribute value for interpolation" do
525
495
  store_translations(:errors => { :messages => { :foo => "You fooed: {{value}}." } })
526
496
  @reply.title = "da title"
@@ -9,6 +9,8 @@ require 'models/guid'
9
9
  require 'models/owner'
10
10
  require 'models/pet'
11
11
  require 'models/event'
12
+ require 'models/man'
13
+ require 'models/interest'
12
14
 
13
15
  # The following methods in Topic are used in test_conditional_validation_*
14
16
  class Topic
@@ -186,7 +188,7 @@ class ValidationsTest < ActiveRecord::TestCase
186
188
  end
187
189
  end
188
190
 
189
- def test_single_error_per_attr_iteration
191
+ def test_single_error_string_per_attr_iteration
190
192
  r = Reply.new
191
193
  r.save
192
194
 
@@ -197,6 +199,17 @@ class ValidationsTest < ActiveRecord::TestCase
197
199
  assert errors.include?(["content", "Empty"])
198
200
  end
199
201
 
202
+ def test_single_error_object_per_attr_iteration
203
+ r = Reply.new
204
+ r.save
205
+
206
+ errors = []
207
+ r.errors.each_error { |attr, error| errors << [attr, error.attribute] }
208
+
209
+ assert errors.include?(["title", "title"])
210
+ assert errors.include?(["content", "content"])
211
+ end
212
+
200
213
  def test_multiple_errors_per_attr_iteration_with_full_error_composition
201
214
  r = Reply.new
202
215
  r.title = "Wrong Create"
@@ -356,6 +369,25 @@ class ValidationsTest < ActiveRecord::TestCase
356
369
  assert t.save
357
370
  end
358
371
 
372
+ def test_validates_presence_of_belongs_to_association__parent_is_new_record
373
+ repair_validations(Interest) do
374
+ # Note that Interest and Man have the :inverse_of option set
375
+ Interest.validates_presence_of(:man)
376
+ man = Man.new(:name => 'John')
377
+ interest = man.interests.build(:topic => 'Airplanes')
378
+ assert interest.valid?, "Expected interest to be valid, but was not. Interest should have a man object associated"
379
+ end
380
+ end
381
+
382
+ def test_validates_presence_of_belongs_to_association__existing_parent
383
+ repair_validations(Interest) do
384
+ Interest.validates_presence_of(:man)
385
+ man = Man.create!(:name => 'John')
386
+ interest = man.interests.build(:topic => 'Airplanes')
387
+ assert interest.valid?, "Expected interest to be valid, but was not. Interest should have a man object associated"
388
+ end
389
+ end
390
+
359
391
  def test_validate_uniqueness
360
392
  Topic.validates_uniqueness_of(:title)
361
393
 
@@ -0,0 +1,11 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+
4
+ class YamlSerializationTest < ActiveRecord::TestCase
5
+ def test_to_yaml_with_time_with_zone_should_not_raise_exception
6
+ Time.zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
7
+ ActiveRecord::Base.time_zone_aware_attributes = true
8
+ topic = Topic.new(:written_on => DateTime.now)
9
+ assert_nothing_raised { topic.to_yaml }
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ trusting:
2
+ description: trusting
3
+ man: gordon
4
+
5
+ weather_beaten:
6
+ description: weather beaten
7
+ man: steve
8
+
9
+ confused:
10
+ description: confused
11
+ polymorphic_man: gordon (Man)
@@ -0,0 +1,33 @@
1
+ trainspotting:
2
+ topic: Trainspotting
3
+ zine: staying_in
4
+ man: gordon
5
+
6
+ birdwatching:
7
+ topic: Birdwatching
8
+ zine: staying_in
9
+ man: gordon
10
+
11
+ stamp_collecting:
12
+ topic: Stamp Collecting
13
+ zine: staying_in
14
+ man: gordon
15
+
16
+ hunting:
17
+ topic: Hunting
18
+ zine: going_out
19
+ man: steve
20
+
21
+ woodsmanship:
22
+ topic: Woodsmanship
23
+ zine: going_out
24
+ man: steve
25
+
26
+ survival:
27
+ topic: Survival
28
+ zine: going_out
29
+ man: steve
30
+
31
+ llama_wrangling:
32
+ topic: Llama Wrangling
33
+ polymorphic_man: gordon (Man)
@@ -0,0 +1,5 @@
1
+ gordon:
2
+ name: Gordon
3
+
4
+ steve:
5
+ name: Steve
@@ -0,0 +1,5 @@
1
+ staying_in:
2
+ title: Staying in '08
3
+
4
+ going_out:
5
+ title: Outdoor Pursuits 2k+8
@@ -88,6 +88,9 @@ class Author < ActiveRecord::Base
88
88
  has_many :tags, :through => :posts # through has_many :through
89
89
  has_many :post_categories, :through => :posts, :source => :categories
90
90
 
91
+ has_many :event_authors
92
+ has_many :events, :through => :event_authors
93
+
91
94
  has_one :essay, :primary_key => :name, :as => :writer
92
95
 
93
96
  belongs_to :author_address, :dependent => :destroy
@@ -1,3 +1,9 @@
1
1
  class Bird < ActiveRecord::Base
2
2
  validates_presence_of :name
3
+
4
+ attr_accessor :cancel_save_from_callback
5
+ before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
6
+ def cancel_save_callback_method
7
+ false
8
+ end
3
9
  end
@@ -30,6 +30,23 @@ module MyApplication
30
30
  has_and_belongs_to_many :developers
31
31
  end
32
32
 
33
+ module Prefixed
34
+ def self.table_name_prefix
35
+ 'prefixed_'
36
+ end
37
+
38
+ class Company < ActiveRecord::Base
39
+ end
40
+
41
+ class Firm < Company
42
+ self.table_name = 'companies'
43
+ end
44
+
45
+ module Nested
46
+ class Company < ActiveRecord::Base
47
+ end
48
+ end
49
+ end
33
50
  end
34
51
 
35
52
  module Billing
@@ -0,0 +1,5 @@
1
+ class EventAuthor < ActiveRecord::Base
2
+ belongs_to :author
3
+ belongs_to :event
4
+ end
5
+
@@ -0,0 +1,7 @@
1
+ class Face < ActiveRecord::Base
2
+ belongs_to :man, :inverse_of => :face
3
+ belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_face
4
+ # These is a "broken" inverse_of for the purposes of testing
5
+ belongs_to :horrible_man, :class_name => 'Man', :inverse_of => :horrible_face
6
+ belongs_to :horrible_polymorphic_man, :polymorphic => true, :inverse_of => :horrible_polymorphic_face
7
+ end
@@ -0,0 +1,5 @@
1
+ class Interest < ActiveRecord::Base
2
+ belongs_to :man, :inverse_of => :interests
3
+ belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_interests
4
+ belongs_to :zine, :inverse_of => :interests
5
+ end
@@ -0,0 +1,4 @@
1
+ class Invoice < ActiveRecord::Base
2
+ has_many :line_items, :autosave => true
3
+ before_save {|record| record.balance = record.line_items.map(&:amount).sum }
4
+ end
@@ -0,0 +1,3 @@
1
+ class LineItem < ActiveRecord::Base
2
+ belongs_to :invoice, :touch => true
3
+ end
@@ -0,0 +1,9 @@
1
+ class Man < ActiveRecord::Base
2
+ has_one :face, :inverse_of => :man
3
+ has_one :polymorphic_face, :class_name => 'Face', :as => :polymorphic_man, :inverse_of => :polymorphic_man
4
+ has_many :interests, :inverse_of => :man
5
+ has_many :polymorphic_interests, :class_name => 'Interest', :as => :polymorphic_man, :inverse_of => :polymorphic_man
6
+ # These are "broken" inverse_of associations for the purposes of testing
7
+ has_one :dirty_face, :class_name => 'Face', :inverse_of => :dirty_man
8
+ has_many :secret_interests, :class_name => 'Interest', :inverse_of => :secret_man
9
+ end
@@ -6,6 +6,12 @@ class Parrot < ActiveRecord::Base
6
6
  alias_attribute :title, :name
7
7
 
8
8
  validates_presence_of :name
9
+
10
+ attr_accessor :cancel_save_from_callback
11
+ before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
12
+ def cancel_save_callback_method
13
+ false
14
+ end
9
15
  end
10
16
 
11
17
  class LiveParrot < Parrot
@@ -19,6 +19,7 @@ class Pirate < ActiveRecord::Base
19
19
 
20
20
  # These both have :autosave enabled because accepts_nested_attributes_for is used on them.
21
21
  has_one :ship
22
+ has_one :update_only_ship, :class_name => 'Ship'
22
23
  has_one :non_validated_ship, :class_name => 'Ship'
23
24
  has_many :birds
24
25
  has_many :birds_with_method_callbacks, :class_name => "Bird",
@@ -31,11 +32,14 @@ class Pirate < ActiveRecord::Base
31
32
  :after_add => proc {|p,b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}"},
32
33
  :before_remove => proc {|p,b| p.ship_log << "before_removing_proc_bird_#{b.id}"},
33
34
  :after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
35
+ has_many :birds_with_reject_all_blank, :class_name => "Bird"
34
36
 
35
37
  accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
36
38
  accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
39
+ accepts_nested_attributes_for :update_only_ship, :update_only => true
37
40
  accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks,
38
41
  :birds_with_method_callbacks, :birds_with_proc_callbacks, :allow_destroy => true
42
+ accepts_nested_attributes_for :birds_with_reject_all_blank, :reject_if => :all_blank
39
43
 
40
44
  validates_presence_of :catchphrase
41
45
 
@@ -47,6 +51,12 @@ class Pirate < ActiveRecord::Base
47
51
  attributes.delete('_reject_me_if_new').present? && new_record?
48
52
  end
49
53
 
54
+ attr_accessor :cancel_save_from_callback
55
+ before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
56
+ def cancel_save_callback_method
57
+ false
58
+ end
59
+
50
60
  private
51
61
  def log_before_add(record)
52
62
  log(record, "before_adding_method")
@@ -2,9 +2,18 @@ class Ship < ActiveRecord::Base
2
2
  self.record_timestamps = false
3
3
 
4
4
  belongs_to :pirate
5
- has_many :parts, :class_name => 'ShipPart', :autosave => true
5
+ belongs_to :update_only_pirate, :class_name => 'Pirate'
6
+ has_many :parts, :class_name => 'ShipPart'
6
7
 
8
+ accepts_nested_attributes_for :parts, :allow_destroy => true
7
9
  accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
10
+ accepts_nested_attributes_for :update_only_pirate, :update_only => true
8
11
 
9
12
  validates_presence_of :name
13
+
14
+ attr_accessor :cancel_save_from_callback
15
+ before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
16
+ def cancel_save_callback_method
17
+ false
18
+ end
10
19
  end
@@ -1,5 +1,7 @@
1
1
  class ShipPart < ActiveRecord::Base
2
2
  belongs_to :ship
3
-
3
+ has_many :trinkets, :class_name => "Treasure", :as => :looter
4
+ accepts_nested_attributes_for :trinkets, :allow_destroy => true
5
+
4
6
  validates_presence_of :name
5
7
  end
@@ -0,0 +1,3 @@
1
+ class Zine < ActiveRecord::Base
2
+ has_many :interests, :inverse_of => :zine
3
+ end
@@ -58,6 +58,7 @@ ActiveRecord::Schema.define do
58
58
 
59
59
  create_table :birds, :force => true do |t|
60
60
  t.string :name
61
+ t.string :color
61
62
  t.integer :pirate_id
62
63
  end
63
64
 
@@ -174,6 +175,12 @@ ActiveRecord::Schema.define do
174
175
 
175
176
  create_table :events, :force => true do |t|
176
177
  t.string :title, :limit => 5
178
+ t.datetime :ends_on
179
+ end
180
+
181
+ create_table :event_authors, :force => true do |t|
182
+ t.integer :event_id
183
+ t.integer :author_id
177
184
  end
178
185
 
179
186
  create_table :funny_jokes, :force => true do |t|
@@ -185,6 +192,11 @@ ActiveRecord::Schema.define do
185
192
  t.string :info
186
193
  end
187
194
 
195
+ create_table :invoices, :force => true do |t|
196
+ t.integer :balance
197
+ t.datetime :updated_at
198
+ end
199
+
188
200
  create_table :items, :force => true do |t|
189
201
  t.column :name, :integer
190
202
  end
@@ -210,6 +222,11 @@ ActiveRecord::Schema.define do
210
222
  t.integer :version, :null => false, :default => 0
211
223
  end
212
224
 
225
+ create_table :line_items, :force => true do |t|
226
+ t.integer :invoice_id
227
+ t.integer :amount
228
+ end
229
+
213
230
  create_table :lock_without_defaults, :force => true do |t|
214
231
  t.column :lock_version, :integer
215
232
  end
@@ -479,6 +496,30 @@ ActiveRecord::Schema.define do
479
496
  end
480
497
  end
481
498
 
499
+ # NOTE - the following 4 tables are used by models that have :inverse_of options on the associations
500
+ create_table :men, :force => true do |t|
501
+ t.string :name
502
+ end
503
+
504
+ create_table :faces, :force => true do |t|
505
+ t.string :description
506
+ t.integer :man_id
507
+ t.integer :polymorphic_man_id
508
+ t.string :polymorphic_man_type
509
+ end
510
+
511
+ create_table :interests, :force => true do |t|
512
+ t.string :topic
513
+ t.integer :man_id
514
+ t.integer :polymorphic_man_id
515
+ t.string :polymorphic_man_type
516
+ t.integer :zine_id
517
+ end
518
+
519
+ create_table :zines, :force => true do |t|
520
+ t.string :title
521
+ end
522
+
482
523
  except 'SQLite' do
483
524
  # fk_test_has_fk should be before fk_test_has_pk
484
525
  create_table :fk_test_has_fk, :force => true do |t|