associations_test.rb 12.8 KB
Newer Older
1
require "cases/helper"
2
require 'models/computer'
J
Jeremy Kemper 已提交
3 4 5 6 7 8 9 10 11 12 13 14
require 'models/developer'
require 'models/project'
require 'models/company'
require 'models/categorization'
require 'models/category'
require 'models/post'
require 'models/author'
require 'models/comment'
require 'models/tag'
require 'models/tagging'
require 'models/person'
require 'models/reader'
15
require 'models/parrot'
16 17
require 'models/ship_part'
require 'models/ship'
18 19 20
require 'models/liquid'
require 'models/molecule'
require 'models/electron'
21 22
require 'models/man'
require 'models/interest'
D
Initial  
David Heinemeier Hansson 已提交
23

24
class AssociationsTest < ActiveRecord::TestCase
25
  fixtures :accounts, :companies, :developers, :projects, :developers_projects,
26
           :computers, :people, :readers, :authors, :author_favorites
27

28 29 30 31 32 33
  def test_eager_loading_should_not_change_count_of_children
    liquid = Liquid.create(:name => 'salty')
    molecule = liquid.molecules.create(:name => 'molecule_1')
    molecule.electrons.create(:name => 'electron_1')
    molecule.electrons.create(:name => 'electron_2')

34
    liquids = Liquid.includes(:molecules => :electrons).references(:molecules).where('molecules.id is not null')
35 36 37
    assert_equal 1, liquids[0].molecules.length
  end

38 39 40 41 42 43 44
  def test_subselect
    author = authors :david
    favs = author.author_favorites
    fav2 = author.author_favorites.where(:author => Author.where(id: author.id)).to_a
    assert_equal favs, fav2
  end

45 46 47 48
  def test_clear_association_cache_stored
    firm = Firm.find(1)
    assert_kind_of Firm, firm

49
    firm.send(:clear_association_cache)
50 51 52 53 54 55 56 57 58 59 60 61 62
    assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
  end

  def test_clear_association_cache_new_record
     firm            = Firm.new
     client_stored   = Client.find(3)
     client_new      = Client.new
     client_new.name = "The Joneses"
     clients         = [ client_stored, client_new ]

     firm.clients    << clients
     assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set

63
     firm.send(:clear_association_cache)
64 65 66
     assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
  end

67 68 69 70 71 72 73 74 75 76 77 78
  def test_loading_the_association_target_should_keep_child_records_marked_for_destruction
    ship = Ship.create!(:name => "The good ship Dollypop")
    part = ship.parts.create!(:name => "Mast")
    part.mark_for_destruction
    ship.parts.send(:load_target)
    assert ship.parts[0].marked_for_destruction?
  end

  def test_loading_the_association_target_should_load_most_recent_attributes_for_child_records_marked_for_destruction
    ship = Ship.create!(:name => "The good ship Dollypop")
    part = ship.parts.create!(:name => "Mast")
    part.mark_for_destruction
79
    ShipPart.find(part.id).update_columns(name: 'Deck')
80 81 82
    ship.parts.send(:load_target)
    assert_equal 'Deck', ship.parts[0].name
  end
83

84

85
  def test_include_with_order_works
86 87
    assert_nothing_raised {Account.all.merge!(:order => 'id', :includes => :firm).first}
    assert_nothing_raised {Account.all.merge!(:order => :id, :includes => :firm).first}
88 89
  end

J
Jeremy Kemper 已提交
90 91 92 93 94
  def test_bad_collection_keys
    assert_raise(ArgumentError, 'ActiveRecord should have barked on bad collection keys') do
      Class.new(ActiveRecord::Base).has_many(:wheels, :name => 'wheels')
    end
  end
J
Jeremy Kemper 已提交
95

96
  def test_should_construct_new_finder_sql_after_create
97
    person = Person.new :first_name => 'clark'
J
Jon Leighton 已提交
98
    assert_equal [], person.readers.to_a
99 100
    person.save!
    reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
101
    assert person.readers.find(reader.id)
102
  end
J
Jeremy Kemper 已提交
103

D
Initial  
David Heinemeier Hansson 已提交
104
  def test_force_reload
105
    firm = Firm.new("name" => "A New Firm, Inc")
D
Initial  
David Heinemeier Hansson 已提交
106
    firm.save
V
Vipul A M 已提交
107
    firm.clients.each {} # forcing to load all clients
D
Initial  
David Heinemeier Hansson 已提交
108 109 110
    assert firm.clients.empty?, "New firm shouldn't have client objects"
    assert_equal 0, firm.clients.size, "New firm should have 0 clients"

111
    client = Client.new("name" => "TheClient.com", "firm_id" => firm.id)
D
Initial  
David Heinemeier Hansson 已提交
112 113 114 115 116 117 118 119
    client.save

    assert firm.clients.empty?, "New firm should have cached no client objects"
    assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"

    assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
    assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
  end
