<commit_before>module DataMapper

  # Class representing a relation's attribute within a mapper
  #
  # @abstract
  #
  # @api private
  class Attribute

    include AbstractType

    include Equalizer.new(:name, :type, :field, :options)

    # The attribute's name
    #
    # @example
    #
    #   attribute = DataMapper[Person].attributes[:name]
    #   attribute.name
    #
    # @return [Symbol]
    #
    # @api public
    attr_reader :name

    # The attribute's type
    #
    # @example
    #
    #   attribute = DataMapper[Person].attributes[:name]
    #   attribute.type
    #
    # @return [Class]
    #
    # @api public
    attr_reader :type

    # The attribute's field name
    #
    # @example
    #
    #   attribute = DataMapper[Person].attributes[:name]
    #   attribute.field
    #
    # @return [Symbol]
    #
    # @api public
    attr_reader :field

    # The attribute's options
    #
    # @example
    #
    #   attribute = DataMapper[Person].attributes[:name]
    #   attribute.options
    #
    # @return [Hash]
    #
    # @api public
    attr_reader :options

    # The primitive attribute types
    #
    # @api private
    PRIMITIVES = [ String, Time, Integer, Float, BigDecimal, DateTime, Date, Class, TrueClass, Numeric, Object ].freeze

    # Option keys that can't be changed with {#clone}
    #
    # @api private
    STABLE_OPTIONS = [ :type, :collection, :association ].freeze

    # Instantiate a concrete attribute subclass based on the given options
    #
    # @example
    #
    #   DataMapper::Mapper::Attribute.build(:name, :type => String)
    #
    # @param [Symbol] name
    #   the attribute's name
    #
    # @param [Hash] options
    #   the attribute's options
    # @option options [String] :to
    #   the field name to map to
    # @option options [String] :key
    #   true if this attribute is (part of) the key
    # @option options [String] :type
    #   the attribute's type
    # @option options [String] :collection
    #   true if this attribute is a collection
    #
    # @return [Attribute]
    #   a concrete subclass based on the given options
    #
    # @api public
    def self.build(name, options = EMPTY_HASH)
      klass = if PRIMITIVES.include?(options[:type])
                Attribute::Primitive
              elsif options[:collection]
                Attribute::EmbeddedCollection
              elsif options[:association]
                Attribute::Association
              else
                Attribute::EmbeddedValue
              end

      attribute = klass.new(name, options)
      attribute.extend(Coercible) if options[:coercion_method]
      attribute
    end

    # Initialize a new attribute instance
    #
    # @see Attribute.build
    #
    # @return [undefined]
    #
    # @api private
    def initialize(name, options)
      @name    = name
      @field   = options.fetch(:to, @name)
      @key     = options.fetch(:key, false)
      @options = options.dup.freeze
    end

    # Finalize this attribute
    #
    # This is a noop. Concrete subclasses may overwrite this.
    #
    # @return [self]
    #
    # @api private
    def finalize(*)
      self # noop
    end

    # Load this attribute's value from a tuple
    #
    # @abstract
    #
    # @param [(#each, #[])] tuple
    #   the tuple to load
    #
    # @raise NotImplementedError
    #
    # @return [undefined]
    #
    # @api private
    abstract_method :load

    # Tests wether the attribute is (part of) a key
    #
    # @return [Boolean]
    #   true if attribute is (part of) a key, false otherwise
    #
    # @api private
    def key?
      @key
    end

    # Tests wether the attribute's type is primitive
    #
    # @return [Boolean]
    #   true if attribute's type is primitive, false otherwise
    #
    # @api private
    def primitive?
      false
    end

    # Return a cloned instance but with the given options merged
    #
    # The method will not change the +:type+ and +:collection+ options
    #
    # @see Attribute.build
    # @see STABLE_OPTIONS
    #
    # @param [Hash] options
    #   the options accepted by {Attribute.build} minus {STABLE_OPTIONS}
    #
    # @return [Attribute]
    #
    # @api private
    def clone(options = EMPTY_HASH)
      self.class.build(name, @options.merge(clone_options(options)))
    end

    private

    # Strip stable options from options passed to #clone
    #
    # @param [Hash] options
    #   the options for a cloned attribute
    #
    # @return [Hash]
    #
    # @api private
    def clone_options(options)
      options.reject { |key, _| STABLE_OPTIONS.include?(key) }
    end

  end # class Attribute

