<commit_before>require 'multi_json'
require 'set'

module Grape
  # An Entity is a lightweight structure that allows you to easily
  # represent data from your application in a consistent and abstracted
  # way in your API. Entities can also provide documentation for the
  # fields exposed.
  #
  # @example Entity Definition
  #
  #   module API
  #     module Entities
  #       class User < Grape::Entity
  #         expose :first_name, :last_name, :screen_name, :location
  #         expose :field, documentation: { type: "string", desc: "describe the field" }
  #         expose :latest_status, using: API::Status, as: :status, unless: { collection: true }
  #         expose :email, if: { type: :full }
  #         expose :new_attribute, if: { version: 'v2' }
  #         expose(:name) { |model, options| [model.first_name, model.last_name].join(' ') }
  #       end
  #     end
  #   end
  #
  # Entities are not independent structures, rather, they create
  # **representations** of other Ruby objects using a number of methods
  # that are convenient for use in an API. Once you've defined an Entity,
  # you can use it in your API like this:
  #
  # @example Usage in the API Layer
  #
  #   module API
  #     class Users < Grape::API
  #       version 'v2'
  #
  #       desc 'User index', { params: API::Entities::User.documentation }
  #       get '/users' do
  #         @users = User.all
  #         type = current_user.admin? ? :full : :default
  #         present @users, with: API::Entities::User, type: type
  #       end
  #     end
  #   end
  class Entity
    attr_reader :object, :options

    # The Entity DSL allows you to mix entity functionality into
    # your existing classes.
    module DSL
      def self.included(base)
        base.extend ClassMethods
        ancestor_entity_class = base.ancestors.detect { |a| a.entity_class if a.respond_to?(:entity_class) }
        base.const_set(:Entity, Class.new(ancestor_entity_class || Grape::Entity)) unless const_defined?(:Entity)
      end

      module ClassMethods
        # Returns the automatically-created entity class for this
        # Class.
        def entity_class(search_ancestors = true)
          klass = const_get(:Entity) if const_defined?(:Entity)
          klass ||= ancestors.detect { |a| a.entity_class(false) if a.respond_to?(:entity_class) } if search_ancestors
          klass
        end

        # Call this to make exposures to the entity for this Class.
        # Can be called with symbols for the attributes to expose,
        # a block that yields the full Entity DSL (See Grape::Entity),
        # or both.
        #
        # @example Symbols only.
        #
        #   class User
        #     include Grape::Entity::DSL
        #
        #     entity :name, :email
        #   end
        #
        # @example Mixed.
        #
        #   class User
        #     include Grape::Entity::DSL
        #
        #     entity :name, :email do
        #       expose :latest_status, using: Status::Entity, if: :include_status
        #       expose :new_attribute, if: { version: 'v2' }
        #     end
        #   end
        def entity(*exposures, &block)
          entity_class.expose(*exposures) if exposures.any?
          entity_class.class_eval(&block) if block_given?
          entity_class
        end
      end

      # Instantiates an entity version of this object.
      def entity(options = {})
        self.class.entity_class.new(self, options)
      end
    end

    class << self
      # Returns exposures that have been declared for this Entity or
      # ancestors. The keys are symbolized references to methods on the
      # containing object, the values are the options that were passed into expose.
      # @return [Hash] of exposures
      attr_accessor :exposures
      # Returns all formatters that are registered for this and it's ancestors
      # @return [Hash] of formatters
      attr_accessor :formatters
      attr_accessor :nested_attribute_names
      attr_accessor :nested_exposures
    end

    def self.inherited(subclass)
      subclass.exposures = exposures.try(:dup) || {}
      subclass.nested_exposures = nested_exposures.try(:dup) || {}
      subclass.nested_attribute_names = nested_attribute_names.try(:dup) || {}
      subclass.formatters = formatters.try(:dup) || {}
    end

    # This method is the primary means by which you will declare what attributes
    # should be exposed by the entity.
    #
    # @option options :as Declare an alias for the representation of this attribute.
    # @option options :if When passed a Hash, the attribute will only be exposed if the
    #   runtime options match all the conditions passed in. When passed a lambda, the
    #   lambda will execute with two arguments: the object being represented and the
    #   options passed into the representation call. Return true if you want the attribute
    #   to be exposed.
    # @option options :unless When passed a Hash, the attribute will be exposed if the
    #   runtime options fail to match any of the conditions passed in. If passed a lambda,
    #   it will yield the object being represented and the options passed to the
    #   representation call. Return true to prevent exposure, false to allow it.
    # @option options :using This option allows you to map an attribute to another Grape
    #   Entity. Pass it a Grape::Entity class and the attribute in question will
    #   automatically be transformed into a representation that will receive the same
    #   options as the parent entity when called. Note that arrays are fine here and
    #   will automatically be detected and handled appropriately.
    # @option options :proc If you pass a Proc into this option, it will
    #   be used directly to determine the value for that attribute. It
    #   will be called with the represented object as well as the
    #   runtime options that were passed in. You can also just supply a
    #   block to the expose call to achieve the same effect.
    # @option options :documentation Define documenation for an exposed
    #   field, typically the value is a hash with two fields, type and desc.
    def self.expose(*args, &block)
      options = merge_options(args.last.is_a?(Hash) ? args.pop : {})

      if args.size > 1
        fail ArgumentError, 'You may not use the :as option on multi-attribute exposures.' if options[:as]
        fail ArgumentError, 'You may not use block-setting on multi-attribute exposures.' if block_given?
      end

      fail ArgumentError, 'You may not use block-setting when also using format_with' if block_given? && options[:format_with].respond_to?(:call)

      options[:proc] = block if block_given? && block.parameters.any?

      @nested_attributes ||= []

      # rubocop:disable Style/Next
      args.each do |attribute|
        unless @nested_attributes.empty?
          orig_attribute = attribute.to_sym
          attribute = "#{@nested_attributes.last}__#{attribute}"
          nested_attribute_names[attribute.to_sym] = orig_attribute
          options[:nested] = true
          nested_exposures.deep_merge!(@nested_attributes.last.to_sym  => { attribute.to_sym => options })
        end

        exposures[attribute.to_sym] = options

        # Nested exposures are given in a block with no parameters.
        if block_given? && block.parameters.empty?
          @nested_attributes << attribute
          block.call
          @nested_attributes.pop
        end
      end
    end

    def self.unexpose(attribute)
      exposures.delete(attribute)
    end

    # Set options that will be applied to any exposures declared inside the block.
    #
    # @example Multi-exposure if
    #
    #   class MyEntity < Grape::Entity<commit_msg>Remove `to_sym` calls duplication<commit_after>require 'multi_json'
