From e0ef0936193491689724880599ae26a8f5c2b5a6 Mon Sep 17 00:00:00 2001 From: Alexey Muranov Date: Tue, 20 Dec 2011 21:06:30 +0100 Subject: Use foo/bar instead of foo_bar keys for fixtures This solves an issue with set_fixture_class class method caused by create_fixtures method's overwriting passed to it fixture model classes, when the fixtures are "namespased": foo/bar or admin/users. The idea is to use "foo/bar" string as the name and identifier of a fixture file bar in directory foo. The model class of the fixture is either set with set_fixture_class method, or otherwise inferred from its name using camelize method. Also a bug is fixed in lines 487-489 when the table names were guessed by substitution from the fixture file names, ambiguously called table_names, instead of being taken from fixture attributes. Now they are taken from attributes. I plan to submit another fix so that the fixture's table name (for example foo_bar) be defined by the fixture's model whenever possible, instead of inferring it from the fixture's name ("foo/bar"). --- activerecord/lib/active_record/fixtures.rb | 37 ++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index deffc7cec5..b22692de98 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -388,10 +388,10 @@ module ActiveRecord @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} } - def self.find_table_name(table_name) # :nodoc: + def self.default_fixture_model_name(fixture_name) # :nodoc: ActiveRecord::Base.pluralize_table_names ? - table_name.to_s.singularize.camelize : - table_name.to_s.camelize + fixture_name.singularize.camelize : + fixture_name.camelize end def self.reset_cache @@ -441,9 +441,6 @@ module ActiveRecord def self.create_fixtures(fixtures_directory, table_names, class_names = {}) table_names = [table_names].flatten.map { |n| n.to_s } - table_names.each { |n| - class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/') - } # FIXME: Apparently JK uses this. connection = block_given? ? yield : ActiveRecord::Base.connection @@ -458,11 +455,12 @@ module ActiveRecord fixture_files = files_to_read.map do |path| table_name = path.tr '/', '_' + fixture_name = path - fixtures_map[path] = ActiveRecord::Fixtures.new( + fixtures_map[fixture_name] = new( # ActiveRecord::Fixtures.new connection, table_name, - class_names[table_name.to_sym] || table_name.classify, + class_names[fixture_name] || default_fixture_model_name(fixture_name), ::File.join(fixtures_directory, path)) end @@ -723,14 +721,29 @@ module ActiveRecord self.use_instantiated_fixtures = false self.pre_loaded_fixtures = false - self.fixture_class_names = Hash.new do |h, table_name| - h[table_name] = ActiveRecord::Fixtures.find_table_name(table_name) + self.fixture_class_names = Hash.new do |h, fixture_name| + h[fixture_name] = ActiveRecord::Fixtures.default_fixture_model_name(fixture_name) end end module ClassMethods + # Sets the model class for a fixture when the class name cannot be inferred from the fixture name. + # + # Examples: + # + # set_fixture_class :some_fixture => SomeModel, + # 'namespaced/fixture' => Another::Model + # + # The keys must be the fixture names, that coincide with the short paths to the fixture files. + #-- + # It is also possible to pass the class name instead of the class: + # set_fixture_class 'some_fixture' => 'SomeModel' + # I think this option is redundant, i propose to deprecate it. + # Isn't it easier to always pass the class itself? + # (2011-12-20 alexeymuranov) + #++ def set_fixture_class(class_names = {}) - self.fixture_class_names = self.fixture_class_names.merge(class_names) + self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys) end def fixtures(*fixture_names) @@ -770,7 +783,7 @@ module ActiveRecord fixture_names = Array.wrap(fixture_names || fixture_table_names) methods = Module.new do fixture_names.each do |fixture_name| - fixture_name = fixture_name.to_s.tr('./', '_') + fixture_name = fixture_name.to_s.tr('./', '_') # TODO: use fixture_name variable for only one form of fixture names ("admin/users" for example) define_method(fixture_name) do |*fixtures| force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload -- cgit v1.2.3 From 7162ea2a0c75f270eab37217d1daa0268e258e71 Mon Sep 17 00:00:00 2001 From: Alexey Muranov Date: Tue, 20 Dec 2011 21:58:26 +0100 Subject: Fixture's table name be defined in the model Made the fixture's table name be taken from its model class whenever this class responds to table_name, instead of inferring it sometimes from the fixture's pass. The previous behavior seemed buggy because it depended on whether the model class was passed as a constant or as a name string. Improved Fixtures#initialize. --- activerecord/lib/active_record/fixtures.rb | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index b22692de98..77e38e8efd 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -394,6 +394,11 @@ module ActiveRecord fixture_name.camelize end + def self.default_fixture_table_name(fixture_name) # :nodoc: + "#{ActiveRecord::Base.table_name_prefix}"\ + "#{fixture_name.tr('/', '_')}#{ActiveRecord::Base.table_name_suffix}".to_sym + end + def self.reset_cache @@all_cached_fixtures.clear end @@ -454,12 +459,11 @@ module ActiveRecord fixtures_map = {} fixture_files = files_to_read.map do |path| - table_name = path.tr '/', '_' fixture_name = path fixtures_map[fixture_name] = new( # ActiveRecord::Fixtures.new connection, - table_name, + fixture_name, class_names[fixture_name] || default_fixture_model_name(fixture_name), ::File.join(fixtures_directory, path)) end @@ -504,25 +508,28 @@ module ActiveRecord attr_reader :table_name, :name, :fixtures, :model_class - def initialize(connection, table_name, class_name, fixture_path) + def initialize(connection, fixture_name, class_name, fixture_path) @connection = connection - @table_name = table_name @fixture_path = fixture_path - @name = table_name # preserve fixture base name + @name = fixture_name.tr('/', '_') # preserve fixture base name + # TODO: see how it is used and if the substitution can be avoided @class_name = class_name @fixtures = ActiveSupport::OrderedHash.new - @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}" # Should be an AR::Base type class if class_name.is_a?(Class) - @table_name = class_name.table_name - @connection = class_name.connection - @model_class = class_name + @model_class = class_name else - @model_class = class_name.constantize rescue nil + @model_class = class_name.constantize rescue nil end + @connection = model_class.connection if model_class && model_class.respond_to?(:connection) + + @table_name = ( model_class.respond_to?(:table_name) ? + model_class.table_name : + self.class.default_fixture_table_name(fixture_name) ) + read_fixture_files end -- cgit v1.2.3 From b52d9d0ff585f3131e26317542df2971a667d319 Mon Sep 17 00:00:00 2001 From: Alexey Muranov Date: Thu, 29 Dec 2011 16:36:00 +0100 Subject: Fixes for TestFixtures::setup_fixture_accessors Renamed "fixture_name" to "accessor_name" and made fixture names in the form "admin/users" be used as the key for the hashes @fixture_cache and @loaded_fixtures. Previously the variable "fixture_name" here was getting a value of the form "admin_user", and this value was then used as the hash key. --- activerecord/lib/active_record/fixtures.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 77e38e8efd..c59c00f424 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -511,8 +511,7 @@ module ActiveRecord def initialize(connection, fixture_name, class_name, fixture_path) @connection = connection @fixture_path = fixture_path - @name = fixture_name.tr('/', '_') # preserve fixture base name - # TODO: see how it is used and if the substitution can be avoided + @name = fixture_name @class_name = class_name @fixtures = ActiveSupport::OrderedHash.new @@ -790,9 +789,9 @@ module ActiveRecord fixture_names = Array.wrap(fixture_names || fixture_table_names) methods = Module.new do fixture_names.each do |fixture_name| - fixture_name = fixture_name.to_s.tr('./', '_') # TODO: use fixture_name variable for only one form of fixture names ("admin/users" for example) + accessor_name = fixture_name.tr('/', '_').to_sym - define_method(fixture_name) do |*fixtures| + define_method(accessor_name) do |*fixtures| force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload @fixture_cache[fixture_name] ||= {} @@ -805,13 +804,13 @@ module ActiveRecord @fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find end else - raise StandardError, "No fixture with name '#{fixture}' found for table '#{fixture_name}'" + raise StandardError, "No entry named '#{fixture}' found for fixture collection '#{fixture_name}'" end end instances.size == 1 ? instances.first : instances end - private fixture_name + private accessor_name end end include methods -- cgit v1.2.3