<commit_before>module RailsDevelopmentBoost
  class LoadedFile
    class Files < Hash
      def initialize(*args)
        super {|hash, file_path| hash[file_path] = LoadedFile.new(file_path)}
      end
      
      def unload_modified!
        values.each do |file|
          unload_modified_file(file) if file.changed?
        end
      end
      
      def unload_modified_file(file)
        file.unload!
      end
      
      def constants
        values.map(&:constants).flatten
      end
      
      def stored?(file)
        key?(file.path) && self[file.path] == file
      end
    end
    
    class ConstantsToFiles < Hash
      def associate(const_name, file)
        (self[const_name] ||= []) << file
      end
      
      def deassociate(const_name, file)
        if files = self[const_name]
          files.delete(file)
          delete(const_name) if files.empty?
        end
      end
      
      def each_file_with_const(const_name, &block)
        if files = self[const_name]
          files.dup.each(&block)
        end
      end
    end
    
    class Interdependencies < Hash
      def associate(file_a, file_b)
        (self[file_a] ||= []) << file_b
        (self[file_b] ||= []) << file_a
      end
      
      def each_dependent_on(file)
        if deps = delete(file)
          deps.each do |dep|
            deassociate(dep, file)
            yield dep
          end
        end
      end
      
      def []=(file, v)
        super(file.path, v)
      end
      
      def [](file)
        super(file.path)
      end
      
      def delete(file)
        super(file.path)
      end
      
      private
      def deassociate(file_a, file_b)
        if deps = self[file_a]
          deps.delete(file_b)
          delete(file_a) if deps.empty?
        end
      end
    end
    
    LOADED             = Files.new
    CONSTANTS_TO_FILES = ConstantsToFiles.new
    INTERDEPENDENCIES  = Interdependencies.new
    NOW_UNLOADING      = Set.new
    
    attr_accessor :path, :constants
  
    def initialize(path, constants=[])
      @path       = path
      @constants  = constants
      @mtime      = current_mtime
    end
  
    def changed?
      previous_mtime, @mtime = @mtime, current_mtime
      previous_mtime != @mtime
    end
    
    def add_constants(new_constants)
      new_constants.each {|new_constant| CONSTANTS_TO_FILES.associate(new_constant, self)}
      @constants |= new_constants
    end
    
    def unload!
      guard_double_unloading do
        INTERDEPENDENCIES.each_dependent_on(self) {|dependent_file| unload_dependent_file(dependent_file)}
        @constants.dup.each {|const| ActiveSupport::Dependencies.remove_constant(const)}
        clean_up_if_necessary
      end
    end
    
    def unload_dependent_file(dependent_file)
      dependent_file.unload!
    end
    
    def guard_double_unloading
      unless NOW_UNLOADING.include?(self)
        NOW_UNLOADING << self
        begin
          yield
        ensure
          NOW_UNLOADING.delete(self)
        end
      end
    end
    
    def delete_constant(const_name)
      CONSTANTS_TO_FILES.deassociate(const_name, self)
      @constants.delete(const_name)
      clean_up_if_necessary
    end
    
    def clean_up_if_necessary
      if @constants.empty? && LOADED.stored?(self)<commit_msg>Fix LoadedFile's #hash and #eql? instaed of overwriting methods on Interdependencies.<commit_after>module RailsDevelopmentBoost
  class LoadedFile
    class Files < Hash
      def initialize(*args)
        super {|hash, file_path| hash[file_path] = LoadedFile.new(file_path)}
      end
      
      def unload_modified!
        values.each do |file|
          unload_modified_file(file) if file.changed?
        end
      end
      
      def unload_modified_file(file)
        file.unload!
      end
      
      def constants
        values.map(&:constants).flatten
      end
      
      def stored?(file)
        key?(file.path) && self[file.path] == file
      end
    end
    
    class ConstantsToFiles < Hash
      def associate(const_name, file)
        (self[const_name] ||= []) << file
      end
      
      def deassociate(const_name, file)
        if files = self[const_name]
          files.delete(file)
          delete(const_name) if files.empty?
        end
      end
      
      def each_file_with_const(const_name, &block)
        if files = self[const_name]
          files.dup.each(&block)
        end
      end
    end
    
    class Interdependencies < Hash
      def associate(file_a, file_b)
        (self[file_a] ||= []) << file_b
        (self[file_b] ||= []) << file_a
      end
      
      def each_dependent_on(file)
        if deps = delete(file)
          deps.each do |dep|
            deassociate(dep, file)
            yield dep
          end
        end
      end
      
      private
      def deassociate(file_a, file_b)
        if deps = self[file_a]
          deps.delete(file_b)
          delete(file_a) if deps.empty?
        end
      end
    end
    
    LOADED             = Files.new
    CONSTANTS_TO_FILES = ConstantsToFiles.new
    INTERDEPENDENCIES  = Interdependencies.new
    NOW_UNLOADING      = Set.new
    
    attr_accessor :path, :constants
  
    def initialize(path, constants=[])
      @path       = path
      @constants  = constants
      @mtime      = current_mtime
    end
  
    def changed?
      previous_mtime, @mtime = @mtime, current_mtime
      previous_mtime != @mtime
    end
    
    def add_constants(new_constants)
      new_constants.each {|new_constant| CONSTANTS_TO_FILES.associate(new_constant, self)}
      @constants |= new_constants
    end
    
    def unload!
      guard_double_unloading do
        INTERDEPENDENCIES.each_dependent_on(self) {|dependent_file| unload_dependent_file(dependent_file)}
        @constants.dup.each {|const| ActiveSupport::Dependencies.remove_constant(const)}
        clean_up_if_necessary
      end
    end
    
    # consistent hashing
    def hash
      @path.hash
    end
    
    def eql?(other)
      @path.eql?(other)
    end
    
    def unload_dependent_file(dependent_file)
      dependent_file.unload!
    end
    
    def guard_double_unloading
      unless NOW_UNLOADING.include?(self)
        NOW_UNLOADING << self
        begin
          yield
        ensure
          NOW_UNLOADING.delete(self)
        end
      end
    end
    
    def delete_constant(const_name)
      CONSTANTS_TO_FILES.deassociate(const_name, self)
      @constants.delete(const_name)
      clean_up_if_necessary
    end
    
    def clean_up_if_necessary
      if @constants.empty? && LOADED.stored?(self)
{'fraction_non_alphanumeric': 0.07830687830687831, 'fraction_numerical': 0.0, 'mean_word_length': 1.9158219479947114, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3213878', 'n_tokens_mistral': 2110, 'n_tokens_neox': 2100, 'n_words': 417}