<commit_before>require 'sequel'
require 'terminal-table'

Sequel.extension :inflector

class AnnotateSequel
  module Model
    class << self
      def model_dir
        @model_dir || "app/models"
      end

      def model_dir=(dir)
        @model_dir = dir
      end

      def schema_info(klass)
        fks = process_fks(klass)

        table = Terminal::Table.new
        table.title = klass.table_name
        table.headings = ["Column", "Ruby Type", "DB Type", "Default", "Null?", "PK?", "FK?"]

        table.rows = klass.db_schema.map do |key, value|
          [ key,
            value[:type],
            value[:db_type],
            value[:ruby_default] || '-',
            value[:allow_null]  ? 'Y' : 'N',
            value[:primary_key] ? 'Y' : 'N',
            fks.include?(key)   ? 'Y' : 'N'
          ]
        end

        # Align to the center the columns:
        # Default, Null?, PK? and FK?
        for i in 3..6
          table.align_column(i, :center)
        end

        # Comment the table
        output = String.new
        table.to_s.each_line { |line| output << "# #{line}" }

        output << "\n\n"
      end

      def process_fks(model)
        model.db.foreign_key_list(model.table_name).map do |x|
          x[:columns]
        end.flatten
      end

      def get_model_files
        models = []
        begin
          Dir.chdir(model_dir) do
            models = Dir["**/*.rb"]
          end
        rescue SystemCallError
          puts "No models found in directory '#{model_dir}'."
          exit 1
        end
        models
      end

      def get_model_class(file)
        require File.expand_path("#{model_dir}/#{file}")
        model_path = file.gsub(/\.rb$/, '')
        get_loaded_model(model_path) || get_loaded_model(model_path.split("/").last)
      end

      def get_loaded_model(model_path)
        ObjectSpace.each_object(::Class).
          select do |c|
            Class === c and
            c.ancestors.respond_to?(:include?) and
            c.ancestors.include?(Sequel::Model)
          end.
          detect { |c| c.name.demodulize.underscore == model_path }
      end

      def annotate_model_file(annotated, file)
        begin
          klass = get_model_class(file)
          if klass && annotate(klass, file)
            annotated << klass.to_s.demodulize
          end
        rescue Exception => e
          puts "Unable to annotate #{file}: #{e.message}"
          puts "\t" + e.backtrace.join("\n\t")
        end
      end

      def do_annotations
        annotated = []

        get_model_files.each do |file|
          annotate_model_file(annotated, file)
        end

        puts annotated.empty? ?
          "Nothing annotated" :
          "Annotated (#{annotated.length}): #{annotated.join(', ')}"
      end
<commit_msg>Prevent anonymous classes from cascading<commit_after>require 'sequel'
require 'terminal-table'

Sequel.extension :inflector

class AnnotateSequel
  module Model
    class << self
      def model_dir
        @model_dir || "app/models"
      end

      def model_dir=(dir)
        @model_dir = dir
      end

      def schema_info(klass)
        fks = process_fks(klass)

        table = Terminal::Table.new
        table.title = klass.table_name
        table.headings = ["Column", "Ruby Type", "DB Type", "Default", "Null?", "PK?", "FK?"]

        table.rows = klass.db_schema.map do |key, value|
          [ key,
            value[:type],
            value[:db_type],
            value[:ruby_default] || '-',
            value[:allow_null]  ? 'Y' : 'N',
            value[:primary_key] ? 'Y' : 'N',
            fks.include?(key)   ? 'Y' : 'N'
          ]
        end

        # Align to the center the columns:
        # Default, Null?, PK? and FK?
        for i in 3..6
          table.align_column(i, :center)
        end

        # Comment the table
        output = String.new
        table.to_s.each_line { |line| output << "# #{line}" }

        output << "\n\n"
      end

      def process_fks(model)
        model.db.foreign_key_list(model.table_name).map do |x|
          x[:columns]
        end.flatten
      end

      def get_model_files
        models = []
        begin
          Dir.chdir(model_dir) do
            models = Dir["**/*.rb"]
          end
        rescue SystemCallError
          puts "No models found in directory '#{model_dir}'."
          exit 1
        end
        models
      end

      def get_model_class(file)
        require File.expand_path("#{model_dir}/#{file}")
        model_path = file.gsub(/\.rb$/, '')
        get_loaded_model(model_path) || get_loaded_model(model_path.split("/").last)
      end

      def get_loaded_model(model_path)
        ObjectSpace.each_object(::Class).
          select do |c|
            Class === c and
            c.ancestors.respond_to?(:include?) and
            c.ancestors.include?(Sequel::Model)
          end.
          detect { |c| c.name && c.name.demodulize.underscore == model_path }
      end

      def annotate_model_file(annotated, file)
        begin
          klass = get_model_class(file)
          if klass && annotate(klass, file)
            annotated << klass.to_s.demodulize
          end
        rescue Exception => e
          puts "Unable to annotate #{file}: #{e.message}"
          puts "\t" + e.backtrace.join("\n\t")
        end
      end

      def do_annotations
        annotated = []

        get_model_files.each do |file|
          annotate_model_file(annotated, file)
        end

        puts annotated.empty? ?
          "Nothing annotated" :
          "Annotated (#{annotated.length}): #{annotated.join(', ')}"
      end

{'fraction_non_alphanumeric': 0.12214146427938309, 'fraction_numerical': 0.0010636411983690835, 'mean_word_length': 1.7361784675072744, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5774373', 'n_tokens_mistral': 1788, 'n_tokens_neox': 1748, 'n_words': 387}