reflection_test.rb 17.5 KB
Newer Older
1
require "cases/helper"
J
Jeremy Kemper 已提交
2 3 4 5
require 'models/topic'
require 'models/customer'
require 'models/company'
require 'models/company_in_module'
6
require 'models/ship'
7
require 'models/pirate'
8
require 'models/price_estimate'
9 10 11 12 13 14 15 16 17 18
require 'models/essay'
require 'models/author'
require 'models/organization'
require 'models/post'
require 'models/tagging'
require 'models/category'
require 'models/book'
require 'models/subscriber'
require 'models/subscription'
require 'models/tag'
19
require 'models/sponsor'
20
require 'models/edge'
21 22 23 24 25
require 'models/hotel'
require 'models/chef'
require 'models/department'
require 'models/cake_designer'
require 'models/drink_designer'
D
Initial  
David Heinemeier Hansson 已提交
26

27
class ReflectionTest < ActiveRecord::TestCase
28 29
  include ActiveRecord::Reflection

30
  fixtures :topics, :customers, :companies, :subscribers, :price_estimates
31

D
Initial  
David Heinemeier Hansson 已提交
32 33 34 35
  def setup
    @first = Topic.find(1)
  end

36
  def test_human_name
37 38
    assert_equal "Price estimate", PriceEstimate.model_name.human
    assert_equal "Subscriber", Subscriber.model_name.human
39 40
  end

D
Initial  
David Heinemeier Hansson 已提交
41 42
  def test_read_attribute_names
    assert_equal(
43
      %w( id title author_name author_email_address bonus_time written_on last_read content important group approved replies_count unique_replies_count parent_id parent_title type created_at updated_at ).sort,
44
      @first.attribute_names.sort
D
Initial  
David Heinemeier Hansson 已提交
45 46
    )
  end
47

D
Initial  
David Heinemeier Hansson 已提交
48
  def test_columns
49
    assert_equal 18, Topic.columns.length
D
Initial  
David Heinemeier Hansson 已提交
50 51
  end

52 53
  def test_columns_are_returned_in_the_order_they_were_declared
    column_names = Topic.columns.map { |column| column.name }
54
    assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content important approved replies_count unique_replies_count parent_id parent_title type group created_at updated_at), column_names
55 56
  end

D
Initial  
David Heinemeier Hansson 已提交
57
  def test_content_columns
58 59
    content_columns        = Topic.content_columns
    content_column_names   = content_columns.map {|column| column.name}
A
Alvaro Bautista 已提交
60 61
    assert_equal 13, content_columns.length
    assert_equal %w(title author_name author_email_address written_on bonus_time last_read content important group approved parent_title created_at updated_at).sort, content_column_names.sort
D
Initial  
David Heinemeier Hansson 已提交
62
  end
63

D
Initial  
David Heinemeier Hansson 已提交
64 65
  def test_column_string_type_and_limit
    assert_equal :string, @first.column_for_attribute("title").type
66
    assert_equal 250, @first.column_for_attribute("title").limit
D
Initial  
David Heinemeier Hansson 已提交
67
  end
J
Jeremy Kemper 已提交
68

69
  def test_column_null_not_null
70
    subscriber = Subscriber.first
71 72 73
    assert subscriber.column_for_attribute("name").null
    assert !subscriber.column_for_attribute("nick").null
  end
D
Initial  
David Heinemeier Hansson 已提交
74 75 76 77

  def test_human_name_for_column
    assert_equal "Author name", @first.column_for_attribute("author_name").human_name
  end
78

D
Initial  
David Heinemeier Hansson 已提交
79 80
  def test_integer_columns
    assert_equal :integer, @first.column_for_attribute("id").type
81
  end
D
Initial  
David Heinemeier Hansson 已提交
82

83 84 85 86
  def test_non_existent_columns_return_nil
    assert_deprecated do
      assert_nil @first.column_for_attribute("attribute_that_doesnt_exist")
    end
