<commit_before># RSpec matcher to spec delegations.
# Based on https://gist.github.com/awinograd/6158961
#
# Usage:
#
#     describe Post do
#       it { should delegate(:name).to(:author)   }                     # name         => author.name
#       it { should delegate(:name).to(:@author)  }                     # name         => @author.name
#
#       it { should delegate(:name).to(:author).with_prefix }           # author_name  => author.name
#       it { should delegate(:name).to(:author).with_prefix(:writer) }  # writer_name  => author.name
#
#       it { should delegate(:name).to(:author).via(:writer) }          # writer       => author.name
#
#       it { should delegate(:name).to(:author).allow_nil   }           # name         => author && author.name
#       it { should delegate(:name).to(:author).allow_nil(true)   }     # name         => author && author.name
#       it { should delegate(:name).to(:author).allow_nil(false)   }    # name         => author.name
#     end

RSpec::Matchers.define(:delegate) do |method|
  match do |delegator|
    raise 'cannot specify delegate using "with_prefix" and "via"' if @prefix && @delegator_method
    raise 'need to provide a "to"' unless @delegate

    @method = method
    @delegator = delegator

    delegate? && delegate_with_nil? && block_ok?
  end

  description do
    "delegate #{method}#{arguments_description} to its #{delegation_description}#{nil_description}#{block_description}"
  end

  def failure_message
    super + ' but' + block_failure_message(false)
  end

  def failure_message_when_negated
    super + ' but' + block_failure_message(true)
  end

  def block_failure_message(negated)
    if @expected_block
      " a block was #{negated ? '' : 'not '}passed to #{delegate}.#{method}"
    else
      " a block was #{negated ? 'not ' : ''}passed to #{delegate}.#{method}"
    end
  end

  chain(:to)            { |receiver|       @delegate         = receiver }
  chain(:allow_nil)     { |allow_nil=true| @nil_allowed      = allow_nil }
  chain(:via)           { |via|            @delegator_method = via }
  chain(:with_prefix)   { |prefix=nil|     @prefix           = prefix || delegate.to_s.sub(/@/, '') }
  chain(:with)          { |*args|          @expected_args    = args }
  chain(:with_block)    { |block=true|     @expected_block   = block }
  chain(:without_block) {                  @expected_block   = false }

  private

  attr_reader :method, :delegator, :delegate, :prefix, :expected_args

  def block_ok?
    @actual_block == block_expected?
  end

  def arguments_description
    expected_args ? " with arguments #{expected_args.join ', '}" : ''
  end

  def delegation_description
    delegate.to_s + case
      when @delegator_method
        " via #{@delegator_method}"
      when @prefix
        " with prefix #{@prefix}"
      else
        ''
    end
  end

  def nil_description
    case
      when @nil_allowed == true
        ' with nil allowed'
      when @nil_allowed == false
        ' with nil not allowed'
      else
        ''
    end
  end

  def block_description<commit_msg>Improved failure messages with blocks.<commit_after># RSpec matcher to spec delegations.
# Based on https://gist.github.com/awinograd/6158961
#
# Usage:
#
#     describe Post do
#       it { should delegate(:name).to(:author)   }                     # name         => author.name
#       it { should delegate(:name).to(:@author)  }                     # name         => @author.name
#
#       it { should delegate(:name).to(:author).with_prefix }           # author_name  => author.name
#       it { should delegate(:name).to(:author).with_prefix(:writer) }  # writer_name  => author.name
#
#       it { should delegate(:name).to(:author).via(:writer) }          # writer       => author.name
#
#       it { should delegate(:name).to(:author).allow_nil   }           # name         => author && author.name
#       it { should delegate(:name).to(:author).allow_nil(true)   }     # name         => author && author.name
#       it { should delegate(:name).to(:author).allow_nil(false)   }    # name         => author.name
#     end

RSpec::Matchers.define(:delegate) do |method|
  match do |delegator|
    raise 'cannot specify delegate using "with_prefix" and "via"' if @prefix && @delegator_method
    raise 'need to provide a "to"' unless @delegate

    @method = method
    @delegator = delegator

    delegate? && delegate_with_nil? && block_ok?
  end

  description do
    "delegate #{method}#{arguments_description} to its #{delegation_description}#{nil_description}#{block_description}"
  end

  def failure_message
    super + ' but' + block_failure_message(false)
  end

  def failure_message_when_negated
    super + ' but' + block_failure_message(true)
  end

  chain(:to)            { |receiver|       @delegate         = receiver }
  chain(:allow_nil)     { |allow_nil=true| @nil_allowed      = allow_nil }
  chain(:via)           { |via|            @delegator_method = via }
  chain(:with_prefix)   { |prefix=nil|     @prefix           = prefix || delegate.to_s.sub(/@/, '') }
  chain(:with)          { |*args|          @expected_args    = args }
  chain(:with_block)    { |block=true|     @expected_block   = block }
  chain(:without_block) {                  @expected_block   = false }

  private

  attr_reader :method, :delegator, :delegate, :prefix, :expected_args

  def block_ok?
    @actual_block == block_expected?
  end

  def block_failure_message(negated)
    if @expected_block
      " a block was #{negated ? '' : 'not '}passed to #{delegate}.#{method}"
    else
      " a block was #{negated ? 'not ' : ''}passed to #{delegate}.#{method}"
    end
  end

  def arguments_description
    expected_args ? " with arguments #{expected_args.join ', '}" : ''
  end

  def delegation_description
    delegate.to_s + case
      when @delegator_method
        " via #{@delegator_method}"
      when @prefix
        " with prefix #{@prefix}"
      else
        ''
    end
  end

  def nil_description
    case
      when @nil_allowed == true
        ' with nil allowed'
      when @nil_allowed == false
        ' with nil not allowed'
      else
        ''
    end
  end

  def block_description
{'fraction_non_alphanumeric': 0.127196517813961, 'fraction_numerical': 0.0022569724326938576, 'mean_word_length': 2.4087912087912087, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 2, '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': '7453371', 'n_tokens_mistral': 1889, 'n_tokens_neox': 1857, 'n_words': 437}