require 'set'

module Grape
  # An Entity is a lightweight structure that allows you to easily
  # represent data from your application in a consistent and abstracted
  # way in your API. Entities can also provide documentation for the
  # fields exposed.
  #
  # @example Entity Definition
  #
  #   module API
  #     module Entities
  #       class User < Grape::Entity
  #         expose :first_name, :last_name, :screen_name, :location
  #         expose :field, documentation: { type: "string", desc: "describe the field" }
  #         expose :latest_status, using: API::Status, as: :status, unless: { collection: true }
  #         expose :email, if: { type: :full }
  #         expose :new_attribute, if: { version: 'v2' }
  #         expose(:name) { |model, options| [model.first_name, model.last_name].join(' ') }
  #       end
  #     end
  #   end
  #
  # Entities are not independent structures, rather, they create
  # **representations** of other Ruby objects using a number of methods
  # that are convenient for use in an API. Once you've defined an Entity,
  # you can use it in your API like this:
  #
  # @example Usage in the API Layer
  #
  #   module API
  #     class Users < Grape::API
  #       version 'v2'
  #
  #       desc 'User index', { params: API::Entities::User.documentation }
  #       get '/users' do
  #         @users = User.all
  #         type = current_user.admin? ? :full : :default
  #         present @users, with: API::Entities::User, type: type
  #       end
  #     end
  #   end
  class Entity
    attr_reader :object, :options

    # The Entity DSL allows you to mix entity functionality into
    # your existing classes.
    module DSL
      def self.included(base)
        base.extend ClassMethods
        ancestor_entity_class = base.ancestors.detect { |a| a.entity_class if a.respond_to?(:entity_class) }
        base.const_set(:Entity, Class.new(ancestor_entity_class || Grape::Entity)) unless const_defined?(:Entity)
      end

      module ClassMethods
        # Returns the automatically-created entity class for this
        # Class.
        def entity_class(search_ancestors = true)
          klass = const_get(:Entity) if const_defined?(:Entity)
          klass ||= ancestors.detect { |a| a.entity_class(false) if a.respond_to?(:entity_class) } if search_ancestors
          klass
        end

        # Call this to make exposures to the entity for this Class.
        # Can be called with symbols for the attributes to expose,
        # a block that yields the full Entity DSL (See Grape::Entity),
        # or both.
        #
        # @example Symbols only.
        #
        #   class User
        #     include Grape::Entity::DSL
        #
        #     entity :name, :email
        #   end
        #
        # @example Mixed.
        #
        #   class User
        #     include Grape::Entity::DSL
        #
        #     entity :name, :email do
        #       expose :latest_status, using: Status::Entity, if: :include_status
        #       expose :new_attribute, if: { version: 'v2' }
        #     end
        #   end
        def entity(*exposures, &block)
          entity_class.expose(*exposures) if exposures.any?
          entity_class.class_eval(&block) if block_given?
          entity_class
        end
      end

      # Instantiates an entity version of this object.
      def entity(options = {})
        self.class.entity_class.new(self, options)
      end
    end

    class << self
      # Returns exposures that have been declared for this Entity or
      # ancestors. The keys are symbolized references to methods on the
      # containing object, the values are the options that were passed into expose.
      # @return [Hash] of exposures
      attr_accessor :exposures
      # Returns all formatters that are registered for this and it's ancestors
      # @return [Hash] of formatters
      attr_accessor :formatters
      attr_accessor :nested_attribute_names
      attr_accessor :nested_exposures
    end

    def self.inherited(subclass)
      subclass.exposures = exposures.try(:dup) || {}
      subclass.nested_exposures = nested_exposures.try(:dup) || {}
      subclass.nested_attribute_names = nested_attribute_names.try(:dup) || {}
      subclass.formatters = formatters.try(:dup) || {}
    end

    # This method is the primary means by which you will declare what attributes
    # should be exposed by the entity.
    #
    # @option options :as Declare an alias for the representation of this attribute.
    # @option options :if When passed a Hash, the attribute will only be exposed if the
    #   runtime options match all the conditions passed in. When passed a lambda, the
    #   lambda will execute with two arguments: the object being represented and the
    #   options passed into the representation call. Return true if you want the attribute
    #   to be exposed.
    # @option options :unless When passed a Hash, the attribute will be exposed if the
    #   runtime options fail to match any of the conditions passed in. If passed a lambda,
    #   it will yield the object being represented and the options passed to the
    #   representation call. Return true to prevent exposure, false to allow it.
    # @option options :using This option allows you to map an attribute to another Grape
    #   Entity. Pass it a Grape::Entity class and the attribute in question will
    #   automatically be transformed into a representation that will receive the same
    #   options as the parent entity when called. Note that arrays are fine here and
    #   will automatically be detected and handled appropriately.
    # @option options :proc If you pass a Proc into this option, it will
    #   be used directly to determine the value for that attribute. It
    #   will be called with the represented object as well as the
    #   runtime options that were passed in. You can also just supply a
    #   block to the expose call to achieve the same effect.
    # @option options :documentation Define documenation for an exposed
    #   field, typically the value is a hash with two fields, type and desc.
    def self.expose(*args, &block)
      options = merge_options(args.last.is_a?(Hash) ? args.pop : {})

      if args.size > 1
        fail ArgumentError, 'You may not use the :as option on multi-attribute exposures.' if options[:as]
        fail ArgumentError, 'You may not use block-setting on multi-attribute exposures.' if block_given?
      end

      fail ArgumentError, 'You may not use block-setting when also using format_with' if block_given? && options[:format_with].respond_to?(:call)

      options[:proc] = block if block_given? && block.parameters.any?

      @nested_attributes ||= []

      # rubocop:disable Style/Next
      args.each do |attribute|
        unless @nested_attributes.empty?
          orig_attribute = attribute.to_sym
          attribute = "#{@nested_attributes.last}__#{attribute}".to_sym
          nested_attribute_names[attribute] = orig_attribute
          options[:nested] = true
          nested_exposures.deep_merge!(@nested_attributes.last.to_sym  => { attribute => options })
        end

        exposures[attribute] = options

        # Nested exposures are given in a block with no parameters.
        if block_given? && block.parameters.empty?
          @nested_attributes << attribute
          block.call
          @nested_attributes.pop
        end
      end
    end

    def self.unexpose(attribute)
      exposures.delete(attribute)
    end

    # Set options that will be applied to any exposures declared inside the block.
    #
    # @example Multi-exposure if
    #
    #   class MyEntity < Grape::Entity
{'fraction_non_alphanumeric': 0.0780846670108415, 'fraction_numerical': 0.0005162622612287042, 'mean_word_length': 2.54136197440585, 'pattern_counts': {'":': 0, '<': 17, '<?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': '5800866', 'n_tokens_mistral': 4249, 'n_tokens_neox': 4167, 'n_words': 1584}