87 88
  end

89
  def test_reflection_klass_for_nested_class_name
90
    reflection = MacroReflection.new(:company, nil, nil, { :class_name => 'MyApplication::Business::Company' }, ActiveRecord::Base)
91 92 93 94 95
    assert_nothing_raised do
      assert_equal MyApplication::Business::Company, reflection.klass
    end
  end

96 97 98 99 100 101 102 103
  def test_irregular_reflection_class_name
    ActiveSupport::Inflector.inflections do |inflect|
      inflect.irregular 'plural_irregular', 'plurales_irregulares'
    end
    reflection = AssociationReflection.new(:has_many, 'plurales_irregulares', nil, {}, ActiveRecord::Base)
    assert_equal 'PluralIrregular', reflection.class_name
  end

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
  def test_aggregation_reflection
    reflection_for_address = AggregateReflection.new(
      :composed_of, :address, nil, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer
    )

    reflection_for_balance = AggregateReflection.new(
      :composed_of, :balance, nil, { :class_name => "Money", :mapping => %w(balance amount) }, Customer
    )

    reflection_for_gps_location = AggregateReflection.new(
      :composed_of, :gps_location, nil, { }, Customer
    )

    assert Customer.reflect_on_all_aggregations.include?(reflection_for_gps_location)
    assert Customer.reflect_on_all_aggregations.include?(reflection_for_balance)
    assert Customer.reflect_on_all_aggregations.include?(reflection_for_address)

    assert_equal reflection_for_address, Customer.reflect_on_aggregation(:address)

    assert_equal Address, Customer.reflect_on_aggregation(:address).klass

    assert_equal Money, Customer.reflect_on_aggregation(:balance).klass
  end

128 129 130 131 132 133 134 135 136
  def test_reflect_on_all_autosave_associations
    expected = Pirate.reflect_on_all_associations.select { |r| r.options[:autosave] }
    received = Pirate.reflect_on_all_autosave_associations

    assert !received.empty?
    assert_not_equal Pirate.reflect_on_all_associations.length, received.length
    assert_equal expected, received
  end

137
  def test_has_many_reflection
138
    reflection_for_clients = AssociationReflection.new(:has_many, :clients, nil, { :order => "id", :dependent => :destroy }, Firm)
D
Initial  
David Heinemeier Hansson 已提交
139 140 141 142

    assert_equal reflection_for_clients, Firm.reflect_on_association(:clients)

    assert_equal Client, Firm.reflect_on_association(:clients).klass
143 144
    assert_equal 'companies', Firm.reflect_on_association(:clients).table_name

D
Initial  
David Heinemeier Hansson 已提交
145
    assert_equal Client, Firm.reflect_on_association(:clients_of_firm).klass
146 147 148 149
    assert_equal 'companies', Firm.reflect_on_association(:clients_of_firm).table_name
  end

  def test_has_one_reflection
