Class: Parlour::RbiGenerator::Namespace

Inherits:
RbiObject
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/parlour/rbi_generator/namespace.rb

Overview

A generic namespace. This shouldn’t be used, except as the type of #root.

Direct Known Subclasses

ClassNamespace, ModuleNamespace

Instance Attribute Summary collapse

Attributes inherited from RbiObject

#comments, #generated_by, #generator, #name

Instance Method Summary collapse

Methods inherited from RbiObject

#add_comment

Constructor Details

#initialize(generator, name = nil, &block) ⇒ void

Note:

Unless you’re doing something impressively hacky, this shouldn’t be invoked outside of Parlour::RbiGenerator#initialize.

Creates a new namespace.

Parameters:

  • generator (RbiGenerator)

    The current RbiGenerator.

  • name (String, nil) (defaults to: nil)

    The name of this module.

  • block

    A block which the new instance yields itself to.



40
41
42
43
44
45
# File 'lib/parlour/rbi_generator/namespace.rb', line 40

def initialize(generator, name = nil, &block)
  super(generator, name || '<anonymous namespace>')
  @children = []
  @next_comments = []
  yield_self(&block) if block
end

Instance Attribute Details

#childrenArray<RbiObject> (readonly)

The child RbiObject instances inside this namespace.

Returns:



50
51
52
# File 'lib/parlour/rbi_generator/namespace.rb', line 50

def children
  @children
end

Instance Method Details

#add_comment_to_next_child(comment) ⇒ void

This method returns an undefined value.

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

Examples:

Creating a module with a comment.

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

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')

Parameters:

  • comment (String, Array<String>)

    The new comment(s).



123
124
125
126
127
128
129
# File 'lib/parlour/rbi_generator/namespace.rb', line 123

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

#constantsArray<RbiGenerator::Constant>

The Constant objects from #children.

Returns:



75
76
77
78
79
80
# File 'lib/parlour/rbi_generator/namespace.rb', line 75

def constants
  T.cast(
    children.select { |c| c.is_a?(RbiGenerator::Constant) },
    T::Array[RbiGenerator::Constant]
  )
end

#create_arbitrary(code:, &block) ⇒ RbiGenerator::Arbitrary

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

Parameters:

  • code (String)

    The code to insert.

  • block

    A block which the new instance yields itself to.

Returns:



322
323
324
325
326
327
328
329
330
331
# File 'lib/parlour/rbi_generator/namespace.rb', line 322

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_accessor(name, type:, &block) ⇒ RbiGenerator::Attribute

Creates a new read and write attribute (attr_accessor).

Parameters:

  • name (String)

    The name of this attribute.

  • type (String)

    A Sorbet string of this attribute’s type, such as “String” or “T.untyped”.

  • block

    A block which the new instance yields itself to.

Returns:



312
313
314
# File 'lib/parlour/rbi_generator/namespace.rb', line 312

def create_attr_accessor(name, type:, &block)
  create_attribute(name, kind: :accessor, type: type, &block)
end

#create_attr_reader(name, type:, &block) ⇒ RbiGenerator::Attribute

Creates a new read-only attribute (attr_reader).

Parameters:

  • name (String)

    The name of this attribute.

  • type (String)

    A Sorbet string of this attribute’s type, such as “String” or “T.untyped”.

  • block

    A block which the new instance yields itself to.

Returns:



290
291
292
# File 'lib/parlour/rbi_generator/namespace.rb', line 290

def create_attr_reader(name, type:, &block)
  create_attribute(name, kind: :reader, type: type, &block)
end

#create_attr_writer(name, type:, &block) ⇒ RbiGenerator::Attribute

Creates a new write-only attribute (attr_writer).

Parameters:

  • name (String)

    The name of this attribute.

  • type (String)

    A Sorbet string of this attribute’s type, such as “String” or “T.untyped”.

  • block

    A block which the new instance yields itself to.

Returns:



301
302
303
# File 'lib/parlour/rbi_generator/namespace.rb', line 301

def create_attr_writer(name, type:, &block)
  create_attribute(name, kind: :writer, type: type, &block)
end

#create_attribute(name, kind:, type:, &block) ⇒ RbiGenerator::Attribute Also known as: create_attr

Creates a new attribute.

Examples:

Create an attr_reader.

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

Create an attr_writer.

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

