Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #1911: more_flexible_fixture_architecture_without_breakpoint.patch

File more_flexible_fixture_architecture_without_breakpoint.patch, 21.1 kB (added by duane.johnson@gmail.com, 3 years ago)

Removed an errant breakpoint and nearby comment

  • test/fixtures_test.rb

    old new  
    6868    end 
    6969  end 
    7070 
     71  def test_fixtures_not_found 
     72    assert_raise(Fixture::FixtureError) { 
     73      Fixtures.new(nil, File.join(File.dirname(__FILE__), 'fixtures'), FixtureGroup.new("bad_extension")) 
     74    } 
     75  end 
     76 
    7177  def test_deprecated_yaml_extension 
    7278    assert_raise(Fixture::FormatError) { 
    73       Fixtures.new(nil, 'bad_extension', File.join(File.dirname(__FILE__), 'fixtures')) 
     79      Fixtures.new(nil, File.join(File.dirname(__FILE__), 'fixtures', 'bad_fixtures'), FixtureGroup.new("deprecated")) 
    7480    } 
    7581  end 
    7682 
     
    101107  end 
    102108 
    103109  def test_empty_yaml_fixture 
    104     assert_not_nil Fixtures.new( Account.connection, "accounts", File.dirname(__FILE__) + "/fixtures/naked/yml/accounts"
     110    assert_not_nil Fixtures.new( Account.connection, File.dirname(__FILE__) + "/fixtures/naked/yml/", FixtureGroup.new("accounts")
    105111  end 
    106112 
    107113  def test_empty_yaml_fixture_with_a_comment_in_it 
    108     assert_not_nil Fixtures.new( Account.connection, "companies", File.dirname(__FILE__) + "/fixtures/naked/yml/companies"
     114    assert_not_nil Fixtures.new( Account.connection, File.dirname(__FILE__) + "/fixtures/naked/yml/", FixtureGroup.new("companies")
    109115  end 
    110116 
    111117  def test_dirty_dirty_yaml_file 
    112118    assert_raises(Fixture::FormatError) do 
    113       Fixtures.new( Account.connection, "courses", File.dirname(__FILE__) + "/fixtures/naked/yml/courses"
     119      Fixtures.new( Account.connection, File.dirname(__FILE__) + "/fixtures/naked/yml/", FixtureGroup.new("courses")
    114120    end 
    115121  end 
    116122 
    117123  def test_empty_csv_fixtures 
    118     assert_not_nil Fixtures.new( Account.connection, "accounts", File.dirname(__FILE__) + "/fixtures/naked/csv/accounts"
     124    assert_not_nil Fixtures.new( Account.connection, File.dirname(__FILE__) + "/fixtures/naked/csv/", FixtureGroup.new("accounts")
    119125  end 
    120126end 
    121127 
     
    212218 
    213219end 
    214220 
     221class MultipleFixturesForTheSameTableTest < Test::Unit::TestCase 
     222  self.use_instantiated_fixtures = true 
     223  fixture :topics_set_one, :table_name => "topics", :file_name => "topics" 
     224  fixture :topics_set_two, :table_name => "topics", :file_name => "topics2" 
     225   
     226  def test_loaded_fixtures 
     227    assert_equal @topics_set_one.size, 2 
     228    assert_equal @topics_set_two.size, 2 
     229    union = Topic.find(:all) 
     230    assert_equal union.size, 4 
     231  end 
     232end 
    215233 
     234class MixSingularAndPluralFixturesTest < Test::Unit::TestCase 
     235  self.use_instantiated_fixtures = true 
     236  fixtures :topics, :developers, :accounts 
     237  fixture :additional_topics, :table_name => "topics", :file_name => "topics2" 
     238   
     239  def test_loaded_fixtures 
     240    assert_equal @topics.size, 2 
     241    assert_equal @additional_topics.size, 2 
     242    union = Topic.find(:all) 
     243    assert_equal union.size, 4 
     244  end 
     245end 
    216246 
    217  
    218  
    219  
     247class AlternateFileFixturesTest < Test::Unit::TestCase 
     248  self.use_instantiated_fixtures = true 
     249  fixture :topics, :file_name => "topics2" 
     250   
     251  def test_alternate_fixture_file_was_loaded 
     252    assert_equal @topics.size, 2 
     253    assert_nil @first 
     254    assert_nil @second 
     255    assert_equal @third.id, 3 
     256    assert_equal @fourth.id, 4 
     257    topics = Topic.find(:all) 
     258    assert_equal topics.size, 2 
     259  end 
     260end 
  • test/fixtures/topics2.yml

    old new  
     1third: 
     2  id: 3 
     3  title: The Third Topic 
     4  author_name: Duane 
     5  author_email_address: david@loudthinking.com 
     6  written_on: 2003-07-16t15:28:00.00+01:00 
     7  last_read: 2004-04-15 
     8  bonus_time: 2005-01-30t15:28:00.00+01:00 
     9  content: Have a nice day 
     10  approved: 0 
     11  replies_count: 0 
     12 
     13fourth: 
     14  id: 4 
     15  title: The Fourth topic 
     16  author_name: John 
     17  written_on: 2003-07-15t15:28:00.00+01:00 
     18  content: Have a nice day 
     19  approved: 1 
     20  replies_count: 2 
     21  parent_id: 1 
  • lib/active_record/fixtures.rb

    old new  
    22require 'yaml' 
    33require 'csv' 
    44 
     5# A FixtureGroup is a set of fixtures identified by a name.  Normally, this is the name of the 
     6# corresponding table in the database.  For example, when you declare the use of fixtures in a 
     7# TestUnit class, like so: 
     8#   fixtures :users 
     9# you are creating a FixtureGroup whose name is 'users', and whose defaults are set such that the 
     10# +class_name+, +file_name+ and +table_name+ are guessed from the FixtureGroup's name. 
     11class FixtureGroup 
     12  attr_accessor :table_name, :class_name, :file_name 
     13  attr_reader :group_name 
     14 
     15  def initialize(group_name, optional_names = {}) 
     16    self.group_name = group_name 
     17    self.table_name = optional_names[:table_name] || @group_name.to_s 
     18    self.file_name = optional_names[:file_name]   || @table_name.to_s.gsub('.','_') 
     19    self.class_name = optional_names[:class_name] || Inflector.classify(@table_name.to_s.gsub('.','_')) 
     20  end 
     21 
     22  def group_name=(name) 
     23    @group_name = name.to_sym 
     24  end 
     25 
     26  def class_file_name 
     27    Inflector.underscore(@class_name) 
     28  end 
     29   
     30  # Instantiate an array of FixtureGroup objects from an array of strings (table_names) 
     31  def self.array_from_names(names) 
     32    names.collect { |n| FixtureGroup.new(n) } 
     33  end 
     34   
     35  def hash 
     36    @group_name.hash 
     37  end 
     38   
     39  def eql?(other) 
     40    @group_name.eql? other.group_name 
     41  end 
     42end 
     43 
    544# Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavours: 
    645# 
    746#   1.  YAML fixtures 
     
    194233class Fixtures < Hash 
    195234  DEFAULT_FILTER_RE = /\.ya?ml$/ 
    196235 
    197   def self.instantiate_fixtures(object, table_name, fixtures, load_instances=true) 
    198     old_logger_level = ActiveRecord::Base.logger.level 
    199     ActiveRecord::Base.logger.level = Logger::ERROR 
    200  
    201     object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures 
    202     if load_instances 
    203       fixtures.each do |name, fixture| 
    204         if model = fixture.find 
    205           object.instance_variable_set "@#{name}", model 
    206         end 
    207       end 
    208     end 
    209  
    210     ActiveRecord::Base.logger.level = old_logger_level 
    211   end 
    212    
    213   def self.instantiate_all_loaded_fixtures(object, load_instances=true) 
    214     all_loaded_fixtures.each do |table_name, fixtures| 
    215       Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances) 
    216     end 
    217   end 
    218    
    219236  cattr_accessor :all_loaded_fixtures 
    220237  self.all_loaded_fixtures = {} 
    221238 
    222   def self.create_fixtures(fixtures_directory, *table_names) 
    223     connection = block_given? ? yield : ActiveRecord::Base.connection 
    224     old_logger_level = ActiveRecord::Base.logger.level 
     239  attr_accessor :connection, :fixtures_directory, :file_filter 
     240  attr_accessor :fixture_group 
    225241 
    226     begin 
    227       ActiveRecord::Base.logger.level = Logger::ERROR 
    228  
    229       fixtures_map = {} 
    230       fixtures = table_names.flatten.map do |table_name| 
    231         fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, File.join(fixtures_directory, table_name.to_s)) 
    232       end                
    233       all_loaded_fixtures.merge! fixtures_map   
    234        
    235       connection.transaction do 
    236         fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } 
    237         fixtures.each { |fixture| fixture.insert_fixtures } 
    238       end 
    239        
    240       reset_sequences(connection, table_names) if connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) 
    241  
    242       return fixtures.size > 1 ? fixtures : fixtures.first 
    243     ensure 
    244       ActiveRecord::Base.logger.level = old_logger_level 
    245     end 
    246   end 
    247  
    248   # Work around for PostgreSQL to have new fixtures created from id 1 and running. 
    249   def self.reset_sequences(connection, table_names) 
    250     table_names.flatten.each do |table| 
    251       table_class = Inflector.classify(table.to_s) 
    252       if Object.const_defined?(table_class) 
    253         pk = eval("#{table_class}::primary_key") 
    254         if pk == 'id' 
    255           connection.execute( 
    256             "SELECT setval('#{table.to_s}_id_seq', (SELECT MAX(id) FROM #{table.to_s}), true)",  
    257             'Setting Sequence' 
    258           ) 
    259         end 
    260       end 
    261     end 
    262   end 
    263  
    264   attr_reader :table_name 
    265  
    266   def initialize(connection, table_name, fixture_path, file_filter = DEFAULT_FILTER_RE) 
    267     @connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter 
    268     @class_name = Inflector.classify(@table_name) 
    269  
     242  def initialize(connection, fixtures_directory, fixture_group, file_filter = DEFAULT_FILTER_RE) 
     243    @connection, @fixtures_directory = connection, fixtures_directory 
     244    @fixture_group = fixture_group 
     245    @file_filter = file_filter 
    270246    read_fixture_files 
    271247  end 
    272248 
    273249  def delete_existing_fixtures 
    274     @connection.delete "DELETE FROM #{@table_name}", 'Fixture Delete' 
     250    @connection.delete "DELETE FROM #{@fixture_group.table_name}", 'Fixture Delete' 
    275251  end 
    276252 
    277253  def insert_fixtures 
    278254    values.each do |fixture| 
    279       @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert' 
     255      @connection.execute "INSERT INTO #{@fixture_group.table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert' 
    280256    end 
    281257  end 
    282258 
    283259  private 
    284260    def read_fixture_files 
    285261      if File.file?(yaml_file_path) 
    286         # YAML fixtures 
    287         begin 
    288           yaml = YAML::load(erb_render(IO.read(yaml_file_path))) 
    289           yaml.each { |name, data| self[name] = Fixture.new(data, @class_name) } if yaml 
    290         rescue Exception=>boom 
    291           raise Fixture::FormatError, "a YAML error occured parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n  #{boom.class}: #{boom}" 
    292         end 
     262        read_yaml_fixture_files 
    293263      elsif File.file?(csv_file_path) 
    294         # CSV fixtures 
    295         reader = CSV::Reader.create(erb_render(IO.read(csv_file_path))) 
    296         header = reader.shift 
    297         i = 0 
    298         reader.each do |row| 
    299           data = {} 
    300           row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip } 
    301           self["#{Inflector::underscore(@class_name)}_#{i+=1}"]= Fixture.new(data, @class_name) 
    302         end 
     264        read_csv_fixture_files 
    303265      elsif File.file?(deprecated_yaml_file_path) 
    304266        raise Fixture::FormatError, ".yml extension required: rename #{deprecated_yaml_file_path} to #{yaml_file_path}" 
     267      elsif File.directory?(File.join(@fixtures_directory, @fixture_group.file_name)) 
     268        read_standard_fixture_files 
    305269      else 
    306         # Standard fixtures 
    307         Dir.entries(@fixture_path).each do |file| 
    308           path = File.join(@fixture_path, file) 
    309           if File.file?(path) and file !~ @file_filter 
    310             self[file] = Fixture.new(path, @class_name) 
    311           end 
     270        raise Fixture::FixtureError, "Couldn't find a yaml, csv or standard file to load at #{@fixtures_directory}." 
     271      end 
     272    end 
     273 
     274    def read_yaml_fixture_files 
     275      # YAML fixtures 
     276      begin 
     277        yaml = YAML::load(erb_render(IO.read(yaml_file_path))) 
     278        yaml.each { |name, data| self[name] = Fixture.new(data, @fixture_group.class_name) } if yaml 
     279      rescue Exception=>boom 
     280        raise Fixture::FormatError, "a YAML error occured parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n  #{boom.class}: #{boom}" 
     281      end 
     282    end 
     283 
     284    def read_csv_fixture_files 
     285      # CSV fixtures 
     286      reader = CSV::Reader.create(erb_render(IO.read(csv_file_path))) 
     287      header = reader.shift 
     288      i = 0 
     289      reader.each do |row| 
     290        data = {} 
     291        row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip } 
     292        self["#{fixture_group.class_file_name}_#{i+=1}"]= Fixture.new(data, @fixture_group.class_name) 
     293      end 
     294    end 
     295 
     296    def read_standard_fixture_files 
     297      # Standard fixtures 
     298      path = File.join(@fixtures_directory, @fixture_group.file_name) 
     299      Dir.entries(path).each do |file| 
     300        path = File.join(@fixtures_directory, file) 
     301        if File.file?(path) and file !~ @file_filter 
     302          self[file] = Fixture.new(path, @fixture_group.class_name) 
    312303        end 
    313304      end 
    314305    end 
    315306 
    316307    def yaml_file_path 
    317       "#{@fixture_path}.yml" 
     308      fixture_path_with_extension ".yml" 
    318309    end 
    319310 
    320311    def deprecated_yaml_file_path 
    321       "#{@fixture_path}.yaml" 
     312      fixture_path_with_extension ".yaml" 
    322313    end 
    323314 
    324315    def csv_file_path 
    325       @fixture_path + ".csv" 
     316      fixture_path_with_extension ".csv" 
    326317    end 
    327318 
    328     def yaml_fixtures_key(path
    329       File.basename(@fixture_path).split(".").first 
    330     end 
     319    def fixture_path_with_extension(ext
     320      File.join(@fixtures_directory, @fixture_group.file_name + ext) 
     321    end       
    331322 
    332323    def erb_render(fixture_content) 
    333324      ERB.new(fixture_content).result 
    334325    end 
     326 
     327    #def yaml_fixtures_key(path) 
     328    #  File.basename(@fixture_path).split(".").first 
     329    #end 
     330 
     331  public 
     332    class << self 
     333      def instantiate_fixtures(object, fixture_group_name, fixtures, load_instances=true) 
     334        old_logger_level = ActiveRecord::Base.logger.level 
     335        ActiveRecord::Base.logger.level = Logger::ERROR 
     336 
     337        # table_name.to_s.gsub('.','_') replaced by 'fixture_group_name' 
     338        object.instance_variable_set "@#{fixture_group_name}", fixtures 
     339        if load_instances 
     340          fixtures.each do |name, fixture| 
     341            if model = fixture.find 
     342              object.instance_variable_set "@#{name}", model 
     343            end 
     344          end 
     345        end 
     346 
     347        ActiveRecord::Base.logger.level = old_logger_level 
     348      end 
     349 
     350      def instantiate_all_loaded_fixtures(object, load_instances=true) 
     351        all_loaded_fixtures.each do |fixture_group_name, fixtures| 
     352          Fixtures.instantiate_fixtures(object, fixture_group_name, fixtures, load_instances) 
     353        end 
     354      end 
     355 
     356 
     357      def create_fixtures(fixtures_directory, *fixture_groups) 
     358        connection = block_given? ? yield : ActiveRecord::Base.connection 
     359        old_logger_level = ActiveRecord::Base.logger.level 
     360        fixture_groups.flatten! 
     361         
     362        # Backwards compatibility: Allow an array of table names to be passed in, but just use them 
     363        # to create an array of FixtureGroup objects 
     364        if not fixture_groups.empty? and fixture_groups.first.is_a?(String) 
     365          fixture_groups = FixtureGroup.array_from_names(fixture_groups) 
     366        end 
     367 
     368        begin 
     369          ActiveRecord::Base.logger.level = Logger::ERROR 
     370 
     371          fixtures_map = {} 
     372          fixtures = fixture_groups.map do |group| 
     373            fixtures_map[group.group_name] = Fixtures.new(connection, fixtures_directory, group) 
     374          end                
     375          # Make sure all refs to all_loaded_fixtures use group_name as hash index, not table_name 
     376          all_loaded_fixtures.merge! fixtures_map   
     377 
     378          connection.transaction do 
     379            fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } 
     380            fixtures.each { |fixture| fixture.insert_fixtures } 
     381          end 
     382 
     383          reset_sequences(connection, fixture_groups) if connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) 
     384 
     385          return fixtures.size > 1 ? fixtures : fixtures.first 
     386        ensure 
     387          ActiveRecord::Base.logger.level = old_logger_level 
     388        end 
     389      end 
     390 
     391      # Work around for PostgreSQL to have new fixtures created from id 1 and running. 
     392      def reset_sequences(connection, fixture_groups) 
     393        fixture_groups.flatten.each do |group| 
     394          if Object.const_defined?(group.class_name) 
     395            pk = eval("#{group.class_name}::primary_key") 
     396            if pk == 'id' 
     397              connection.execute( 
     398                "SELECT setval('#{group.table_name}_id_seq', (SELECT MAX(id) FROM #{group.table_name}), true)",  
     399                'Setting Sequence' 
     400              ) 
     401            end 
     402          end 
     403        end 
     404      end 
     405    end 
    335406end 
    336407 
    337408class Fixture #:nodoc: 
     
    399470    class TestCase #:nodoc: 
    400471      include ClassInheritableAttributes 
    401472 
    402       cattr_accessor :fixture_path 
    403       class_inheritable_accessor :fixture_table_name
     473      cattr_accessor :fixtures_directory 
     474      class_inheritable_accessor :fixture_group
    404475      class_inheritable_accessor :use_transactional_fixtures 
    405476      class_inheritable_accessor :use_instantiated_fixtures   # true, false, or :no_instances 
    406477      class_inheritable_accessor :pre_loaded_fixtures 
    407478 
    408       self.fixture_table_names = [] 
     479      self.fixture_groups = [] 
    409480      self.use_transactional_fixtures = false 
    410481      self.use_instantiated_fixtures = true 
    411482      self.pre_loaded_fixtures = false 
    412483 
    413484      @@already_loaded_fixtures = {} 
    414485 
    415       def self.fixtures(*table_names) 
    416         table_names = table_names.flatten 
    417         self.fixture_table_names |= table_names 
    418         require_fixture_classes(table_names) 
    419         setup_fixture_accessors(table_names) 
     486      # Backwards compatibility 
     487      def self.fixture_path=(path); self.fixtures_directory = path; end 
     488      def self.fixture_path; self.fixtures_directory; end 
     489      # It would be more appropriate to call this 'fixture_group_names', but it's for back compat. 
     490      def fixture_table_names; fixture_groups.collect { |g| g.group_name }; end 
     491 
     492      def self.fixture(group_name, options = {}) 
     493        self.fixture_groups |= [FixtureGroup.new(group_name, options)] 
     494        require_fixture_classes 
     495        setup_fixture_accessors 
    420496      end 
    421497 
    422       def self.require_fixture_classes(table_names=nil) 
    423         (table_names || fixture_table_names).each do |table_name|  
     498      def self.fixtures(*group_names) 
     499        self.fixture_groups |= FixtureGroup.array_from_names(group_names.flatten) 
     500        require_fixture_classes 
     501        setup_fixture_accessors 
     502      end 
     503 
     504      def self.require_fixture_classes(override_fixture_groups=nil) 
     505        (override_fixture_groups || fixture_groups).each do |group|  
    424506          begin 
    425             require Inflector.singularize(table_name.to_s) 
     507            require group.class_file_name 
    426508          rescue LoadError 
    427509            # Let's hope the developer has included it himself 
    428510          end 
    429511        end 
    430512      end 
    431513 
    432       def self.setup_fixture_accessors(table_names=nil) 
    433         (table_names || fixture_table_names).each do |table_name
    434           table_name = table_name.to_s.tr('.','_') 
    435           define_method(table_name) do |fixture, *optionals| 
     514      def self.setup_fixture_accessors(override_fixture_groups=nil) 
     515        (override_fixture_groups || fixture_groups).each do |group
     516          # table_name = table_name.to_s.tr('.','_') 
     517          define_method(group.group_name) do |fixture, *optionals| 
    436518            force_reload = optionals.shift 
    437             @fixture_cache[table_name] ||= Hash.new 
    438             @fixture_cache[table_name][fixture] = nil if force_reload 
    439             @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find 
     519            @fixture_cache[group.group_name] ||= Hash.new 
     520            @fixture_cache[group.group_name][fixture] = nil if force_reload 
     521            @fixture_cache[group.group_name][fixture] ||= @loaded_fixtures[group.group_name][fixture.to_s].find 
    440522          end 
    441523        end 
    442524      end 
     
    519601      private 
    520602        def load_fixtures 
    521603          @loaded_fixtures = {} 
    522           fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names) 
     604          fixtures = Fixtures.create_fixtures(fixtures_directory, fixture_groups) 
    523605          unless fixtures.nil? 
    524606            if fixtures.instance_of?(Fixtures) 
    525               @loaded_fixtures[fixtures.table_name] = fixtures 
     607              @loaded_fixtures[fixtures.fixture_group.group_name] = fixtures 
    526608            else 
    527               fixtures.each { |f| @loaded_fixtures[f.table_name] = f } 
     609              fixtures.each { |f| @loaded_fixtures[f.fixture_group.group_name] = f } 
    528610            end 
    529611          end 
    530612        end 
     
    536618          if pre_loaded_fixtures 
    537619            raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty? 
    538620            unless @@required_fixture_classes 
    539               self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys 
     621              groups = Fixtures.all_loaded_fixtures.values.collect { |f| f.group_name } 
     622              self.class.require_fixture_classes groups 
    540623              @@required_fixture_classes = true 
    541624            end 
    542625            Fixtures.instantiate_all_loaded_fixtures(self, load_instances?) 
    543626          else 
    544627            raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil? 
    545             @loaded_fixtures.each do |table_name, fixtures| 
    546               Fixtures.instantiate_fixtures(self, table_name, fixtures, load_instances?) 
     628            @loaded_fixtures.each do |fixture_group_name, fixtures| 
     629              Fixtures.instantiate_fixtures(self, fixture_group_name, fixtures, load_instances?) 
    547630            end 
    548631          end 
    549632        end