150
    reflection_for_account = AssociationReflection.new(:has_one, :account, nil, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
151 152 153 154
    assert_equal reflection_for_account, Firm.reflect_on_association(:account)

    assert_equal Account, Firm.reflect_on_association(:account).klass
    assert_equal 'accounts', Firm.reflect_on_association(:account).table_name
D
Initial  
David Heinemeier Hansson 已提交
155
  end
156

157 158
  def test_belongs_to_inferred_foreign_key_from_assoc_name
    Company.belongs_to :foo
159
    assert_equal "foo_id", Company.reflect_on_association(:foo).foreign_key
160
    Company.belongs_to :bar, :class_name => "Xyzzy"
161
    assert_equal "bar_id", Company.reflect_on_association(:bar).foreign_key
162
    Company.belongs_to :baz, :class_name => "Xyzzy", :foreign_key => "xyzzy_id"
163
    assert_equal "xyzzy_id", Company.reflect_on_association(:baz).foreign_key
164 165
  end

D
Initial  
David Heinemeier Hansson 已提交
166
  def test_association_reflection_in_modules
167
    ActiveRecord::Base.store_full_sti_class = false
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
    assert_reflection MyApplication::Business::Firm,
      :clients_of_firm,
      :klass      => MyApplication::Business::Client,
      :class_name => 'Client',
      :table_name => 'companies'

    assert_reflection MyApplication::Billing::Account,
      :firm,
      :klass      => MyApplication::Business::Firm,
      :class_name => 'MyApplication::Business::Firm',
      :table_name => 'companies'

    assert_reflection MyApplication::Billing::Account,
      :qualified_billing_firm,
      :klass      => MyApplication::Billing::Firm,
      :class_name => 'MyApplication::Billing::Firm',
      :table_name => 'companies'

    assert_reflection MyApplication::Billing::Account,
      :unqualified_billing_firm,
      :klass      => MyApplication::Billing::Firm,
      :class_name => 'Firm',
      :table_name => 'companies'

    assert_reflection MyApplication::Billing::Account,
      :nested_qualified_billing_firm,
      :klass      => MyApplication::Billing::Nested::Firm,
      :class_name => 'MyApplication::Billing::Nested::Firm',
      :table_name => 'companies'

    assert_reflection MyApplication::Billing::Account,
      :nested_unqualified_billing_firm,
      :klass      => MyApplication::Billing::Nested::Firm,
      :class_name => 'Nested::Firm',
      :table_name => 'companies'
204 205
  ensure
    ActiveRecord::Base.store_full_sti_class = true
D
Initial  
David Heinemeier Hansson 已提交
206
  end
J
Jeremy Kemper 已提交
207

208
  def test_reflection_should_not_raise_error_when_compared_to_other_object
209 210 211 212 213 214
    assert_not_equal Object.new, Firm._reflections['clients']
  end

  def test_has_and_belongs_to_many_reflection
    assert_equal :has_and_belongs_to_many, Category.reflections['posts'].macro
    assert_equal :posts, Category.reflect_on_all_associations(:has_and_belongs_to_many).first.name
215 216
  end

217
  def test_has_many_through_reflection
218
    assert_kind_of ThroughReflection, Subscriber.reflect_on_association(:books)
219
  end
J
Jon Leighton 已提交
220

221
  def test_chain
222
    expected = [
223
      Organization.reflect_on_association(:author_essay_categories),
224 225 226
      Author.reflect_on_association(:essays),
      Organization.reflect_on_association(:authors)
    ]
227
    actual = Organization.reflect_on_association(:author_essay_categories).chain
J
Jon Leighton 已提交
228

229 230
    assert_equal expected, actual
  end
J
Jon Leighton 已提交
231

232
  def test_scope_chain
233
    expected = [
234 235 236
      [Tagging.reflect_on_association(:tag).scope, Post.reflect_on_association(:first_blue_tags).scope],
      [Post.reflect_on_association(:first_taggings).scope],
      [Author.reflect_on_association(:misc_posts).scope]
237
    ]
238
    actual = Author.reflect_on_association(:misc_post_first_blue_tags).scope_chain
239
    assert_equal expected, actual
J
Jon Leighton 已提交
240

241
    expected = [
242 243 244 245 246
      [
        Tagging.reflect_on_association(:blue_tag).scope,
        Post.reflect_on_association(:first_blue_tags_2).scope,
        Author.reflect_on_association(:misc_post_first_blue_tags_2).scope
      ],
247
      [],
248 249
      []
    ]
250
    actual = Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain
251 252
    assert_equal expected, actual
  end
J
Jon Leighton 已提交
253

254 255 256 257 258 259 260 261 262 263 264
  def test_scope_chain_does_not_interfere_with_hmt_with_polymorphic_case
    @hotel = Hotel.create!
    @department = @hotel.departments.create!
    @department.chefs.create!(employable: CakeDesigner.create!)
    @department.chefs.create!(employable: DrinkDesigner.create!)

    assert_equal 1, @hotel.cake_designers.size
    assert_equal 1, @hotel.drink_designers.size
    assert_equal 2, @hotel.chefs.size
  end

265 266 267
  def test_nested?
    assert !Author.reflect_on_association(:comments).nested?
    assert Author.reflect_on_association(:tags).nested?
J
Jon Leighton 已提交
268

269 270 271 272
    # Only goes :through once, but the through_reflection is a has_and_belongs_to_many, so this is
    # a nested through association
    assert Category.reflect_on_association(:post_comments).nested?
  end
J
Jon Leighton 已提交
273

274 275 276 277
  def test_association_primary_key
    # Normal association
    assert_equal "id",   Author.reflect_on_association(:posts).association_primary_key.to_s
    assert_equal "name", Author.reflect_on_association(:essay).association_primary_key.to_s
278
    assert_equal "name", Essay.reflect_on_association(:writer).association_primary_key.to_s
J
Jon Leighton 已提交
279

280 281 282 283 284
    # Through association (uses the :primary_key option from the source reflection)
    assert_equal "nick", Author.reflect_on_association(:subscribers).association_primary_key.to_s
    assert_equal "name", Author.reflect_on_association(:essay_category).association_primary_key.to_s
    assert_equal "custom_primary_key", Author.reflect_on_association(:tags_with_primary_key).association_primary_key.to_s # nested
  end
J
Jon Leighton 已提交
285

286
  def test_association_primary_key_raises_when_missing_primary_key
287
    reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :edge, nil, {}, Author)
