Class: RbsActivesupport::DeclarationBuilder

Inherits:
Object
  • Object
show all
Includes:
AST
Defined in:
lib/rbs_activesupport/declaration_builder.rb,
sig/rbs_activesupport/declaration_builder.rbs

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from AST

#eval_args_with_options, #eval_include_args, #eval_node

Constructor Details

#initialize(resolver, method_searcher) ⇒ DeclarationBuilder

Returns a new instance of DeclarationBuilder.

Parameters:

  • resolver (RBS::Resolver::TypeNameResolver)
  • method_searcher (MethodSearcher)


15
16
17
18
19
# File 'lib/rbs_activesupport/declaration_builder.rb', line 15

def initialize(resolver, method_searcher) #: void
  @resolver = resolver
  @method_searcher = method_searcher
  @processed_modules = []
end

Instance Attribute Details

#method_searcherMethodSearcher (readonly)

: MethodSearcher

Returns:



10
11
12
# File 'lib/rbs_activesupport/declaration_builder.rb', line 10

def method_searcher
  @method_searcher
end

#processed_modulesArray[RBS::Namespace] (readonly)

: Array

Returns:

  • (Array[RBS::Namespace])


11
12
13
# File 'lib/rbs_activesupport/declaration_builder.rb', line 11

def processed_modules
  @processed_modules
end

#resolverRBS::Resolver::TypeNameResolver (readonly)

: RBS::Resolver::TypeNameResolver

Returns:

  • (RBS::Resolver::TypeNameResolver)


9
10
11
# File 'lib/rbs_activesupport/declaration_builder.rb', line 9

def resolver
  @resolver
end

Instance Method Details

#build(namespace, method_calls, context = nil) ⇒ [ Array[String], Array[String] ]

Parameters:

  • namespace (RBS::Namespace)
  • method_calls (Array[Parser::MethodCall])
  • context (RBS::Namespace, nil) (defaults to: nil)

Returns:

  • ([ Array[String], Array[String] ])


24
25
26
27
28
29
# File 'lib/rbs_activesupport/declaration_builder.rb', line 24

def build(namespace, method_calls, context = nil) #: [Array[String], Array[String]]
  built = build_method_calls(namespace, method_calls, context)
  public_decls, private_decls = built.partition(&:public?)
  [public_decls.map { |decl| render(namespace, decl) },
   private_decls.map { |decl| render(namespace, decl) }]
end

#build_attribute_accessor(method_call) ⇒ Array[AttributeAccessor]

Parameters:

Returns:



58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/rbs_activesupport/declaration_builder.rb', line 58

def build_attribute_accessor(method_call) #: Array[AttributeAccessor]
  methods, options = eval_args_with_options(method_call.args)
  options[:singleton_reader] = false if i[cattr_writer mattr_writer].include?(method_call.name)
  options[:singleton_writer] = false if i[cattr_reader mattr_reader].include?(method_call.name)
  options[:instance_reader] = false if i[cattr_writer mattr_writer].include?(method_call.name)
  options[:instance_writer] = false if i[cattr_reader mattr_reader].include?(method_call.name)
  options[:private] = true if method_call.private?
  options[:included] = method_call.included
  options[:trailing_comment] = method_call.trailing_comment
  methods.map do |method|
    AttributeAccessor.new(method, options)
  end
end

#build_class_attribute(method_call) ⇒ Array[ClassAttribute]

Parameters:

Returns:



73
74
75
76
77
78
79
80
81
# File 'lib/rbs_activesupport/declaration_builder.rb', line 73

def build_class_attribute(method_call) #: Array[ClassAttribute]
  methods, options = eval_args_with_options(method_call.args)
  options[:private] = true if method_call.private?
  options[:included] = method_call.included
  options[:trailing_comment] = method_call.trailing_comment
  methods.map do |method|
    ClassAttribute.new(method, options)
  end
end

#build_delegate(namespace, method_call) ⇒ Array[Delegate]

Parameters:

Returns:



85
86
87
88
89
90
91
# File 'lib/rbs_activesupport/declaration_builder.rb', line 85

def build_delegate(namespace, method_call) #: Array[Delegate]
  methods, options = eval_args_with_options(method_call.args)
  options[:private] = true if method_call.private?
  methods.map do |method|
    Delegate.new(namespace, method, options)
  end
end

#build_include(namespace, method_call, context, implicit:) ⇒ Array[t]

Parameters:

  • namespace (RBS::Namespace)
  • method_call (Parser::MethodCall)
  • context (RBS::Namespace, nil)
  • implicit: (Boolean)

Returns:

  • (Array[t])


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/rbs_activesupport/declaration_builder.rb', line 97

def build_include(namespace, method_call, context, implicit:) #: Array[t]  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
  module_paths = eval_include_args(method_call.args)
  module_paths.delete_if do |module_path|
    unless module_path.is_a?(RBS::Namespace)
      puts "ERROR: #{namespace}:#{method_call.name}: Failed to recognize an included module: #{module_path}"
      true
    end
  end
  module_paths.filter_map do |module_path|
    include = Include.new(context || namespace, module_path, { private: method_call.private?, implicit: })

    if include.module_name
      next if processed_modules.include?(include.module_name)

      processed_modules << include.module_name
    end

    calls = [] #: Array[t]
    calls << include if include.implicit? || (include.concern? && include.classmethods?)
    calls.concat(build_method_calls(namespace, include.nested_includes, include.module_name))
    calls.concat(build_method_calls(namespace, include.method_calls_in_included_block, include.module_name,
                                    { implicit_include: true }))
    calls
  end.flatten