Create an attr_accessor.

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

Parameters:

  • name (String)

    The name of this attribute.

  • kind (Symbol)

    The kind of attribute this is; one of :writer, :reader, or :accessor.

  • type (String)

    A Sorbet string of this attribute’s type, such as “String” or “T.untyped”.

  • block

    A block which the new instance yields itself to.

Returns:



269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/parlour/rbi_generator/namespace.rb', line 269

def create_attribute(name, kind:, type:, &block)
  new_attribute = RbiGenerator::Attribute.new(
    generator,
    name,
    kind,
    type,
    &block
  )
  move_next_comments(new_attribute)
  children << new_attribute
  new_attribute
end

#create_class(name, superclass: nil, abstract: false, &block) ⇒ ClassNamespace

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

Examples:

Create a class with a nested module.

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

Create a class that is the child of another class.

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

Parameters:

  • name (String)

    The name of this class.

  • superclass (String, nil) (defaults to: nil)

    The superclass of this class, or nil if it doesn’t have one.

  • abstract (Boolean) (defaults to: false)

    A boolean indicating whether this class is abstract.

  • block

    A block which the new instance yields itself to.

Returns:



155
156
157
158
159
160
# File 'lib/parlour/rbi_generator/namespace.rb', line 155

def create_class(name, superclass: nil, abstract: false, &block)
  new_class = ClassNamespace.new(generator, name, superclass, abstract, &block)
  move_next_comments(new_class)
  children << new_class
  new_class
end

#create_constant(name, value:, &block) ⇒ RbiGenerator::Constant

Adds a new constant definition to this namespace.

Examples:

Add an Elem constant to the class.

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

Parameters:

  • name (String)

    The name of the constant.

  • value (String)

    The value of the constant, as a Ruby code string.

  • block

    A block which the new instance yields itself to.

Returns:



417
418
419
420
421
422
423
424
425
426
427
# File 'lib/parlour/rbi_generator/namespace.rb', line 417

def create_constant(name, value:, &block)
  new_constant = RbiGenerator::Constant.new(
    generator,
    name: name,
    value: value,
    &block
  )
  move_next_comments(new_constant)
  children << new_constant
  new_constant
end

#create_extend(name, &block) ⇒ RbiGenerator::Extend

Adds a new extend to this namespace.

Examples:

Add an extend to a class.

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

Parameters:

  • object (String)

    A code string for what is extended, for example “MyModule”.

  • block

    A block which the new instance yields itself to.

Returns:



343
344
345
346
347
348
349
350
351
352
# File 'lib/parlour/rbi_generator/namespace.rb', line 343

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) ⇒ Array<RbiGenerator::Extend>

Adds new extends to this namespace.

Examples:

Add extends to a class.

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

Parameters:

  • extendables (Array<String>)

    An array of names for whatever is being extended.

Returns:



362
363
364
365
366
367
368
# File 'lib/parlour/rbi_generator/namespace.rb', line 362

def create_extends(extendables)
  returned_extendables = []
  extendables.each do |extendable|
    returned_extendables << create_extend(extendable)
  end
  returned_extendables
end

#create_include(name, &block) ⇒ RbiGenerator::Include

Adds a new include to this namespace.

Examples:

Add an include to a class.

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

Parameters:

  • name (String)

    A code string for what is included, for example “Enumerable”.

  • block

    A block which the new instance yields itself to.

Returns:



380
381
382
383
384
385
386
387
388
389
# File 'lib/parlour/rbi_generator/namespace.rb', line 380

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) ⇒ Array<RbiGenerator::Include>

Adds new includes to this namespace.

Examples:

Add includes to a class.

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

Parameters:

  • includables (Array<String>)

    An array of names for whatever is being included.

Returns:



399
400
401
402
403
404
405
# File 'lib/parlour/rbi_generator/namespace.rb', line 399

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, &block) ⇒ Method

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