288 289
    assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.association_primary_key }

290 291 292
    through = Class.new(ActiveRecord::Reflection::ThroughReflection) {
      define_method(:source_reflection) { reflection }
    }.new(:fuu, :edge, nil, {}, Author)
293 294 295
    assert_raises(ActiveRecord::UnknownPrimaryKey) { through.association_primary_key }
  end

296 297 298 299
  def test_active_record_primary_key
    assert_equal "nick", Subscriber.reflect_on_association(:subscriptions).active_record_primary_key.to_s
    assert_equal "name", Author.reflect_on_association(:essay).active_record_primary_key.to_s
  end
300

301
  def test_active_record_primary_key_raises_when_missing_primary_key
302
    reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :author, nil, {}, Edge)
303 304 305
    assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.active_record_primary_key }
  end

306 307 308 309 310
  def test_foreign_type
    assert_equal "sponsorable_type", Sponsor.reflect_on_association(:sponsorable).foreign_type.to_s
    assert_equal "sponsorable_type", Sponsor.reflect_on_association(:thing).foreign_type.to_s
  end

311
  def test_collection_association
312 313
    assert Pirate.reflect_on_association(:birds).collection?
    assert Pirate.reflect_on_association(:parrots).collection?
314

315 316
    assert !Pirate.reflect_on_association(:ship).collection?
    assert !Ship.reflect_on_association(:pirate).collection?
317 318
  end

319
  def test_default_association_validation
320
    assert AssociationReflection.new(:has_many, :clients, nil, {}, Firm).validate?
321

322 323 324
    assert !AssociationReflection.new(:has_one, :client, nil, {}, Firm).validate?
    assert !AssociationReflection.new(:belongs_to, :client, nil, {}, Firm).validate?
    assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, {}, Firm).validate?
325 326 327
  end

  def test_always_validate_association_if_explicit
328 329 330 331
    assert AssociationReflection.new(:has_one, :client, nil, { :validate => true }, Firm).validate?
    assert AssociationReflection.new(:belongs_to, :client, nil, { :validate => true }, Firm).validate?
    assert AssociationReflection.new(:has_many, :clients, nil, { :validate => true }, Firm).validate?
    assert AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :validate => true }, Firm).validate?
332 333 334
  end

  def test_validate_association_if_autosave
