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, final = false, &block) ⇒ void

Note:

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

Creates a new namespace.



42
43
44
45
46
47
48
# File 'lib/parlour/rbi_generator/namespace.rb', line 42

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

Instance Attribute Details

#childrenArray<RbiObject> (readonly)

The child RbiObject instances inside this namespace.



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

def children
  @children
end

#finalBoolean (readonly)

Whether this namespace is final.



53
54
55
# File 'lib/parlour/rbi_generator/namespace.rb', line 53

def final
  @final
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')


131
132
133
134
135
136
137
# File 'lib/parlour/rbi_generator/namespace.rb', line 131

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.



83
84
85
86
87
88
# File 'lib/parlour/rbi_generator/namespace.rb', line 83

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!



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

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:, class_attribute: false, &block) ⇒ RbiGenerator::Attribute

Creates a new read and write attribute (attr_accessor).



406
407
408
# File 'lib/parlour/rbi_generator/namespace.rb', line 406

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

Creates a new read-only attribute (attr_reader).



364
365
366
# File 'lib/parlour/rbi_generator/namespace.rb', line 364

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

Creates a new write-only attribute (attr_writer).



385
386
387
# File 'lib/parlour/rbi_generator/namespace.rb', line 385

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) ⇒ RbiGenerator::Attribute Also known as: create_attr

Creates a new attribute.

Examples:

Create an attr_reader.

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

Create an attr_writer.

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

Create an attr_accessor.

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

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


332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/parlour/rbi_generator/namespace.rb', line 332

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

#create_class(name, final: false, 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


165
166
167
168
169
170
# File 'lib/parlour/rbi_generator/namespace.rb', line 165

def create_class(name, final: false, superclass: nil, abstract: false, &block)
  new_class = ClassNamespace.new(generator, name, final, 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


511
512
513
514
515
516
517
518
519
520
521
# File 'lib/parlour/rbi_generator/namespace.rb', line 511

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_enum_class(name, final: false, enums: nil, abstract: false, &block) ⇒ EnumClassNamespace

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

Examples:

Create a compass direction enum.

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


192
193
194
195
196
197
# File 'lib/parlour/rbi_generator/namespace.rb', line 192

def create_enum_class(name, final: false, enums: nil, abstract: false, &block)
  new_enum_class = EnumClassNamespace.new(generator, name, final, enums || [], abstract, &block)
  move_next_comments(new_enum_class)
  children << new_enum_class
  new_enum_class
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


437
438
439
440
441
442
443
444
445
446
# File 'lib/parlour/rbi_generator/namespace.rb', line 437

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


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

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


474
475
476
477
478
479
480
481
482
483
# File 'lib/parlour/rbi_generator/namespace.rb', line 474

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


493
494
495
496
497
498
499
# File 'lib/parlour/rbi_generator/namespace.rb', line 493

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) ⇒ Method

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



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/parlour/rbi_generator/namespace.rb', line 267

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, 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


223
224
225
226
227
228
# File 'lib/parlour/rbi_generator/namespace.rb', line 223

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

#create_type_alias(name, type:, &block) ⇒ RbiGenerator::Constant

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

Examples:

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

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


533
534
535
# File 'lib/parlour/rbi_generator/namespace.rb', line 533

def create_type_alias(name, type:, &block)
  create_constant(name, value: "T.type_alias { #{type} }", &block)
end

#describeString

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



581
582
583
584
# File 'lib/parlour/rbi_generator/namespace.rb', line 581

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.



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

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.



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.



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

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.

There may also be Method instances in the stream, which are ignored.



568
569
570
571
572
573
574
575
# File 'lib/parlour/rbi_generator/namespace.rb', line 568

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) ⇒ 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.)



550
551
552
# File 'lib/parlour/rbi_generator/namespace.rb', line 550

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.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/parlour/rbi_generator/namespace.rb', line 96

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