120

121
  def test_using_limitable_reflections_helper
122
    using_limitable_reflections = lambda { |reflections| Tagging.all.send :using_limitable_reflections?, reflections }
123 124 125 126 127 128 129
    belongs_to_reflections = [Tagging.reflect_on_association(:tag), Tagging.reflect_on_association(:super_tag)]
    has_many_reflections = [Tag.reflect_on_association(:taggings), Developer.reflect_on_association(:projects)]
    mixed_reflections = (belongs_to_reflections + has_many_reflections).uniq
    assert using_limitable_reflections.call(belongs_to_reflections), "Belong to associations are limitable"
    assert !using_limitable_reflections.call(has_many_reflections), "All has many style associations are not limitable"
    assert !using_limitable_reflections.call(mixed_reflections), "No collection associations (has many style) should pass"
  end
130

131 132
  def test_force_reload_is_uncached
    firm = Firm.create!("name" => "A New Firm, Inc")
A
Aaron Patterson 已提交
133
    Client.create!("name" => "TheClient.com", :firm => firm)
134 135 136 137 138 139
    ActiveRecord::Base.cache do
      firm.clients.each {}
      assert_queries(0) { assert_not_nil firm.clients.each {} }
      assert_queries(1) { assert_not_nil firm.clients(true).each {} }
    end
  end
D
Initial  
David Heinemeier Hansson 已提交
140

141 142
  def test_association_with_references
    firm = companies(:first_firm)
143
    assert_equal ['foo'], firm.association_with_references.references_values
144 145
  end

D
Initial  
David Heinemeier Hansson 已提交
146
end
J
Jeremy Kemper 已提交
147

148
class AssociationProxyTest < ActiveRecord::TestCase
149
  fixtures :authors, :posts, :categorizations, :categories, :developers, :projects, :developers_projects
J
Jeremy Kemper 已提交
150

151 152
  def test_push_does_not_load_target
    david = authors(:david)
153

154 155 156 157 158 159 160 161
    david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
    assert !david.posts.loaded?
    assert david.posts.include?(post)
  end

  def test_push_has_many_through_does_not_load_target
    david = authors(:david)

162
    david.categories << categories(:technology)
163
    assert !david.categories.loaded?
164 165
    assert david.categories.include?(categories(:technology))
  end
J
Jeremy Kemper 已提交
166

167 168 169 170 171 172 173 174 175
  def test_push_followed_by_save_does_not_load_target
    david = authors(:david)

    david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
    assert !david.posts.loaded?
    david.save
    assert !david.posts.loaded?
    assert david.posts.include?(post)
  end
176

177 178 179 180 181 182 183
  def test_push_does_not_lose_additions_to_new_record
    josh = Author.new(:name => "Josh")
    josh.posts << Post.new(:title => "New on Edge", :body => "More cool stuff!")
    assert josh.posts.loaded?
    assert_equal 1, josh.posts.size
  end

184 185 186 187 188 189 190 191 192 193 194 195
  def test_append_behaves_like_push
    josh = Author.new(:name => "Josh")
    josh.posts.append Post.new(:title => "New on Edge", :body => "More cool stuff!")
    assert josh.posts.loaded?
    assert_equal 1, josh.posts.size
  end

  def test_prepend_is_not_defined
    josh = Author.new(:name => "Josh")
    assert_raises(NoMethodError) { josh.posts.prepend Post.new }
  end

196 197 198 199
  def test_save_on_parent_does_not_load_target
    david = developers(:david)

    assert !david.projects.loaded?
200
    david.update_columns(created_at: Time.now)
201 202 203
    assert !david.projects.loaded?
  end

204 205 206 207 208 209
  def test_inspect_does_not_reload_a_not_yet_loaded_target
    andreas = Developer.new :name => 'Andreas', :log => 'new developer added'
    assert !andreas.audit_logs.loaded?
    assert_match(/message: "new developer added"/, andreas.audit_logs.inspect)
  end

210 211 212 213
  def test_save_on_parent_saves_children
    developer = Developer.create :name => "Bryan", :salary => 50_000
    assert_equal 1, developer.reload.audit_logs.size
  end
214

215
  def test_create_via_association_with_block
