<commit_before># This is a hybrid between HoboFields and model annotators.
#
# It works like other annotators, by updating the model annotations from the DB schema.
# But the annotations are syntactic Ruby as in HoboFields rather than comments.
#
# Apart from looking better to my eyes, this allows triggering special functionality 
# from the field declations (such as specifying validations).
#
# The annotations are kept up to date by the migration tasks.
# Comments and validation, etc. specifications modified manually are preserved, at least
# if the field block syntax is kept as generated (one line per field, one line for the
# block start and end...)
#
# Custom type fields and hooks can be define in files (e.g. fields.rb) in config/initializers/
#
module ModalFields
  
  SPECIFIERS = [:indexed, :unique, :required]
  COMMON_ATTRIBUTES = {:default=>nil, :null=>true}
  
  class FieldDeclaration < Struct.new(:name, :type, :specifiers, :attributes)
    
    def self.declare(name, type, *args)
      attributes = args.extract_options!
      new(name, type, args, attributes)
    end
    
    def replace!(replacements={})
      replacements.each_pair do |key, value|
        self[key] = value
      end
      self
    end
    
    def remove_attributes!(*attrs)
      self.attributes = self.attributes.except(*attrs)
      self
    end
    
    def to_s
      code = "#{name} :#{type}"
      code << ", "+specifiers.inspect[1...-1] unless specifiers.empty?
      unless attributes.empty?
        code << ", "+attributes.keys.sort_by{|attr| attr.to_s}.map{|attr|
          v = attributes[attr]
          v = v.kind_of?(BigDecimal) ? "BigDecimal('#{v.to_s}')" : v.inspect
          ":#{attr}=>#{v}"
        }*", "
      end
      code
    end
    
  end
  
  
  class DefinitionsDsl
    def field(name, attributes={})
      ModalFields.definitions[name.to_sym] = COMMON_ATTRIBUTES.merge(attributes)
    end
    def method_missing(name, *args)
      field(name, *args)
    end
  end
  
  class HooksDsl
    def field_type(type, &blk)
      ModalFields.hooks[type.to_sym] = lambda{|model, column_declaration|
        blk[model, column_declaration]
      }
    end
    # geric filter applied to all the fields (after a specific filter for the type, if there is one)
    def all_fields(&blk)
      field_type :all_fields, &blk
    end
    def method_missing(name, *args, &blk)
      field_type name, *args, &blk
    end
  end

  class DeclarationsDsl
    def initialize(model)
      @model = model
    end
    def field(name, type, *args)
      declaration = FieldDeclaration.declare(name, type, *args)
      specific_hook = ModalFields.hooks[type.to_sym]
      general_hook = ModalFields.hooks[:all_fields]
      [specific_hook, general_hook].compact.each do |hook|
        hook[@model, declaration] if hook
      end
      if ModalFields.validate(declaration)
        @model.fields_info << declaration
      end      
    end
    def timestamps
      field :created_at, :datetime
      field :updated_at, :datetime
    end
    def method_missing(name, type, *args)
      field(name, type, *args)
    end
  end

  module FieldDeclarationClassMethods
    def fields(&blk)
      @fields_info ||= []
      unless self.respond_to?(:fields_info)
        self.instance_eval do
          def fields_info
            @fields_info
          end
        end
      end
      DeclarationsDsl.new(self).instance_eval(&blk)
    end    
  end

  @show_primary_keys = false
  @hooks = {}
  @definitions = {}
  @column_to_field_declaration_hook = nil
  
  class <<self
    attr_reader :hooks, :definitions
    # Define declaration of primary keys
    #   ModalFields.show_primary_keys = false # the default: do not show primary keys
    #   ModalFields.show_primary_keys = true  # always declare primary keys
    #   ModalFields.show_primary_keys = :id   # only declare if named 'id' (otherwise the model will have a primary_key declaration)
    #   ModalFields.show_primary_keys = :except_id   # only declare if named differently from 'id'
    attr_accessor :show_primary_keys
    
    # Run a definition block that executes field type definitions
    def define(&blk)
      DefinitionsDsl.new.instance_eval(&blk)
    end
    
    # Run a hooks block that defines field declaration processors
    def hook(&blk)
      HooksDsl.new.instance_eval(&blk)
    end
    
    # Define a custom column to field declaration conversion
    def column_to_field_declaration(&blk)
      @column_to_field_declaration_hook = blk
    end
    
    # Enable the ModalFields plugin (adds the fields declarator to model classes)
    def enable
      if defined?(::Rails)        
        # class ::ActiveRecord::Base
        #   extend FieldDeclarationClassMethods          
        # end
        ::ActiveRecord::Base.send :extend, FieldDeclarationClassMethods
      end
    end
    
    # Update the field declarations of all the models.
    # This modifies the source files of all the models (touches only the fields block or adds one if not present).<commit_msg>Fix unnecessary use of send.<commit_after># This is a hybrid between HoboFields and model annotators.
