Generate unique members of a composite key in fixtures
Typically, a fixture's label is hashed into a single unique identifier that sufficiently represent the respective fixture. In composite key models, there's still only one label, but a requirement to populate values for an arbitrary number of columns. Until now, these columns would all take on the same value, but these may appear odd to an end user and lead to confusion. This commit uses the index of the column in the composite key to shift the hash in a deterministic way. In this case, we bit shift the hashed value index-times and modulo it with the max. This differentiates components of the key enough that there doesn't appear to be a correlation.
This commit is contained in:
parent
4d171a4bde
commit
f7062f2676
@ -87,7 +87,7 @@ def fill_row_model_attributes
|
||||
return unless model_class
|
||||
fill_timestamps
|
||||
interpolate_label
|
||||
generate_primary_key
|
||||
model_class.composite_primary_key? ? generate_composite_primary_key : generate_primary_key
|
||||
resolve_enums
|
||||
resolve_sti_reflections
|
||||
end
|
||||
@ -117,14 +117,28 @@ def interpolate_label
|
||||
end
|
||||
|
||||
def generate_primary_key
|
||||
# generate a primary key if necessary
|
||||
Array(model_metadata.primary_key_name).each do |pk|
|
||||
next if !model_metadata.has_column?(pk) || @row.include?(pk)
|
||||
pk = model_metadata.primary_key_name
|
||||
|
||||
unless column_defined?(pk)
|
||||
@row[pk] = ActiveRecord::FixtureSet.identify(@label, model_metadata.column_type(pk))
|
||||
end
|
||||
end
|
||||
|
||||
def generate_composite_primary_key
|
||||
id = ActiveRecord::FixtureSet.identify(@label)
|
||||
model_metadata.primary_key_name.each_with_index do |column, index|
|
||||
next if column_defined?(column)
|
||||
raise "Automatic key generation assumes columns of type Integer." unless model_metadata.column_type(column) == :integer
|
||||
|
||||
# Shift label identifier index-#-of-times to differentiate sub-components in deterministic manner.
|
||||
@row[column] = (id << index) % ActiveRecord::FixtureSet::MAX_ID
|
||||
end
|
||||
end
|
||||
|
||||
def column_defined?(col)
|
||||
!model_metadata.has_column?(col) || @row.include?(col)
|
||||
end
|
||||
|
||||
def resolve_enums
|
||||
reflection_class.defined_enums.each do |name, values|
|
||||
if @row.include?(name)
|
||||
|
@ -1643,7 +1643,16 @@ def readonly_config
|
||||
end
|
||||
|
||||
class CompositePkFixturesTest < ActiveRecord::TestCase
|
||||
fixtures :cpk_orders, :cpk_books
|
||||
fixtures :cpk_orders, :cpk_books, :authors
|
||||
|
||||
def test_generates_composite_primary_key_for_partially_filled_fixtures
|
||||
david = authors(:david)
|
||||
david_cpk_book = cpk_books(:cpk_known_author_david_book)
|
||||
|
||||
assert_not_empty(david_cpk_book.id.compact)
|
||||
assert_equal david.id, david_cpk_book.author_id
|
||||
assert_not_nil david_cpk_book.number
|
||||
end
|
||||
|
||||
def test_generates_composite_primary_key_ids
|
||||
assert_not_empty(cpk_orders(:cpk_groceries_order_1).id.compact)
|
||||
@ -1651,5 +1660,9 @@ def test_generates_composite_primary_key_ids
|
||||
assert_not_nil(cpk_books(:cpk_great_author_first_book).author_id)
|
||||
assert_not_nil(cpk_books(:cpk_great_author_first_book).number)
|
||||
end
|
||||
|
||||
def test_generates_composite_primary_key_with_unique_components
|
||||
assert_equal 2, cpk_orders(:cpk_groceries_order_1).id.uniq.size
|
||||
end
|
||||
end
|
||||
end
|
||||
|
5
activerecord/test/fixtures/cpk_books.yml
vendored
5
activerecord/test/fixtures/cpk_books.yml
vendored
@ -12,3 +12,8 @@ cpk_great_author_second_book:
|
||||
cpk_famous_author_first_book:
|
||||
title: "Ruby on Rails"
|
||||
revision: 1
|
||||
|
||||
cpk_known_author_david_book:
|
||||
author_id: 1
|
||||
title: "David's CPK Book"
|
||||
revision: 1
|
||||
|
Loading…
Reference in New Issue
Block a user