end

#build_method_calls(namespace, method_calls, context, options = {}) ⇒ Array[t]

Parameters:

  • namespace (RBS::Namespace)
  • method_calls (Array[Parser::MethodCall])
  • context (RBS::Namespace, nil)
  • options (Hash[Symbol, untyped]) (defaults to: {})

Returns:

  • (Array[t])


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/rbs_activesupport/declaration_builder.rb', line 37

def build_method_calls(namespace, method_calls, context, options = {}) #: Array[t]
  method_calls.flat_map do |method_call|
    case method_call.name
    when :class_attribute
      build_class_attribute(method_call)
    when :delegate
      build_delegate(namespace, method_call)
    when :cattr_accessor, :mattr_accessor, :cattr_reader, :mattr_reader, :cattr_writer, :mattr_writer
      build_attribute_accessor(method_call)
    when :include
      # implicit include is an "include" internally (e.g. include call in the included block)
      implicit = options.fetch(:implicit_include, false)
      build_include(namespace, method_call, context, implicit:)
    end
  rescue StandardError => e
    puts "ERROR: #{namespace}:#{method_call.name}: Failed to build method calls: #{e}"
    nil
  end.compact
end

#context_from(type_name) ⇒ RBS::Resolver::context

Parameters:

  • type_name (RBS::TypeName)

Returns:

  • (RBS::Resolver::context)


200
201
202
203
204
205
206
207
# File 'lib/rbs_activesupport/declaration_builder.rb', line 200

def context_from(type_name) #: RBS::Resolver::context
  if type_name.namespace == RBS::Namespace.root
    [nil, type_name]
  else
    parent = context_from(type_name.namespace.to_type_name)
    [parent, type_name]
  end
end

#render(namespace, decl) ⇒ String

Parameters:

  • namespace (RBS::Namespace)
  • decl (t)

Returns:

  • (String)


125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/rbs_activesupport/declaration_builder.rb', line 125

def render(namespace, decl) #: String
  case decl
  when AttributeAccessor
    render_attribute_accessor(namespace, decl)
  when ClassAttribute
    render_class_attribute(namespace, decl)
  when Delegate
    render_delegate(decl)
  when Include
    render_include(decl)
  end
end

#render_attribute_accessor(namespace, decl) ⇒ String

Parameters:

Returns:

  • (String)


140
141
142
143
144
145
146
147
148
# File 'lib/rbs_activesupport/declaration_builder.rb', line 140

def render_attribute_accessor(namespace, decl) #: String
  type = resolve_type(namespace, decl.type)
  methods = []
  methods << "def self.#{decl.name}: () -> (#{type})" if decl.singleton_reader?
  methods << "def self.#{decl.name}=: (#{type}) -> (#{type})" if decl.singleton_writer?
  methods << "def #{decl.name}: () -> (#{type})" if decl.instance_reader?
  methods << "def #{decl.name}=: (#{type}) -> (#{type})" if decl.instance_writer?
  methods.join("\n")
end

#render_class_attribute(namespace, decl) ⇒ String

Parameters:

Returns:

  • (String)


152
153
154
155
156
157
158
159
160
161
162
# File 'lib/rbs_activesupport/declaration_builder.rb', line 152

def render_class_attribute(namespace, decl) #: String
  type = resolve_type(namespace, decl.type)
  methods = []
  methods << "def self.#{decl.name}: () -> (#{type})"
  methods << "def self.#{decl.name}=: (#{type}) -> (#{type})"
  methods << "def self.#{decl.name}?: () -> bool" if decl.instance_predicate?
  methods << "def #{decl.name}: () -> (#{type})" if decl.instance_reader?
  methods << "def #{decl.name}=: (#{type}) -> (#{type})" if decl.instance_writer?
  methods << "def #{decl.name}?: () -> bool" if decl.instance_predicate? && decl.instance_reader?
  methods.join("\n")
end

#render_delegate(decl) ⇒ String

Parameters:

Returns:

  • (String)


165
166
167
168
169
# File 'lib/rbs_activesupport/declaration_builder.rb', line 165

def render_delegate(decl) #: String
  method_types = method_searcher.method_types_for(decl)

  "def #{decl.method_name}: #{method_types.join(" | ")}"
end

#render_include(decl) ⇒ String

Parameters:

Returns:

  • (String)


172
173
174
175
176
177
178
# File 'lib/rbs_activesupport/declaration_builder.rb', line 172

def render_include(decl) #: String
  module_name = decl.module_name || decl.module_path
  mods = []
  mods << "include #{module_name.to_s.delete_suffix("::")}" if decl.implicit?
  mods << "extend #{module_name}ClassMethods" if decl.concern? && decl.classmethods?
  mods.join("\n")
end

#resolve_type(namespace, type) ⇒ String

Parameters:

  • namespace (RBS::Namespace)
  • type (String)

Returns:

  • (String)


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/rbs_activesupport/declaration_builder.rb', line 182

def resolve_type(namespace, type) #: String
  context = context_from(namespace.to_type_name)

  typ = RBS::Parser.parse_type(type)
  if typ
    typ.map_type_name do |type_name|
      resolver.resolve(type_name, context:) || type_name.absolute!
    rescue StandardError
      # Resolver failed to resolve the type name because of a lack of type database
      # (might not have been generated yet).  It will be resolved in the next execution.
      type_name.absolute!
    end.to_s
  else
    type
  end
end