<commit_before>module Virtus

  # A set of Attribute objects
  class AttributeSet < Module
    include Enumerable

    # @api private
    def self.create(descendant)
      superclass = descendant.superclass
      if superclass.respond_to?(:attribute_set)
        parent = superclass.public_send(:attribute_set)
      end
      descendant.instance_variable_set('@attribute_set', AttributeSet.new(parent))
    end

    # Initialize an AttributeSet
    #
    # @param [AttributeSet] parent
    # @param [Array] attributes
    #
    # @return [undefined]
    #
    # @api private
    def initialize(parent = nil, attributes = [])
      @parent     = parent
      @attributes = attributes.dup
      @index      = {}
      reset
    end

    # Iterate over each attribute in the set
    #
    # @example
    #   attribute_set = AttributeSet.new(attributes, parent)
    #   attribute_set.each { |attribute| ... }
    #
    # @yield [attribute]
    #
    # @yieldparam [Attribute] attribute
    #   each attribute in the set
    #
    # @return [self]
    #
    # @api public
    def each
      return to_enum unless block_given?
      @index.values.uniq.each { |attribute| yield attribute }
      self
    end

    # Adds the attributes to the set
    #
    # @example
    #   attribute_set.merge(attributes)
    #
    # @param [Array<Attribute>] attributes
    #
    # @return [self]
    #
    # @api public
    def merge(attributes)
      attributes.each { |attribute| self << attribute }
      self
    end

    # Adds an attribute to the set
    #
    # @example
    #   attribute_set << attribute
    #
    # @param [Attribute] attribute
    #
    # @return [self]
    #
    # @api public
    def <<(attribute)
      self[attribute.name] = attribute
      attribute.define_accessor_methods(self)
    end

    # Get an attribute by name
    #
    # @example
    #   attribute_set[:name]  # => Attribute object
    #
    # @param [Symbol] name
    #
    # @return [Attribute]
    #
    # @api public
    def [](name)
      @index[name]
    end

    # Set an attribute by name
    #
    # @example
    #   attribute_set[:name] = attribute
    #
    # @param [Symbol] name
    # @param [Attribute] attribute
    #
    # @return [Attribute]
    #
    # @api public
    def []=(name, attribute)
      @attributes << attribute
      update_index(name, attribute)
    end

    # Reset the index when the parent is updated
    #
    # @return [self]
    #
    # @api private
    def reset
      merge_attributes(@parent) if @parent
      merge_attributes(@attributes)
      self
    end

    # Defines an attribute reader method
    #
    # @param [Attribute] attribute
    # @param [Symbol] method_name
    # @param [Symbol] visibility
    #
    # @return [undefined]
    #
    # @api private
    def define_reader_method(attribute, method_name, visibility)
      define_method(method_name) { attribute.get(self) }
      send(visibility, method_name)
    end

    # Defines an attribute writer method
    #
    # @param [Attribute] attribute
    # @param [Symbol] method_name
    # @param [Symbol] visibility
    #
    # @return [undefined]
    #
    # @api private
    def define_writer_method(attribute, method_name, visibility)
      define_method(method_name) { |value| attribute.set(self, value) }
      send(visibility, method_name)
    end

    # Get values of all attributes defined for this class, ignoring privacy
    #
    # @return [Hash]
    #
    # @api private
    def get(object)
      each_with_object({}) do |attribute, attributes|
        name = attribute.name
        attributes[name] = object.__send__(name) if attribute.public_reader?
      end
    end

    # Mass-assign attribute values
    #
    # @see Virtus::InstanceMethods#attributes=
    #
    # @return [Hash]
    #
    # @api private
    def set(object, attributes)
      coerce(attributes).each do |name, value|
        writer_name = "#{name}="
        if object.allowed_writer_methods.include?(writer_name)
          object.__send__(writer_name, value)
        end
      end
    end

    # Set default attributes
    #
    # @return [self]
    #
    # @api private
    def set_defaults(object, filter = method(:skip_default?))
      each do |attribute|
        if filter.call(object, attribute)
          next
        end
        set_default(object, attribute)
      end
    end

    # Set default attribute
    #
    # @return [default value]
    #
    # @api private
    def set_default(object, attribute)
      attribute.set_default_value(object, attribute.default.call)
    end

    # Coerce attributes received to a hash
    #
    # @return [Hash]
    #
    # @api private
    def coerce(attributes)
      ::Hash.try_convert(attributes) or raise(
        NoMethodError, "Expected #{attributes.inspect} to respond to #to_hash"
      )<commit_msg>Fix AttributeSet#set_default<commit_after>module Virtus

  # A set of Attribute objects
  class AttributeSet < Module
    include Enumerable

    # @api private
    def self.create(descendant)
      superclass = descendant.superclass
      if superclass.respond_to?(:attribute_set)
        parent = superclass.public_send(:attribute_set)
      end
      descendant.instance_variable_set('@attribute_set', AttributeSet.new(parent))
    end

    # Initialize an AttributeSet
    #
    # @param [AttributeSet] parent
    # @param [Array] attributes
    #
    # @return [undefined]
    #
    # @api private
    def initialize(parent = nil, attributes = [])
      @parent     = parent
      @attributes = attributes.dup
      @index      = {}
      reset
    end

    # Iterate over each attribute in the set
    #
    # @example
    #   attribute_set = AttributeSet.new(attributes, parent)
    #   attribute_set.each { |attribute| ... }
    #
    # @yield [attribute]
    #
    # @yieldparam [Attribute] attribute
    #   each attribute in the set
    #
    # @return [self]
    #
    # @api public
    def each
      return to_enum unless block_given?
      @index.values.uniq.each { |attribute| yield attribute }
      self
    end

    # Adds the attributes to the set
    #
    # @example
    #   attribute_set.merge(attributes)
    #
    # @param [Array<Attribute>] attributes
    #
    # @return [self]
    #
    # @api public
    def merge(attributes)
      attributes.each { |attribute| self << attribute }
      self
    end

    # Adds an attribute to the set
    #
    # @example
    #   attribute_set << attribute
    #
    # @param [Attribute] attribute
    #
    # @return [self]
    #
    # @api public
    def <<(attribute)
      self[attribute.name] = attribute
      attribute.define_accessor_methods(self)
    end

    # Get an attribute by name
    #
    # @example
    #   attribute_set[:name]  # => Attribute object
    #
    # @param [Symbol] name
    #
    # @return [Attribute]
    #
    # @api public
    def [](name)
      @index[name]
    end

    # Set an attribute by name
    #
    # @example
    #   attribute_set[:name] = attribute
    #
    # @param [Symbol] name
    # @param [Attribute] attribute
    #
    # @return [Attribute]
    #
    # @api public
    def []=(name, attribute)
      @attributes << attribute
      update_index(name, attribute)
    end

    # Reset the index when the parent is updated
    #
    # @return [self]
    #
    # @api private
    def reset
      merge_attributes(@parent) if @parent
      merge_attributes(@attributes)
      self
    end

    # Defines an attribute reader method
    #
    # @param [Attribute] attribute
    # @param [Symbol] method_name
    # @param [Symbol] visibility
    #
    # @return [undefined]
    #
    # @api private
    def define_reader_method(attribute, method_name, visibility)
      define_method(method_name) { attribute.get(self) }
      send(visibility, method_name)
    end

    # Defines an attribute writer method
    #
    # @param [Attribute] attribute
    # @param [Symbol] method_name
    # @param [Symbol] visibility
    #
    # @return [undefined]
    #
    # @api private
    def define_writer_method(attribute, method_name, visibility)
      define_method(method_name) { |value| attribute.set(self, value) }
      send(visibility, method_name)
    end

    # Get values of all attributes defined for this class, ignoring privacy
    #
    # @return [Hash]
    #
    # @api private
    def get(object)
      each_with_object({}) do |attribute, attributes|
        name = attribute.name
        attributes[name] = object.__send__(name) if attribute.public_reader?
      end
    end

    # Mass-assign attribute values
    #
    # @see Virtus::InstanceMethods#attributes=
    #
    # @return [Hash]
    #
    # @api private
    def set(object, attributes)
      coerce(attributes).each do |name, value|
        writer_name = "#{name}="
        if object.allowed_writer_methods.include?(writer_name)
          object.__send__(writer_name, value)
        end
      end
    end

    # Set default attributes
    #
    # @return [self]
    #
    # @api private
    def set_defaults(object, filter = method(:skip_default?))
      each do |attribute|
        if filter.call(object, attribute)
          next
        end
        set_default(object, attribute)
      end
    end

    # Set default attribute
    #
    # @return [default value]
    #
    # @api private
    def set_default(object, attribute)
      attribute.set_default_value(object)
    end

    # Coerce attributes received to a hash
    #
    # @return [Hash]
    #
    # @api private
    def coerce(attributes)
      ::Hash.try_convert(attributes) or raise(
        NoMethodError, "Expected #{attributes.inspect} to respond to #to_hash"
      )
{'fraction_non_alphanumeric': 0.09876543209876543, 'fraction_numerical': 0.0, 'mean_word_length': 2.266689257878685, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 7, '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': '7559830', 'n_tokens_mistral': 2891, 'n_tokens_neox': 2835, 'n_words': 738}