end # module DataMapper<commit_msg>Delegate state independent work to class methods<commit_after>module DataMapper

  # Class representing a relation's attribute within a mapper
  #
  # @abstract
  #
  # @api private
  class Attribute

    include AbstractType

    include Equalizer.new(:name, :type, :field, :options)

    # The attribute's name
    #
    # @example
    #
    #   attribute = DataMapper[Person].attributes[:name]
    #   attribute.name
    #
    # @return [Symbol]
    #
    # @api public
    attr_reader :name

    # The attribute's type
    #
    # @example
    #
    #   attribute = DataMapper[Person].attributes[:name]
    #   attribute.type
    #
    # @return [Class]
    #
    # @api public
    attr_reader :type

    # The attribute's field name
    #
    # @example
    #
    #   attribute = DataMapper[Person].attributes[:name]
    #   attribute.field
    #
    # @return [Symbol]
    #
    # @api public
    attr_reader :field

    # The attribute's options
    #
    # @example
    #
    #   attribute = DataMapper[Person].attributes[:name]
    #   attribute.options
    #
    # @return [Hash]
    #
    # @api public
    attr_reader :options

    # The primitive attribute types
    #
    # @api private
    PRIMITIVES = [ String, Time, Integer, Float, BigDecimal, DateTime, Date, Class, TrueClass, Numeric, Object ].freeze

    # Option keys that can't be changed with {#clone}
    #
    # @api private
    STABLE_OPTIONS = [ :type, :collection, :association ].freeze

    # Instantiate a concrete attribute subclass based on the given options
    #
    # @example
    #
    #   DataMapper::Mapper::Attribute.build(:name, :type => String)
    #
    # @param [Symbol] name
    #   the attribute's name
    #
    # @param [Hash] options
    #   the attribute's options
    # @option options [String] :to
    #   the field name to map to
    # @option options [String] :key
    #   true if this attribute is (part of) the key
    # @option options [String] :type
    #   the attribute's type
    # @option options [String] :collection
    #   true if this attribute is a collection
    #
    # @return [Attribute]
    #   a concrete subclass based on the given options
    #
    # @api public
    def self.build(name, options = EMPTY_HASH)
      klass = if PRIMITIVES.include?(options[:type])
                Attribute::Primitive
              elsif options[:collection]
                Attribute::EmbeddedCollection
              elsif options[:association]
                Attribute::Association
              else
                Attribute::EmbeddedValue
              end

      attribute = klass.new(name, options)
      attribute.extend(Coercible) if options[:coercion_method]
      attribute
    end

    # Return a clone of +attribute+ but with the given +clone_options+ merged
    #
    # The method will not change the +:type+ and +:collection+ options
    #
    # @see Attribute.build
    # @see STABLE_OPTIONS
    #
    # @param [Attribute] attribute
    #   the attribute instance to clone
    #
    # @param [Hash] clone_options
    #   the options accepted by {.build}
    #
    # @return [Attribute]
    #
    # @api private
    def self.clone(attribute, clone_options = EMPTY_HASH)
      name, options = attribute.name, attribute.options
      build(name, options.merge(cloneable_options(clone_options)))
    end

    # Strip stable options from options passed to #clone
    #
    # @param [Hash] options
    #   the options for a cloned attribute
    #
    # @return [Hash]
    #   the passed in options minus keys in {STABLE_OPTIONS}
    #
    # @api private
    def self.cloneable_options(options)
      options.reject { |key, _| STABLE_OPTIONS.include?(key) }
    end

    private_class_method :cloneable_options

    # Initialize a new attribute instance
    #
    # @see Attribute.build
    #
    # @return [undefined]
    #
    # @api private
    def initialize(name, options)
      @name    = name
      @field   = options.fetch(:to, @name)
      @key     = options.fetch(:key, false)
      @options = options.dup.freeze
    end

    # Finalize this attribute
    #
    # This is a noop. Concrete subclasses may overwrite this.
    #
    # @return [self]
    #
    # @api private
    def finalize(*)
      self # noop
    end

    # Load this attribute's value from a tuple
    #
    # @abstract
    #
    # @param [(#each, #[])] tuple
    #   the tuple to load
    #
    # @raise NotImplementedError
    #
    # @return [undefined]
    #
    # @api private
    abstract_method :load

    # Tests wether the attribute is (part of) a key
    #
    # @return [Boolean]
    #   true if attribute is (part of) a key, false otherwise
    #
    # @api private
    def key?
      @key
    end

    # Tests wether the attribute's type is primitive
    #
    # @return [Boolean]
    #   true if attribute's type is primitive, false otherwise
    #
    # @api private
    def primitive?
      false
    end

    # Return a cloned instance but with the given options merged
    #
    # The method will not change the +:type+ and +:collection+ options
    #
    # @see Attribute.build
    # @see STABLE_OPTIONS
    #
    # @param [Hash] options
    #   the options accepted by {Attribute.build} minus {STABLE_OPTIONS}
    #
    # @return [Attribute]
    #
    # @api private
    def clone(options = EMPTY_HASH)
      self.class.clone(self, options)
    end

  end # class Attribute

end # module DataMapper
{'fraction_non_alphanumeric': 0.09595009406871967, 'fraction_numerical': 0.0, 'mean_word_length': 2.1780994336060417, 'pattern_counts': {'":': 0, '<': 3, '<?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': '5189183', 'n_tokens_mistral': 3061, 'n_tokens_neox': 2972, 'n_words': 890}