<commit_before>require 'ostruct'

# Blueprint ==
#   - part implementation definition for attributes
#   - part container for views
module Praxis
  class Blueprint
    include Attributor::Type
    extend Finalizable

    @@caching_enabled = false

    CIRCULAR_REFERENCE_MARKER = '...'.freeze

    attr_accessor :object, :decorators
    attr_reader :validating, :active_renders

    class << self
      attr_reader :views, :attribute, :options
      attr_accessor :reference
    end

    def self.inherited(klass)
      super

      klass.instance_eval do
        @views = Hash.new
        @options = Hash.new
      end
    end

    # Override default new behavior to support memoized creation through an IdentityMap
    def self.new(object, decorators=nil)
      if @@caching_enabled && decorators.nil?
        key = object

        cache = if object.respond_to?(:identity_map)
          object.identity_map.blueprint_cache[self]
        else
          self.cache
        end

        return cache[key] ||= begin
          blueprint = self.allocate
          blueprint.send(:initialize, object, decorators)
          blueprint
        end
      end

      blueprint = self.allocate
      blueprint.send(:initialize, object, decorators)
      blueprint
    end


    def self.describe(shallow=false)
      type_name = self.ancestors.find { |k| k.name && !k.name.empty? }.name

      description = self.attribute.type.describe(shallow).merge!(name: type_name)

      unless shallow
        description[:views] = self.views.each_with_object({}) do |(view_name, view), hash|
          hash[view_name] = view.describe
        end
      end
      
      description
    end


    def self.attributes(opts={}, &block)
      if block_given?
        if self.const_defined?(:Struct, false)
          raise "Redefining Blueprint attributes is not currently supported"
        else

          if opts.has_key?(:reference) && opts[:reference] != self.reference
            raise "Reference mismatch in #{self.inspect}. Given :reference option #{opts[:reference].inspect}, while using #{self.reference.inspect}"
          elsif self.reference
            opts[:reference] = self.reference #pass the reference Class down
          else
            opts[:reference] = self
          end

          @options = opts
          @block = block
        end

        return @attribute
      end

      unless @attribute
        raise "@attribute not defined yet for #{self.name}"
      end

      @attribute.attributes
    end<commit_msg>Preserve blueprint options when defining attributes.<commit_after>require 'ostruct'

# Blueprint ==
#   - part implementation definition for attributes
#   - part container for views
module Praxis
  class Blueprint
    include Attributor::Type
    extend Finalizable

    @@caching_enabled = false

    CIRCULAR_REFERENCE_MARKER = '...'.freeze

    attr_accessor :object, :decorators
    attr_reader :validating, :active_renders

    class << self
      attr_reader :views, :attribute, :options
      attr_accessor :reference
    end

    def self.inherited(klass)
      super

      klass.instance_eval do
        @views = Hash.new
        @options = Hash.new
      end
    end

    # Override default new behavior to support memoized creation through an IdentityMap
    def self.new(object, decorators=nil)
      if @@caching_enabled && decorators.nil?
        key = object

        cache = if object.respond_to?(:identity_map)
          object.identity_map.blueprint_cache[self]
        else
          self.cache
        end

        return cache[key] ||= begin
          blueprint = self.allocate
          blueprint.send(:initialize, object, decorators)
          blueprint
        end
      end

      blueprint = self.allocate
      blueprint.send(:initialize, object, decorators)
      blueprint
    end


    def self.describe(shallow=false)
      type_name = self.ancestors.find { |k| k.name && !k.name.empty? }.name

      description = self.attribute.type.describe(shallow).merge!(name: type_name)

      unless shallow
        description[:views] = self.views.each_with_object({}) do |(view_name, view), hash|
          hash[view_name] = view.describe
        end
      end
      
      description
    end


    def self.attributes(opts={}, &block)
      if block_given?
        if self.const_defined?(:Struct, false)
          raise "Redefining Blueprint attributes is not currently supported"
        else

          if opts.has_key?(:reference) && opts[:reference] != self.reference
            raise "Reference mismatch in #{self.inspect}. Given :reference option #{opts[:reference].inspect}, while using #{self.reference.inspect}"
          elsif self.reference
            opts[:reference] = self.reference #pass the reference Class down
          else
            opts[:reference] = self
          end

          @options.merge!(opts)
          @block = block
        end

        return @attribute
      end

      unless @attribute
        raise "@attribute not defined yet for #{self.name}"
      end

      @attribute.attributes
    end
{'fraction_non_alphanumeric': 0.08745322040575143, 'fraction_numerical': 0.0, 'mean_word_length': 2.284605433376455, 'pattern_counts': {'":': 0, '<': 7, '<?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': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2628549', 'n_tokens_mistral': 1413, 'n_tokens_neox': 1356, 'n_words': 373}