class Parlour::RbiGenerator::Namespace

A generic namespace. This shouldn't be used, except as the type of {RbiGenerator#root}.

Attributes

children[R]

The child {RbiObject} instances inside this namespace. @return [Array<RbiObject>]

final[R]

Whether this namespace is final. @return [Boolean]

sealed[R]

Whether this namespace is sealed. @return [Boolean]

Public Class Methods

new(generator, name = nil, final = false, sealed = false, &block) click to toggle source

Creates a new namespace. @note Unless you're doing something impressively hacky, this shouldn't

be invoked outside of {RbiGenerator#initialize}.

@param generator [RbiGenerator] The current RbiGenerator. @param name [String, nil] The name of this module. @param final [Boolean] Whether this namespace is final. @param final [Boolean] Whether this namespace is sealed. @param block A block which the new instance yields itself to. @return [void]

Calls superclass method
# File lib/parlour/rbi_generator/namespace.rb, line 44
def initialize(generator, name = nil, final = false, sealed = false, &block)
  super(generator, name || '<anonymous namespace>')
  @children = []
  @next_comments = []
  @final = final
  @sealed = sealed
  yield_self(&block) if block
end

Public Instance Methods

add_comment_to_next_child(comment) click to toggle source

Adds one or more comments to the next child RBI object to be created.

@example Creating a module with a comment.

namespace.add_comment_to_next_child('This is a module')
namespace.create_module('M')

@example Creating a class with a multi-line comment.

namespace.add_comment_to_next_child(['This is a multi-line comment!', 'It can be as long as you want!'])
namespace.create_class('C')

@param comment [String, Array<String>] The new comment(s). @return [void]

# File lib/parlour/rbi_generator/namespace.rb, line 153
def add_comment_to_next_child(comment)
  if comment.is_a?(String)
    @next_comments << comment
  elsif comment.is_a?(Array)
    @next_comments.concat(comment)
  end
end
aliases() click to toggle source

The {RbiGenerator::TypeAlias} objects from {children}. @return [Array<RbiGenerator::TypeAlias>]

# File lib/parlour/rbi_generator/namespace.rb, line 91
def aliases
  T.cast(
    children.select { |c| c.is_a?(RbiGenerator::TypeAlias) },
    T::Array[RbiGenerator::TypeAlias]
  )
end
Also aliased as: type_aliases
constants() click to toggle source

The {RbiGenerator::Constant} objects from {children}. @return [Array<RbiGenerator::Constant>]

# File lib/parlour/rbi_generator/namespace.rb, line 102
def constants
  T.cast(
    children.select { |c| c.is_a?(RbiGenerator::Constant) },
    T::Array[RbiGenerator::Constant]
  )
end
create_arbitrary(code:, &block) click to toggle source

Creates a new arbitrary code section. You should rarely have to use this!

@param code [String] The code to insert. @param block A block which the new instance yields itself to. @return [RbiGenerator::Arbitrary]

# File lib/parlour/rbi_generator/namespace.rb, line 478
def create_arbitrary(code:, &block)
  new_arbitrary = RbiGenerator::Arbitrary.new(
    generator,
    code: code,
    &block
  )
  move_next_comments(new_arbitrary)
  children << new_arbitrary
  new_arbitrary
end
create_attr(name, kind:, type:, class_attribute: false, &block)
Alias for: create_attribute
create_attr_accessor(name, type:, class_attribute: false, &block) click to toggle source

Creates a new read and write attribute (attr_accessor).

@param name [String] The name of this attribute. @param type [String] A Sorbet string of this attribute's type, such as

+"String"+ or +"T.untyped"+.

@param class_attribute [Boolean] Whether this attribute belongs to the

singleton class.

@param block A block which the new instance yields itself to. @return [RbiGenerator::Attribute]

# File lib/parlour/rbi_generator/namespace.rb, line 468
def create_attr_accessor(name, type:, class_attribute: false, &block)
  create_attribute(name, kind: :accessor, type: type, class_attribute: class_attribute, &block)
end
create_attr_reader(name, type:, class_attribute: false, &block) click to toggle source

Creates a new read-only attribute (attr_reader).

@param name [String] The name of this attribute. @param type [String] A Sorbet string of this attribute's type, such as

+"String"+ or +"T.untyped"+.

@param class_attribute [Boolean] Whether this attribute belongs to the

singleton class.

@param block A block which the new instance yields itself to. @return [RbiGenerator::Attribute]

# File lib/parlour/rbi_generator/namespace.rb, line 426
def create_attr_reader(name, type:, class_attribute: false, &block)
  create_attribute(name, kind: :reader, type: type, class_attribute: class_attribute, &block)
end
create_attr_writer(name, type:, class_attribute: false, &block) click to toggle source

Creates a new write-only attribute (attr_writer).

@param name [String] The name of this attribute. @param type [String] A Sorbet string of this attribute's type, such as

+"String"+ or +"T.untyped"+.

@param class_attribute [Boolean] Whether this attribute belongs to the

singleton class.

@param block A block which the new instance yields itself to. @return [RbiGenerator::Attribute]

# File lib/parlour/rbi_generator/namespace.rb, line 447
def create_attr_writer(name, type:, class_attribute: false, &block)
  create_attribute(name, kind: :writer, type: type, class_attribute: class_attribute, &block)
end
create_attribute(name, kind:, type:, class_attribute: false, &block) click to toggle source

Creates a new attribute.

@example Create an attr_reader.

module.create_attribute('readable', kind: :reader, type: 'String')
# #=> sig { returns(String) }
#     attr_reader :readable

@example Create an attr_writer.

module.create_attribute('writable', kind: :writer, type: 'Integer')
# #=> sig { params(writable: Integer).returns(Integer) }
#     attr_writer :writable

@example Create an attr_accessor.

module.create_attribute('accessible', kind: :accessor, type: 'T::Boolean')
# #=> sig { returns(T::Boolean) }
#     attr_accessor :accessible

@example Create an attr_accessor on the singleton class.

module.create_attribute('singleton_attr', kind: :accessor, type: 'T::Boolean')
# #=> class << self
#       sig { returns(T::Boolean) }
#       attr_accessor :singleton_attr
#     end

@param name [String] The name of this attribute. @param kind [Symbol] The kind of attribute this is; one of :writer, :reader, or

+:accessor+.

@param type [String] A Sorbet string of this attribute's type, such as

+"String"+ or +"T.untyped"+.

@param class_attribute [Boolean] Whether this attribute belongs to the

singleton class.

@param block A block which the new instance yields itself to. @return [RbiGenerator::Attribute]

# File lib/parlour/rbi_generator/namespace.rb, line 394
def create_attribute(name, kind:, type:, class_attribute: false, &block)
  new_attribute = RbiGenerator::Attribute.new(
    generator,
    name,
    kind,
    type,
    class_attribute: class_attribute,
    &block
  )
  move_next_comments(new_attribute)
  children << new_attribute
  new_attribute
end
Also aliased as: create_attr
create_class(name, final: false, sealed: false, superclass: nil, abstract: false, &block) click to toggle source

Creates a new class definition as a child of this namespace.

@example Create a class with a nested module.

namespace.create_class('Foo') do |foo|
  foo.create_module('Bar')
end

@example Create a class that is the child of another class.

namespace.create_class('Bar', superclass: 'Foo') #=> class Bar < Foo

@param name [String] The name of this class. @param final [Boolean] Whether this namespace is final. @param sealed [Boolean] Whether this namespace is sealed. @param superclass [String, nil] The superclass of this class, or nil if it doesn't

have one.

@param abstract [Boolean] A boolean indicating whether this class is abstract. @param block A block which the new instance yields itself to. @return [ClassNamespace]

# File lib/parlour/rbi_generator/namespace.rb, line 189
def create_class(name, final: false, sealed: false, superclass: nil, abstract: false, &block)
  new_class = ClassNamespace.new(generator, name, final, sealed, superclass, abstract, &block)
  move_next_comments(new_class)
  children << new_class
  new_class
end
create_constant(name, value:, eigen_constant: false, &block) click to toggle source

Adds a new constant definition to this namespace.

@example Add an Elem constant to the class.

class.create_constant('Elem', value: 'String') #=> Elem = String

@param name [String] The name of the constant. @param value [String] The value of the constant, as a Ruby code string. @param eigen_constant [Boolean] Whether this constant is defined on the

eigenclass of the current namespace.

@param block A block which the new instance yields itself to. @return [RbiGenerator::Constant]

# File lib/parlour/rbi_generator/namespace.rb, line 575
def create_constant(name, value:, eigen_constant: false, &block)
  new_constant = RbiGenerator::Constant.new(
    generator,
    name: name,
    value: value,
    eigen_constant: eigen_constant,
    &block
  )
  move_next_comments(new_constant)
  children << new_constant
  new_constant
end
create_enum_class(name, final: false, sealed: false, enums: nil, abstract: false, &block) click to toggle source

Creates a new enum class definition as a child of this namespace.

@example Create a compass direction enum.

namespace.create_class('Direction', enums: ['North', 'South', 'East', 'West'])

@param name [String] The name of this class. @param final [Boolean] Whether this namespace is final. @param sealed [Boolean] Whether this namespace is sealed. @param enums [Array<(String, String), String>] The values of the enumeration. @param abstract [Boolean] A boolean indicating whether this class is abstract. @param block A block which the new instance yields itself to. @return [EnumClassNamespace]

# File lib/parlour/rbi_generator/namespace.rb, line 218
def create_enum_class(name, final: false, sealed: false, enums: nil, abstract: false, &block)
  new_enum_class = EnumClassNamespace.new(generator, name, final, sealed, enums || [], abstract, &block)
  move_next_comments(new_enum_class)
  children << new_enum_class
  new_enum_class
end
create_extend(name, &block) click to toggle source

Adds a new extend to this namespace.

@example Add an extend to a class.

class.create_extend('ExtendableClass') #=> extend ExtendableClass

@param object [String] A code string for what is extended, for example

+"MyModule"+.

@param block A block which the new instance yields itself to. @return [RbiGenerator::Extend]

# File lib/parlour/rbi_generator/namespace.rb, line 499
def create_extend(name, &block)
  new_extend = RbiGenerator::Extend.new(
    generator,
    name: name,
    &block
  )
  move_next_comments(new_extend)
  children << new_extend
  new_extend
end
create_extends(extendables) click to toggle source

Adds new +extend+s to this namespace.

@example Add +extend+s to a class.

class.create_extends(['Foo', 'Bar'])

@param [Array<String>] extendables An array of names for whatever is being extended. @return [Array<RbiGenerator::Extend>]

# File lib/parlour/rbi_generator/namespace.rb, line 518
def create_extends(extendables)
  returned_extendables = []
  extendables.each do |extendable|
    returned_extendables << create_extend(extendable)
  end
  returned_extendables
end
create_include(name, &block) click to toggle source

Adds a new include to this namespace.

@example Add an include to a class.

class.create_include('IncludableClass') #=> include IncludableClass

@param [String] name A code string for what is included, for example

+"Enumerable"+.

@param block A block which the new instance yields itself to. @return [RbiGenerator::Include]

# File lib/parlour/rbi_generator/namespace.rb, line 536
def create_include(name, &block)
  new_include = RbiGenerator::Include.new(
    generator,
    name: name,
    &block
  )
  move_next_comments(new_include)
  children << new_include
  new_include
end
create_includes(includables) click to toggle source

Adds new +include+s to this namespace.

@example Add +include+s to a class.

class.create_includes(['Foo', 'Bar'])

@param [Array<String>] includables An array of names for whatever is being included. @return [Array<RbiGenerator::Include>]

# File lib/parlour/rbi_generator/namespace.rb, line 555
def create_includes(includables)
  returned_includables = []
  includables.each do |includable|
    returned_includables << create_include(includable)
  end
  returned_includables
end
create_method(name, parameters: nil, return_type: nil, returns: nil, abstract: false, implementation: false, override: false, overridable: false, class_method: false, final: false, type_parameters: nil, &block) click to toggle source

Creates a new method definition as a child of this namespace.

@param name [String] The name of this method. You should not specify self. in

this - use the +class_method+ parameter instead.

@param parameters [Array<Parameter>] An array of {Parameter} instances representing this

method's parameters.

@param return_type [String, nil] A Sorbet string of what this method returns, such as

+"String"+ or +"T.untyped"+. Passing nil denotes a void return.

@param returns [String, nil] Same as return_type. @param abstract [Boolean] Whether this method is abstract. @param implementation [Boolean] DEPRECATED: Whether this method is an

implementation of a parent abstract method.

@param override [Boolean] Whether this method is overriding a parent overridable

method, or implementing a parent abstract method.

@param overridable [Boolean] Whether this method is overridable by subclasses. @param class_method [Boolean] Whether this method is a class method; that is, it

it is defined using +self.+.

@param final [Boolean] Whether this method is final. @param type_parameters [Array<Symbol>, nil] This method's type parameters. @param block A block which the new instance yields itself to. @return [Method]

# File lib/parlour/rbi_generator/namespace.rb, line 329
def create_method(name, parameters: nil, return_type: nil, returns: nil, abstract: false, implementation: false, override: false, overridable: false, class_method: false, final: false, type_parameters: nil, &block)
  parameters = parameters || []
  raise 'cannot specify both return_type: and returns:' if return_type && returns
  return_type ||= returns
  new_method = RbiGenerator::Method.new(
    generator,
    name,
    parameters,
    return_type,
    abstract: abstract,
    implementation: implementation,
    override: override,
    overridable: overridable,
    class_method: class_method,
    final: final,
    type_parameters: type_parameters,
    &block
  )
  move_next_comments(new_method)
  children << new_method
  new_method
end
create_module(name, final: false, sealed: false, interface: false, abstract: false, &block) click to toggle source

Creates a new module definition as a child of this namespace.

@example Create a basic module.

namespace.create_module('Foo')

@example Create a module with a method.

namespace.create_module('Foo') do |foo|
  foo.create_method('method_name', parameters: [], return_type: 'Integer')
end

@param name [String] The name of this module. @param final [Boolean] Whether this namespace is final. @param sealed [Boolean] Whether this namespace is sealed. @param interface [Boolean] A boolean indicating whether this module is an

interface.

@param abstract [Boolean] A boolean indicating whether this module is

abstract.

@param block A block which the new instance yields itself to. @return [ModuleNamespace]

# File lib/parlour/rbi_generator/namespace.rb, line 285
def create_module(name, final: false, sealed: false, interface: false, abstract: false, &block)
  new_module = ModuleNamespace.new(generator, name, final, sealed, interface, abstract, &block)
  move_next_comments(new_module)
  children << new_module
  new_module
end
create_struct_class(name, final: false, sealed: false, props: nil, abstract: false, &block) click to toggle source

Creates a new struct class definition as a child of this namespace.

@example Create a person struct.

namespace.create_class('Person', props: [
  Parlour::RbiGenerator::StructProp.new('name', 'String')
])

@param name [String] The name of this class. @param final [Boolean] Whether this namespace is final. @param sealed [Boolean] Whether this namespace is sealed. @param props [Array<StructProp>] The props of the struct. @param abstract [Boolean] A boolean indicating whether this class is abstract. @param block A block which the new instance yields itself to. @return [EnumClassNamespace]

# File lib/parlour/rbi_generator/namespace.rb, line 249
def create_struct_class(name, final: false, sealed: false, props: nil, abstract: false, &block)
  new_struct_class = StructClassNamespace.new(generator, name, final, sealed, props || [], abstract, &block)
  move_next_comments(new_struct_class)
  children << new_struct_class
  new_struct_class
end
create_type_alias(name, type:, &block) click to toggle source

Adds a new type alias, in the form of a constant, to this namespace.

@example Add a MyType type alias, to Integer, to the class.

class.create_type_alias('MyType', type: 'Integer') #=> MyType = T.type_alias { Integer }

@param name [String] The name of the type alias. @param value [String] The type to alias, as a Ruby code string. @param block A block which the new instance yields itself to. @return [RbiGenerator::Constant]

# File lib/parlour/rbi_generator/namespace.rb, line 598
def create_type_alias(name, type:, &block)
  new_type_alias = RbiGenerator::TypeAlias.new(
    generator,
    name: name,
    type: type,
    &block
  )
  move_next_comments(new_type_alias)
  children << new_type_alias
  new_type_alias
end
describe() click to toggle source

Returns a human-readable brief string description of this namespace.

@return [String]

# File lib/parlour/rbi_generator/namespace.rb, line 654
def describe
  "Namespace #{name} - #{children.length} children, #{includes.length} " +
    "includes, #{extends.length} extends, #{constants.length} constants"
end
extends() click to toggle source

The {RbiGenerator::Extend} objects from {children}. @return [Array<RbiGenerator::Extend>]

# File lib/parlour/rbi_generator/namespace.rb, line 71
def extends
  T.cast(
    children.select { |c| c.is_a?(RbiGenerator::Extend) },
    T::Array[RbiGenerator::Extend]
  )
end
generalize_from_rbi!() click to toggle source
# File lib/parlour/rbi_generator/namespace.rb, line 660
def generalize_from_rbi!
  children.each(&:generalize_from_rbi!)
end
generate_rbi(indent_level, options) click to toggle source

Generates the RBI lines for this namespace.

@param indent_level [Integer] The indentation level to generate the lines at. @param options [Options] The formatting options to use. @return [Array<String>] The RBI lines, formatted as specified.

# File lib/parlour/rbi_generator/namespace.rb, line 20
def generate_rbi(indent_level, options)
  generate_comments(indent_level, options) +
    generate_body(indent_level, options)
end
includes() click to toggle source

The {RbiGenerator::Include} objects from {children}. @return [Array<RbiGenerator::Include>]

# File lib/parlour/rbi_generator/namespace.rb, line 81
def includes
  T.cast(
    children.select { |c| c.is_a?(RbiGenerator::Include) },
    T::Array[RbiGenerator::Include]
  )
end
merge_into_self(others) click to toggle source

Given an array of {Namespace} instances, merges them into this one. All children, constants, extends and includes are copied into this instance.

There may also be {RbiGenerator::Method} instances in the stream, which are ignored.

@param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances. @return [void]

# File lib/parlour/rbi_generator/namespace.rb, line 641
def merge_into_self(others)
  others.each do |other|
    next if other.is_a?(RbiGenerator::Method)
    other = T.cast(other, Namespace)

    other.children.each { |c| children << c }
  end
end
mergeable?(others) click to toggle source

Given an array of {Namespace} instances, returns true if they may be merged into this instance using {merge_into_self}. All bare namespaces can be merged into each other, as they lack definitions for themselves, so there is nothing to conflict. (This isn't the case for subclasses such as {ClassNamespace}.)

@param others [Array<RbiGenerator::RbiObject>] An array of other {Namespace} instances. @return [true] Always true.

# File lib/parlour/rbi_generator/namespace.rb, line 623
def mergeable?(others)
  true
end
path(constant, &block) click to toggle source

Given a constant (i.e. a Module instance), generates all classes and modules in the path to that object, then executes the given block on the last {Namespace}. This should only be executed on the root namespace. @param [Module] constant @param block A block which the new {Namespace} yields itself to.

# File lib/parlour/rbi_generator/namespace.rb, line 116
def path(constant, &block)
  raise 'only call #path on root' if is_a?(ClassNamespace) || is_a?(ModuleNamespace)

  constant_name = T.let(Module.instance_method(:name).bind(constant).call, T.nilable(String))
  raise 'given constant does not have a name' unless constant_name

  current_part = self
  constant_name.split('::').each_with_object([]) do |name, namespace|
    namespace << name
    instance = Module.const_get(namespace.join("::"))

    case instance
    when Class
      current_part = current_part.create_class(name)
    when Module
      current_part = current_part.create_module(name)
    else
      raise "unexpected type: path part #{name} is a #{instance.class}"
    end
  end

  block.call(current_part)
end
type_aliases()
Alias for: aliases

Private Instance Methods

generate_body(indent_level, options) click to toggle source

Generates the RBI lines for the body of this namespace. This consists of {includes}, {extends} and {children}.

@param indent_level [Integer] The indentation level to generate the lines at. @param options [Options] The formatting options to use. @return [Array<String>] The RBI lines for the body, formatted as specified.

# File lib/parlour/rbi_generator/namespace.rb, line 678
def generate_body(indent_level, options)
  result = []

  result += [options.indented(indent_level, 'final!'), ''] if final
  result += [options.indented(indent_level, 'sealed!'), ''] if sealed

  # Split away the eigen constants; these need to be put in a
  # "class << self" block later
  eigen_constants, non_eigen_constants = constants.partition(&:eigen_constant)
  eigen_constants.sort_by!(&:name) if options.sort_namespaces

  if includes.any? || extends.any? || aliases.any? || non_eigen_constants.any?
    result += (options.sort_namespaces ? includes.sort_by(&:name) : includes)
      .flat_map { |x| x.generate_rbi(indent_level, options) }
      .reject { |x| x.strip == '' }
    result += (options.sort_namespaces ? extends.sort_by(&:name) : extends)
      .flat_map { |x| x.generate_rbi(indent_level, options) }
      .reject { |x| x.strip == '' }
    result += (options.sort_namespaces ? aliases.sort_by(&:name) : aliases)
      .flat_map { |x| x.generate_rbi(indent_level, options) }
      .reject { |x| x.strip == '' }
    result += (options.sort_namespaces ? non_eigen_constants.sort_by(&:name) : non_eigen_constants)
      .flat_map { |x| x.generate_rbi(indent_level, options) }
      .reject { |x| x.strip == '' }
    result << ""
  end

  # Process singleton class attributes
  sorted_children = (
    if options.sort_namespaces
      # sort_by can be unstable (and is in current MRI).
      # Use the this work around to preserve order for ties
      children.sort_by.with_index { |child, i| [child.name, i] }
    else
      children
    end
  )
  class_attributes, remaining_children = sorted_children.partition do |child|
    child.is_a?(Attribute) && child.class_attribute
  end

  # Handle the "class << self block"
  result << options.indented(indent_level, 'class << self') \
    if class_attributes.any? || eigen_constants.any?

  if eigen_constants.any?
    first, *rest = eigen_constants
    result += T.must(first).generate_rbi(indent_level + 1, options) + T.must(rest)
      .map { |obj| obj.generate_rbi(indent_level + 1, options) }
      .map { |lines| [""] + lines }
      .flatten
  end

  result << '' if eigen_constants.any? && class_attributes.any?

  if class_attributes.any?
    first, *rest = class_attributes
    result += T.must(first).generate_rbi(indent_level + 1, options) + T.must(rest)
      .map { |obj| obj.generate_rbi(indent_level + 1, options) }
      .map { |lines| [""] + lines }
      .flatten
  end

  if class_attributes.any? || eigen_constants.any?
    result << options.indented(indent_level, 'end')
    result << ''
  end

  first, *rest = remaining_children.reject do |child|
    # We already processed these kinds of children
    child.is_a?(Include) || child.is_a?(Extend) || child.is_a?(Constant) || child.is_a?(TypeAlias)
  end
  unless first
    # Remove any trailing whitespace due to includes or class attributes
    result.pop while result.last == ''
    return result
  end

  result += first.generate_rbi(indent_level, options) + T.must(rest)
    .map { |obj| obj.generate_rbi(indent_level, options) }
    .map { |lines| [""] + lines }
    .flatten

  result
end
move_next_comments(object) click to toggle source

Copies the comments added with {#add_comment_to_next_child} into the given object, and clears the list of pending comments. @param object [RbiObject] The object to move the comments into. @return [void]

# File lib/parlour/rbi_generator/namespace.rb, line 769
def move_next_comments(object)
  object.comments.unshift(*@next_comments)
  @next_comments.clear
end