module Is::Definable

public

Public Instance Methods

define(*namespace, name, __location__: nil, **kwargs, &block) click to toggle source
public

Defines an object by ‘name`, within `namespace`.

Options passed as keyword arguments are defined as state on the new object.

The ‘__location__` keyword argument is considered part of the public api and must be an instance of `Core::Define::Location`. It’s passed using an internal naming convention to avoid possible collisions with other passed state. If not given, the object’s location is based on ‘caller`.

# File lib/is/definable.rb, line 33
def define(*namespace, name, __location__: nil, **kwargs, &block)
  scope = if namespace.any?
    define_namespace(*namespace)
  else
    Object
  end

  constant = Core::Define::Utils.camelize(name)

  if scope.const_defined?(constant)
    defined = scope.const_get(constant)
  else
    location = if __location__
      unless __location__.respond_to?(:path) && __location__.respond_to?(:line)
        raise ArgumentError, "expected location to respond to `path` and `line`"
      end

      __location__
    else
      path, line = caller(1..1).first.split(":", 3)
      Core::Define::Location.new(path: path, line: line)
    end

    performing "core.define" do
      # Create the right kind of object for `self`.
      #
      defined = case self
      when Class
        Class.new(self)
      when Module
        copy
      end

      # Make the object available under the given name.
      #
      scope.const_set(constant, defined)

      # Define the new object to be stateful.
      #
      definition_block = proc do
        unless ancestors.include?(Is::Stateful)
          include Is::Stateful
        end

        # Create an object holding information about the object's definition.
        #
        definition = Core::Define::Definition.new(*namespace, name, location: location)
        definition.freeze

        state :definition, default: definition
      end

      case defined
      when Class
        defined.class_eval(&definition_block)
      when Module
        defined.module_eval(&definition_block)
      end
    end
  end

  # Define given state on the object.
  #
  kwargs.each_pair do |key, value|
    defined.state key, default: value
  end

  # Evaluate the given block on the object, extending it with any custom behavior.
  #
  if block
    case defined
    when Class
      defined.class_eval(&block)
    when Module
      defined.module_eval(&block)
    end
  end

  defined
end
define_namespace(*namespace) click to toggle source
public
# File lib/is/definable.rb, line 116
def define_namespace(*namespace)
  scope = Object

  namespace.each do |part|
    constant = Core::Define::Utils.camelize(part)

    scope = if scope.const_defined?(constant)
      scope.const_get(constant)
    else
      new_scope = Module.new
      scope.const_set(constant, new_scope)
    end
  end

  scope
end