Parameters:

  • name (String)

    The name of this method. You should not specify self. in this - use the class_method parameter instead.

  • parameters (Array<Parameter>) (defaults to: nil)

    An array of Parameter instances representing this method’s parameters.

  • return_type (String, nil) (defaults to: nil)

    A Sorbet string of what this method returns, such as “String” or “T.untyped”. Passing nil denotes a void return.

  • returns (String, nil) (defaults to: nil)

    Same as return_type.

  • abstract (Boolean) (defaults to: false)

    Whether this method is abstract.

  • implementation (Boolean) (defaults to: false)

    Whether this method is an implementation of a parent abstract method.

  • override (Boolean) (defaults to: false)

    Whether this method is overriding a parent overridable method.

  • overridable (Boolean) (defaults to: false)

    Whether this method is overridable by subclasses.

  • class_method (Boolean) (defaults to: false)

    Whether this method is a class method; that is, it it is defined using self..

  • block

    A block which the new instance yields itself to.

Returns:



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/parlour/rbi_generator/namespace.rb', line 224

def create_method(name, parameters: nil, return_type: nil, returns: nil, abstract: false, implementation: false, override: false, overridable: false, class_method: false, &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,
    &block
  )
  move_next_comments(new_method)
  children << new_method
  new_method
end

#create_module(name, interface: false, &block) ⇒ ModuleNamespace

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

Examples:

Create a basic module.

namespace.create_module('Foo')

Create a module with a method.

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

Parameters:

  • name (String)

    The name of this module.

  • interface (Boolean) (defaults to: false)

    A boolean indicating whether this module is an interface.

  • block

    A block which the new instance yields itself to.

Returns:



184
185
186
187
188
189
# File 'lib/parlour/rbi_generator/namespace.rb', line 184

def create_module(name, interface: false, &block)
  new_module = ModuleNamespace.new(generator, name, interface, &block)
  move_next_comments(new_module)
  children << new_module
  new_module
end

#describeString

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

Returns:

  • (String)


469
470
471
472
# File 'lib/parlour/rbi_generator/namespace.rb', line 469

def describe
  "Namespace #{name} - #{children.length} children, #{includes.length} " +
    "includes, #{extends.length} extends, #{constants.length} constants"
end

#extendsArray<RbiGenerator::Extend>

The Extend objects from #children.

Returns:



55
56
57
58
59
60
# File 'lib/parlour/rbi_generator/namespace.rb', line 55

def extends
  T.cast(
    children.select { |c| c.is_a?(RbiGenerator::Extend) },
    T::Array[RbiGenerator::Extend]
  )
end

#generate_rbi(indent_level, options) ⇒ Array<String>

Generates the RBI lines for this namespace.

Parameters:

  • indent_level (Integer)

    The indentation level to generate the lines at.

  • options (Options)

    The formatting options to use.

Returns:

  • (Array<String>)

    The RBI lines, formatted as specified.



20
21
22
23
# 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

#includesArray<RbiGenerator::Include>

The Include objects from #children.

Returns:



65
66
67
68
69
70
# File 'lib/parlour/rbi_generator/namespace.rb', line 65

def includes
  T.cast(
    children.select { |c| c.is_a?(RbiGenerator::Include) },
    T::Array[RbiGenerator::Include]
  )
end

#merge_into_self(others) ⇒ void

This method returns an undefined value.

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

Parameters:



457
458
459
460
461
462
463
# File 'lib/parlour/rbi_generator/namespace.rb', line 457

def merge_into_self(others)
  others.each do |other|
    other = T.cast(other, Namespace)

    other.children.each { |c| children << c }
  end
end

#mergeable?(others) ⇒ true

Given an array of Parlour::RbiGenerator::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.)

Parameters:

Returns:

  • (true)

    Always true.



442
443
444
# File 'lib/parlour/rbi_generator/namespace.rb', line 442

def mergeable?(others)
  true
end

#path(object, &block) ⇒ Object

Given a Class or Module object, generates all classes and modules in the path to that object, then executes the given block on the last Parlour::RbiGenerator::Namespace. This should only be executed on the root namespace.

Parameters:



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/parlour/rbi_generator/namespace.rb', line 88

def path(object, &block)
  raise 'only call #path on root' if is_a?(ClassNamespace) || is_a?(ModuleNamespace)

  parts = object.to_s.split('::')
  parts_with_types = parts.size.times.map do |i|
    [parts[i], Module.const_get(parts[0..i].join('::')).class]
  end

  current_part = self
  parts_with_types.each do |(name, type)|
    if type == Class
      current_part = current_part.create_class(name)
    elsif type == Module
      current_part = current_part.create_module(name)
    else
      raise "unexpected type: path part #{name} is a #{type}"
    end
  end

  block.call(current_part)
end