#
# It works like other annotators, by updating the model annotations from the DB schema.
# But the annotations are syntactic Ruby as in HoboFields rather than comments.
#
# Apart from looking better to my eyes, this allows triggering special functionality 
# from the field declations (such as specifying validations).
#
# The annotations are kept up to date by the migration tasks.
# Comments and validation, etc. specifications modified manually are preserved, at least
# if the field block syntax is kept as generated (one line per field, one line for the
# block start and end...)
#
# Custom type fields and hooks can be define in files (e.g. fields.rb) in config/initializers/
#
module ModalFields
  
  SPECIFIERS = [:indexed, :unique, :required]
  COMMON_ATTRIBUTES = {:default=>nil, :null=>true}
  
  class FieldDeclaration < Struct.new(:name, :type, :specifiers, :attributes)
    
    def self.declare(name, type, *args)
      attributes = args.extract_options!
      new(name, type, args, attributes)
    end
    
    def replace!(replacements={})
      replacements.each_pair do |key, value|
        self[key] = value
      end
      self
    end
    
    def remove_attributes!(*attrs)
      self.attributes = self.attributes.except(*attrs)
      self
    end
    
    def to_s
      code = "#{name} :#{type}"
      code << ", "+specifiers.inspect[1...-1] unless specifiers.empty?
      unless attributes.empty?
        code << ", "+attributes.keys.sort_by{|attr| attr.to_s}.map{|attr|
          v = attributes[attr]
          v = v.kind_of?(BigDecimal) ? "BigDecimal('#{v.to_s}')" : v.inspect
          ":#{attr}=>#{v}"
        }*", "
      end
      code
    end
    
  end
  
  
  class DefinitionsDsl
    def field(name, attributes={})
      ModalFields.definitions[name.to_sym] = COMMON_ATTRIBUTES.merge(attributes)
    end
    def method_missing(name, *args)
      field(name, *args)
    end
  end
  
  class HooksDsl
    def field_type(type, &blk)
      ModalFields.hooks[type.to_sym] = lambda{|model, column_declaration|
        blk[model, column_declaration]
      }
    end
    # geric filter applied to all the fields (after a specific filter for the type, if there is one)
    def all_fields(&blk)
      field_type :all_fields, &blk
    end
    def method_missing(name, *args, &blk)
      field_type name, *args, &blk
    end
  end

  class DeclarationsDsl
    def initialize(model)
      @model = model
    end
    def field(name, type, *args)
      declaration = FieldDeclaration.declare(name, type, *args)
      specific_hook = ModalFields.hooks[type.to_sym]
      general_hook = ModalFields.hooks[:all_fields]
      [specific_hook, general_hook].compact.each do |hook|
        hook[@model, declaration] if hook
      end
      if ModalFields.validate(declaration)
        @model.fields_info << declaration
      end      
    end
    def timestamps
      field :created_at, :datetime
      field :updated_at, :datetime
    end
    def method_missing(name, type, *args)
      field(name, type, *args)
    end
  end

  module FieldDeclarationClassMethods
    def fields(&blk)
      @fields_info ||= []
      unless self.respond_to?(:fields_info)
        self.instance_eval do
          def fields_info
            @fields_info
          end
        end
      end
      DeclarationsDsl.new(self).instance_eval(&blk)
    end    
  end

  @show_primary_keys = false
  @hooks = {}
  @definitions = {}
  @column_to_field_declaration_hook = nil
  
  class <<self
    attr_reader :hooks, :definitions
    # Define declaration of primary keys
    #   ModalFields.show_primary_keys = false # the default: do not show primary keys
    #   ModalFields.show_primary_keys = true  # always declare primary keys
    #   ModalFields.show_primary_keys = :id   # only declare if named 'id' (otherwise the model will have a primary_key declaration)
    #   ModalFields.show_primary_keys = :except_id   # only declare if named differently from 'id'
    attr_accessor :show_primary_keys
    
    # Run a definition block that executes field type definitions
    def define(&blk)
      DefinitionsDsl.new.instance_eval(&blk)
    end
    
    # Run a hooks block that defines field declaration processors
    def hook(&blk)
      HooksDsl.new.instance_eval(&blk)
    end
    
    # Define a custom column to field declaration conversion
    def column_to_field_declaration(&blk)
      @column_to_field_declaration_hook = blk
    end
    
    # Enable the ModalFields plugin (adds the fields declarator to model classes)
    def enable
      if defined?(::Rails)        
        ::ActiveRecord::Base.extend FieldDeclarationClassMethods
      end
    end
    
    # Update the field declarations of all the models.
    # This modifies the source files of all the models (touches only the fields block or adds one if not present).
{'fraction_non_alphanumeric': 0.08529854490717512, 'fraction_numerical': 0.00040140491721023583, 'mean_word_length': 2.80817730225449, 'pattern_counts': {'":': 2, '<': 21, '<?xml version=': 0, '>': 9, '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': '6487196', 'n_tokens_mistral': 2961, 'n_tokens_neox': 2922, 'n_words': 917}