<commit_before>require "scope_chain/version"

module ScopeChain
  module RspecHelpers
    def scope_chain(&block)
      ScopeChain.for(described_class, &block)
    end
  end

  def self.for(klass, &block)
    Chain.new klass, &block
  end

  def self.on(instance)
    AssociationChain.new instance
  end

  class AssociationChain
    class MissingAssociationError < StandardError; end
    attr_accessor :instance, :association_name, :association

    def initialize(instance)
      self.instance = instance
    end

    def as(association_name)
      self.association_name = association_name

      chain
    end

    private
    def chain
      raise MissingAssociationError.new("Can't find association #{association_name} on #{instance.class.name}") if association.nil?

      ScopeChain::Chain.new(association.klass).tap do |chain|
        instance.stubs(association.name => association.klass)

        # This is a nasty hack
        chain.define_singleton_method(:build) do |*arguments|
          add_link :build, *arguments
        end

        association.klass.define_singleton_method(:build) {}
      end
    end

    def association
      # TODO See if we can just mock for the reflection
      @association ||= instance.class.reflect_on_association(association_name)
    end
  end

  class Chain
    LINKS = [:select, :where, :includes, :order, :find, :sum, :new, :create, :create!, :limit]
    ALIASES = {} 

    class ConflictedExistenceError < StandardError
    end

    attr_accessor :klass
    def initialize(klass, &block)
      self.klass = klass
      @expectations = []
      @exists_condition = false

      link_manual_scopes
      link_named_scopes

      yield self if block_given?
    end

    LINKS.each do |link|
      define_method(link) do |*arguments|
        add_link link, *arguments
      end
    end

    ALIASES.each do |source, dest|
      define_method(source) do |*arguments|
        add_link dest, *arguments
      end
    end

    def all(returned)
      add_link :all
      returns returned
    end

    def returns(object)
      # DON'T LOOK
      @expectations.last.instance_variable_set(:@return_values, Mocha::ReturnValues.build(object))
      
      self
    end

    def exists!
      set_exists true
    end

    def missing!
      set_exists false
    end

    private
    def add_link(name, *arguments)
      expectation = klass.expects(name)
      expectation.with(*arguments) if arguments.size > 0
      expectation.returns(klass)

      @expectations << expectation

      self
    end

    def set_exists(value)
      if @exists_condition
        raise ConflictedExistenceError.new("Can only set one 'exists' conditions, #missing! or #exists!")
      end

      klass.expects(exists?: value)
      @exists_condition = true

      self
    end

    def link_manual_scopes
      klass.stubs(scoped: klass) # Handle manual scope building
    end

    def link_named_scopes
      return unless klass.respond_to?(:scopes) && klass.scopes.present?

      klass.scopes[klass.name].each do |named|
        self.define_singleton_method(named) do |*arguments|
          add_link named, *arguments
        end
      end
    end

  end
end

# Hooks so named scopes are sane
class ActiveRecord::Base
  cattr_reader :scopes
  def self.scope_with_tracking(*args, &block)
    (@@scopes ||= Hash.new {|hash, key| hash[key] = [] })[self.name].push args.first
    scope_without_tracking *args, &block
  end

  class << self
    alias_method_chain :scope, :tracking
  end
end
<commit_msg>stuff?<commit_after>require "scope_chain/version"

module ScopeChain
  module RspecHelpers
    def scope_chain(&block)
      ScopeChain.for(described_class, &block)
    end
  end

  def self.for(klass, &block)
    Chain.new klass, &block
  end

  def self.on(instance)
    AssociationChain.new instance
  end

  class AssociationChain
    class MissingAssociationError < StandardError; end
    attr_accessor :instance, :association_name, :association

    def initialize(instance)
      self.instance = instance
    end

    def as(association_name)
      self.association_name = association_name

      chain
    end

    private
    def chain
      raise MissingAssociationError.new("Can't find association #{association_name} on #{instance.class.name}") if association.nil?

      ScopeChain::Chain.new(association.klass).tap do |chain|
        instance.stubs(association.name => association.klass)

        # This is a nasty hack
        chain.define_singleton_method(:build) do |*arguments|
          add_link :build, *arguments
        end

        association.klass.define_singleton_method(:build) {}
      end
    end

    def association
      # TODO See if we can just mock for the reflection
      @association ||= instance.class.reflect_on_association(association_name)
    end
  end

  class Chain
    LINKS = [:select, :where, :includes, :order, :find, :sum, :new, :create, :create!, :limit, :joins]
    ALIASES = {} 

    class ConflictedExistenceError < StandardError
    end

    attr_accessor :klass
    def initialize(klass, &block)
      self.klass = klass
      @expectations = []
      @exists_condition = false

      link_manual_scopes
      link_named_scopes

      yield self if block_given?
    end

    LINKS.each do |link|
      define_method(link) do |*arguments|
        add_link link, *arguments
      end
    end

    ALIASES.each do |source, dest|
      define_method(source) do |*arguments|
        add_link dest, *arguments
      end
    end

    def all(returned)
      add_link :all
      returns returned
    end

    def returns(object)
      # DON'T LOOK
      @expectations.last.instance_variable_set(:@return_values, Mocha::ReturnValues.build(object))
      
      self
    end

    def exists!
      set_exists true
    end

    def missing!
      set_exists false
    end

    private
    def add_link(name, *arguments)
      expectation = klass.expects(name)
      expectation.with(*arguments) if arguments.size > 0
      expectation.returns(klass)

      @expectations << expectation

      self
    end

    def set_exists(value)
      if @exists_condition
        raise ConflictedExistenceError.new("Can only set one 'exists' conditions, #missing! or #exists!")
      end

      klass.expects(exists?: value)
      @exists_condition = true

      self
    end

    def link_manual_scopes
      klass.stubs(scoped: klass) # Handle manual scope building
    end

    def link_named_scopes
      return unless klass.respond_to?(:scopes) && klass.scopes.present?

      klass.scopes[klass.name].each do |named|
        self.define_singleton_method(named) do |*arguments|
          add_link named, *arguments
        end
      end
    end

  end
end

# Hooks so named scopes are sane
class ActiveRecord::Base
  cattr_reader :scopes
  def self.scope_with_tracking(*args, &block)
    (@@scopes ||= Hash.new {|hash, key| hash[key] = [] })[self.name].push args.first
    scope_without_tracking *args, &block
  end

  class << self
    alias_method_chain :scope, :tracking
  end
end

{'fraction_non_alphanumeric': 0.08476501490841971, 'fraction_numerical': 0.0002839698991906858, 'mean_word_length': 2.7230443974630023, 'pattern_counts': {'":': 0, '<': 15, '<?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': '3053783', 'n_tokens_mistral': 2193, 'n_tokens_neox': 2123, 'n_words': 545}