335 336 337 338
    assert AssociationReflection.new(:has_one, :client, nil, { :autosave => true }, Firm).validate?
    assert AssociationReflection.new(:belongs_to, :client, nil, { :autosave => true }, Firm).validate?
    assert AssociationReflection.new(:has_many, :clients, nil, { :autosave => true }, Firm).validate?
    assert AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :autosave => true }, Firm).validate?
339 340 341
  end

  def test_never_validate_association_if_explicit
342 343 344 345
    assert !AssociationReflection.new(:has_one, :client, nil, { :autosave => true, :validate => false }, Firm).validate?
    assert !AssociationReflection.new(:belongs_to, :client, nil, { :autosave => true, :validate => false }, Firm).validate?
    assert !AssociationReflection.new(:has_many, :clients, nil, { :autosave => true, :validate => false }, Firm).validate?
    assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :autosave => true, :validate => false }, Firm).validate?
346 347
  end

348 349 350 351 352
  def test_foreign_key
    assert_equal "author_id", Author.reflect_on_association(:posts).foreign_key.to_s
    assert_equal "category_id", Post.reflect_on_association(:categorizations).foreign_key.to_s
  end

353 354 355 356
  def test_through_reflection_scope_chain_does_not_modify_other_reflections
    orig_conds = Post.reflect_on_association(:first_blue_tags_2).scope_chain.inspect
    Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain
    assert_equal orig_conds, Post.reflect_on_association(:first_blue_tags_2).scope_chain.inspect
J
Jon Leighton 已提交
357 358
  end

359 360 361 362
  def test_symbol_for_class_name
    assert_equal Client, Firm.reflect_on_association(:unsorted_clients_with_symbol).klass
  end

363 364 365 366
  def test_join_table
    category = Struct.new(:table_name, :pluralize_table_names).new('categories', true)
    product = Struct.new(:table_name, :pluralize_table_names).new('products', true)

367
    reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, product)
368 369 370
    reflection.stubs(:klass).returns(category)
    assert_equal 'categories_products', reflection.join_table

371
    reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, {}, category)
372 373 374 375 376 377 378 379
    reflection.stubs(:klass).returns(product)
    assert_equal 'categories_products', reflection.join_table
  end

  def test_join_table_with_common_prefix
    category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true)
    product = Struct.new(:table_name, :pluralize_table_names).new('catalog_products', true)

380
    reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, product)
381 382 383
    reflection.stubs(:klass).returns(category)
    assert_equal 'catalog_categories_products', reflection.join_table

384
    reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, {}, category)
385 386 387 388 389 390 391 392
    reflection.stubs(:klass).returns(product)
    assert_equal 'catalog_categories_products', reflection.join_table
  end

  def test_join_table_with_different_prefix
    category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true)
    page = Struct.new(:table_name, :pluralize_table_names).new('content_pages', true)

393
    reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, page)
394 395 396
    reflection.stubs(:klass).returns(category)
    assert_equal 'catalog_categories_content_pages', reflection.join_table

397
    reflection = AssociationReflection.new(:has_and_belongs_to_many, :pages, nil, {}, category)
398 399 400 401 402 403 404 405
    reflection.stubs(:klass).returns(page)
    assert_equal 'catalog_categories_content_pages', reflection.join_table
  end

  def test_join_table_can_be_overridden
    category = Struct.new(:table_name, :pluralize_table_names).new('categories', true)
    product = Struct.new(:table_name, :pluralize_table_names).new('products', true)

406
    reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, { :join_table => 'product_categories' }, product)
407 408 409
    reflection.stubs(:klass).returns(category)
    assert_equal 'product_categories', reflection.join_table

410
    reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, { :join_table => 'product_categories' }, category)
411 412 413 414
    reflection.stubs(:klass).returns(product)
    assert_equal 'product_categories', reflection.join_table
  end

415 416 417 418 419 420 421
  private
    def assert_reflection(klass, association, options)
      assert reflection = klass.reflect_on_association(association)
      options.each do |method, value|
        assert_equal(value, reflection.send(method))
      end
    end
422
end