module Behaves

Constants

VERSION

Public Instance Methods

behaves_like(klass) click to toggle source
# File lib/behaves.rb, line 20
def behaves_like(klass)
  add_injected_behaviors(klass)
  at_exit {
    check_for_unimplemented(klass, :public)
    check_for_unimplemented(klass, :private)
  }
end
implements(*methods, **opts) click to toggle source
# File lib/behaves.rb, line 5
def implements(*methods, **opts)
  @_public_behaviors  ||= Set.new
  @_private_behaviors ||= Set.new

  if opts[:private] == true
    @_private_behaviors += Set.new(methods)
  else
    @_public_behaviors  += Set.new(methods)
  end
end
inject_behaviors(&block) click to toggle source
# File lib/behaves.rb, line 16
def inject_behaviors (&block)
  @inject_behaviors ||= block
end

Private Instance Methods

add_injected_behaviors(klass) click to toggle source
# File lib/behaves.rb, line 90
def add_injected_behaviors(klass)
  injected_behaviors = klass.instance_variable_get("@inject_behaviors")
  if injected_behaviors
    self.class_eval &injected_behaviors
  end
end
check_for_unimplemented(klass, scope) click to toggle source
# File lib/behaves.rb, line 30
  def check_for_unimplemented(klass, scope)
    required = defined_behaviors(klass, scope)

    unimplemented = required - implemented(scope)

    return if unimplemented.empty?


    # basic "unimplemented method" error message

    err = <<~ERR
      \n\n  Expected `#{self}` to behave like `#{klass}`, but the following #{scope} methods are unimplemented:\n
      #{unimplemented.to_a.map{|method| "    * `#{method}`"}.join("\n")}\n
    ERR


    # add "wrong scope" message

    other_scopes            = (scope == :public) ? [:private] : [:public]
    methods_in_other_scopes = Set.new( other_scopes.map{|s| implemented(s)}.map(&:to_a).flatten.compact )
    methods_in_wrong_scope  = unimplemented && methods_in_other_scopes

    if !methods_in_wrong_scope.empty?
      err += <<~ERR
        \n  The following methods appear to be defined, but in the wrong scope:\n
        #{methods_in_wrong_scope.to_a.map{|method| "    * `#{method}`"}.join("\n")}\n\n
      ERR
    end


    raise NotImplementedError, err
  end
defined_behaviors(klass, scope) click to toggle source
# File lib/behaves.rb, line 82
def defined_behaviors(klass, scope)
  if behaviors = klass.instance_variable_get(:"@_#{scope}_behaviors")
    behaviors
  else
    raise NotImplementedError, "Expected `#{klass}` to define behaviors, but none found."
  end
end
implemented(scope) click to toggle source
# File lib/behaves.rb, line 63
  def implemented(scope)
    lookups = {
      public:           :instance_methods,
      private:  :private_instance_methods,
    }

    method_lookup_sym = lookups.fetch(scope) do
      raise ArgumentError.new <<~ERR

        Invalid `scope`: #{scope}
        Valid `scope`s include: #{lookups.keys.map{|sym| "`#{sym.inspect}`"}.join(', ')}
      ERR
    end

    methods = self.send(method_lookup_sym, false)

    Set.new(methods)
  end