216 217 218 219 220 221 222 223 224
    post = authors(:david).posts.create(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
    assert_equal post.title, "New on Edge"
    assert_equal post.body, "More cool stuff!"
  end

  def test_create_with_bang_via_association_with_block
    post = authors(:david).posts.create!(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
    assert_equal post.title, "New on Edge"
    assert_equal post.body, "More cool stuff!"
225 226
  end

227
  def test_reload_returns_association
228 229 230 231 232
    david = developers(:david)
    assert_nothing_raised do
      assert_equal david.projects, david.projects.reload.reload
    end
  end
233 234 235 236 237

  def test_proxy_association_accessor
    david = developers(:david)
    assert_equal david.association(:projects), david.projects.proxy_association
  end
238 239

  def test_scoped_allows_conditions
240
    assert developers(:david).projects.merge!(where: 'foo').where_values.include?('foo')
241
  end
J
Jon Leighton 已提交
242 243 244 245 246 247 248

  test "getting a scope from an association" do
    david = developers(:david)

    assert david.projects.scope.is_a?(ActiveRecord::Relation)
    assert_equal david.projects, david.projects.scope
  end
249 250 251 252 253

  test "proxy object is cached" do
    david = developers(:david)
    assert david.projects.equal?(david.projects)
  end
254 255 256 257 258 259 260 261 262 263 264

  test "inverses get set of subsets of the association" do
    man = Man.create
    man.interests.create

    man = Man.find(man.id)

    assert_queries(1) do
      assert_equal man, man.interests.where("1=1").first.man
    end
  end
265 266 267 268 269 270 271 272 273

  def test_reset_unloads_target
    david = authors(:david)
    david.posts.reload

    assert david.posts.loaded?
    david.posts.reset
    assert !david.posts.loaded?
  end
274 275
end

276
class OverridingAssociationsTest < ActiveRecord::TestCase
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
  class DifferentPerson < ActiveRecord::Base; end

  class PeopleList < ActiveRecord::Base
    has_and_belongs_to_many :has_and_belongs_to_many, :before_add => :enlist
    has_many :has_many, :before_add => :enlist
    belongs_to :belongs_to
    has_one :has_one
  end

  class DifferentPeopleList < PeopleList
    # Different association with the same name, callbacks should be omitted here.
    has_and_belongs_to_many :has_and_belongs_to_many, :class_name => 'DifferentPerson'
    has_many :has_many, :class_name => 'DifferentPerson'
    belongs_to :belongs_to, :class_name => 'DifferentPerson'
    has_one :has_one, :class_name => 'DifferentPerson'
  end

  def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
    # redeclared association on AR descendant should not inherit callbacks from superclass
296
    callbacks = PeopleList.before_add_for_has_and_belongs_to_many
297
    assert_equal(1, callbacks.length)
298
    callbacks = DifferentPeopleList.before_add_for_has_and_belongs_to_many
299 300 301 302 303
    assert_equal([], callbacks)
  end

  def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited
    # redeclared association on AR descendant should not inherit callbacks from superclass
304
    callbacks = PeopleList.before_add_for_has_many
305
    assert_equal(1, callbacks.length)
306
    callbacks = DifferentPeopleList.before_add_for_has_many
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
    assert_equal([], callbacks)
  end

  def test_habtm_association_redefinition_reflections_should_differ_and_not_inherited
    assert_not_equal(
      PeopleList.reflect_on_association(:has_and_belongs_to_many),
      DifferentPeopleList.reflect_on_association(:has_and_belongs_to_many)
    )
  end

  def test_has_many_association_redefinition_reflections_should_differ_and_not_inherited
    assert_not_equal(
      PeopleList.reflect_on_association(:has_many),
      DifferentPeopleList.reflect_on_association(:has_many)
    )
  end

  def test_belongs_to_association_redefinition_reflections_should_differ_and_not_inherited
    assert_not_equal(
      PeopleList.reflect_on_association(:belongs_to),
      DifferentPeopleList.reflect_on_association(:belongs_to)
    )
  end

  def test_has_one_association_redefinition_reflections_should_differ_and_not_inherited
    assert_not_equal(
      PeopleList.reflect_on_association(:has_one),
      DifferentPeopleList.reflect_on_association(:has_one)
    )
  end
337 338

  def test_requires_symbol_argument
339
    assert_raises ArgumentError do
340 341 342 343 344
      Class.new(Post) do
        belongs_to "author"
      end
    end
  end
345
end
346 347

class GeneratedMethodsTest < ActiveRecord::TestCase
348
  fixtures :developers, :computers, :posts, :comments
349 350 351 352 353 354 355
  def test_association_methods_override_attribute_methods_of_same_name
    assert_equal(developers(:david), computers(:workstation).developer)
    # this next line will fail if the attribute methods module is generated lazily
    # after the association methods module is generated
    assert_equal(developers(:david), computers(:workstation).developer)
    assert_equal(developers(:david).id, computers(:workstation)[:developer])
  end
356 357 358 359

  def test_model_method_overrides_association_method
    assert_equal(comments(:greetings).body, posts(:welcome).first_comment)
  end
360 361 362 363 364 365 366 367 368 369 370 371 372 373

  module MyModule
    def comments; :none end
  end

  class MyArticle < ActiveRecord::Base
    self.table_name = "articles"
    include MyModule
    has_many :comments, inverse_of: false
  end

  def test_included_module_overwrites_association_methods
    assert_equal :none, MyArticle.new.comments
  end
374
end