<commit_before>require 'delegate'

module Representable
  # The Binding wraps the Definition instance for this property and provides methods to read/write fragments.
  class Binding < SimpleDelegator
    class FragmentNotFound
    end

    def self.build(definition, *args)
      # DISCUSS: move #create_binding to this class?
      return definition.create_binding(*args) if definition.binding
      build_for(definition, *args)
    end

    def initialize(definition, represented, user_options={}, lambda_context=represented)  # TODO: remove default arg for user options. # DISCUSS: make lambda_context an options hash?
      super(definition)
      @represented    = represented
      @user_options   = user_options
      @lambda_context = lambda_context
    end

    attr_reader :user_options, :represented, :lambda_context # TODO: make private/remove.

    # Main entry point for rendering/parsing a property object.
    def serialize(value)
      value
    end

    def deserialize(fragment)
      fragment
    end

    # Retrieve value and write fragment to the doc.
    def compile_fragment(doc)
      represented_exec_for(:writer, doc) do
        write_fragment(doc, get)
      end
    end

    # Parse value from doc and update the model property.
    def uncompile_fragment(doc)
      represented_exec_for(:reader, doc) do
        read_fragment(doc) do |value|
          set(value)
        end
      end
    end

    def write_fragment(doc, value)
      value = default_for(value)

      write_fragment_for(value, doc)
    end

    def write_fragment_for(value, doc)
      return if skipable_nil_value?(value)
      write(doc, value)
    end

    def read_fragment(doc)
      value = read_fragment_for(doc)

      if value == FragmentNotFound
        return unless has_default?
        value = default
      end

      yield value
    end

    def read_fragment_for(doc)
      read(doc)
    end

    def get
      represented_exec_for(:getter) do
        represented.send(getter)
      end
    end

    def set(value)
      represented_exec_for(:setter, value) do
        represented.send(setter, value)
      end
    end

  private
    # Execute the block for +option_name+ on the represented object.
    # Executes passed block when there's no lambda for option.
    def represented_exec_for(option_name, *args)
      return yield unless options[option_name]
      call_proc_for(options[option_name], *args)
    end

    # All lambdas are executed on lambda_context which is either represented or the decorator instance.
    def call_proc_for(proc, *args)
      return proc unless proc.is_a?(Proc)
      # TODO: call method when proc is sympbol.
      args << user_options # DISCUSS: we assume user_options is a Hash!
      lambda_context.instance_exec(*args, &proc)
    end<commit_msg>renaming Binding#lambda_context to exec_context and making it private.<commit_after>require 'delegate'

module Representable
  # The Binding wraps the Definition instance for this property and provides methods to read/write fragments.
  class Binding < SimpleDelegator
    class FragmentNotFound
    end

    def self.build(definition, *args)
      # DISCUSS: move #create_binding to this class?
      return definition.create_binding(*args) if definition.binding
      build_for(definition, *args)
    end

    def initialize(definition, represented, user_options={}, exec_context=represented)  # TODO: remove default arg for user options. # DISCUSS: make exec_context an options hash?
      super(definition)
      @represented  = represented
      @user_options = user_options
      @exec_context = exec_context
    end

    attr_reader :user_options, :represented # TODO: make private/remove.

    # Main entry point for rendering/parsing a property object.
    def serialize(value)
      value
    end

    def deserialize(fragment)
      fragment
    end

    # Retrieve value and write fragment to the doc.
    def compile_fragment(doc)
      represented_exec_for(:writer, doc) do
        write_fragment(doc, get)
      end
    end

    # Parse value from doc and update the model property.
    def uncompile_fragment(doc)
      represented_exec_for(:reader, doc) do
        read_fragment(doc) do |value|
          set(value)
        end
      end
    end

    def write_fragment(doc, value)
      value = default_for(value)

      write_fragment_for(value, doc)
    end

    def write_fragment_for(value, doc)
      return if skipable_nil_value?(value)
      write(doc, value)
    end

    def read_fragment(doc)
      value = read_fragment_for(doc)

      if value == FragmentNotFound
        return unless has_default?
        value = default
      end

      yield value
    end

    def read_fragment_for(doc)
      read(doc)
    end

    def get
      represented_exec_for(:getter) do
        represented.send(getter)
      end
    end

    def set(value)
      represented_exec_for(:setter, value) do
        represented.send(setter, value)
      end
    end

  private
    attr_reader :exec_context

    # Execute the block for +option_name+ on the represented object.
    # Executes passed block when there's no lambda for option.
    def represented_exec_for(option_name, *args)
      return yield unless options[option_name]
      call_proc_for(options[option_name], *args)
    end

    # All lambdas are executed on exec_context which is either represented or the decorator instance.
    def call_proc_for(proc, *args)
      return proc unless proc.is_a?(Proc)
      # TODO: call method when proc is sympbol.
      args << user_options # DISCUSS: we assume user_options is a Hash!
      exec_context.instance_exec(*args, &proc)
    end
{'fraction_non_alphanumeric': 0.06421369184503803, 'fraction_numerical': 0.0, 'mean_word_length': 2.8541240627130198, 'pattern_counts': {'":': 0, '<': 9, '<?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': '6512072', 'n_tokens_mistral': 1628, 'n_tokens_neox': 1559, 'n_words': 507}