Class: Rucoa::DefinitionStore

Inherits:
Object
  • Object
show all
Defined in:
lib/rucoa/definition_store.rb

Instance Method Summary collapse

Constructor Details

#initializeDefinitionStore

Returns a new instance of DefinitionStore.



5
6
7
8
# File 'lib/rucoa/definition_store.rb', line 5

def initialize
  @definition_by_qualified_name = {}
  @qualified_names_by_uri = ::Hash.new { |hash, key| hash[key] = [] }
end

Instance Method Details

#bulk_add(definitions) ⇒ void

This method returns an undefined value.

Parameters:

  • definitions (Array<Rucoa::Definition::Base>)


12
13
14
15
16
17
18
19
# File 'lib/rucoa/definition_store.rb', line 12

def bulk_add(definitions)
  definitions.group_by(&:qualified_name).each_value.map do |grouped_definitions|
    grouped_definitions.reduce(:merge!)
  end.each do |definition|
    @qualified_names_by_uri[definition.location.uri] << definition.qualified_name if definition.location
    @definition_by_qualified_name[definition.qualified_name] = definition
  end
end

#constant_definitions_under(namespace) ⇒ Array<Rucoa::Definitions::ConstantDefinition>

Returns e.g. File::Separator, File::SEPARATOR, etc.

Parameters:

  • namespace (String)

Returns:



23
24
25
26
27
# File 'lib/rucoa/definition_store.rb', line 23

def constant_definitions_under(namespace)
  constant_definitions.select do |constant_definition|
    constant_definition.namespace == namespace
  end
end

#find_definition_by_qualified_name(qualified_name) ⇒ Rucoa::Definitions::Base?

Parameters:

  • qualified_name (String)

Returns:



31
32
33
# File 'lib/rucoa/definition_store.rb', line 31

def find_definition_by_qualified_name(qualified_name)
  @definition_by_qualified_name[qualified_name]
end

#find_method_definition_by(method_name:, namespace:, singleton: false) ⇒ Rucoa::Definition::MethodDefinition?

Examples:

Supports inheritance

source = Rucoa::Source.new(
  content: <<~RUBY,
    class A
      def foo
      end
    end

    class B < A
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.find_method_definition_by(
  method_name: 'foo',
  namespace: 'B',
  singleton: false
)
expect(subject.qualified_name).to eq('A#foo')

supports ‘include`

source = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      def foo
      end
    end

    class B
      include A
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.find_method_definition_by(
  method_name: 'foo',
  namespace: 'B',
  singleton: false
)
expect(subject.qualified_name).to eq('A#foo')

supports ‘prepend`

source = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      def foo
      end
    end

    class B
      prepend A

      def foo
      end
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.find_method_definition_by(
  method_name: 'foo',
  namespace: 'B',
  singleton: false
)
expect(subject.qualified_name).to eq('A#foo')

Parameters:

  • method_name (String)
  • namespace (String)
  • singleton (Boolean) (defaults to: false)

Returns:

  • (Rucoa::Definition::MethodDefinition, nil)


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/rucoa/definition_store.rb', line 107

def find_method_definition_by(
  method_name:,
  namespace:,
  singleton: false
)
  definition = find_definition_by_qualified_name(namespace)
  return unless definition

  ancestors_of(definition).find do |ancestor|
    method_marker = singleton ? '.' : '#'
    qualified_method_name = [
      ancestor.qualified_name,
      method_marker,
      method_name
    ].join
    method_definition = find_definition_by_qualified_name(qualified_method_name)
    break method_definition if method_definition
  end
end

#inspectString

Returns:

  • (String)


128
129
130
# File 'lib/rucoa/definition_store.rb', line 128

def inspect
  "#<#{self.class} definitions_count=#{@definition_by_qualified_name.count}>"
end

#instance_method_definitions_of(type) ⇒ Array<Rucoa::Definitions::MethodDefinition>

Examples:

supports simple instance method

source = Rucoa::Source.new(
  content: <<~RUBY,
    class A
      def foo
      end
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.instance_method_definitions_of('A')
expect(subject.map(&:qualified_name)).to eq(%w[A#foo])

supports inheritance

source = Rucoa::Source.new(
  content: <<~RUBY,
    class A
      def foo
      end
    end

    class B < A
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.instance_method_definitions_of('B')
expect(subject.map(&:qualified_name)).to eq(%w[A#foo])

supports ‘include`

source = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      def foo
      end
    end

    class B
      include A
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.instance_method_definitions_of('B')
expect(subject.map(&:qualified_name)).to eq(%w[A#foo])

supports ‘prepend`

source = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      def foo
      end
    end

    class B
      prepend A

      def foo
      end
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.instance_method_definitions_of('B')
expect(subject.map(&:qualified_name)).to eq(%w[A#foo])

Parameters:

  • type (String)

Returns:



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/rucoa/definition_store.rb', line 204

def instance_method_definitions_of(type)
  singleton_class_name = singleton_class_name_from(type)
  return singleton_method_definitions_of(singleton_class_name) if singleton_class_name

  class_or_module_definition = find_definition_by_qualified_name(type)
  return [] unless class_or_module_definition

  method_definitions = self.method_definitions
  ancestors_of(class_or_module_definition).flat_map do |ancestor|
    method_definitions.select do |method_definition|
      method_definition.namespace == ancestor.qualified_name &&
        method_definition.instance_method?
    end
  end.uniq(&:method_name)
end

#resolve_constant(unqualified_name) ⇒ String

TODO:

Search ancestors.

Parameters:

Returns:

  • (String)


223
224
225
226
227
228
229
230
231
# File 'lib/rucoa/definition_store.rb', line 223

def resolve_constant(unqualified_name)
  (
    unqualified_name.module_nesting.map do |prefix|
      "#{prefix}::#{unqualified_name.chained_name}"
    end + [unqualified_name.chained_name]
  ).find do |candidate|
    find_definition_by_qualified_name(candidate)
  end || unqualified_name.chained_name
end

#singleton_method_definitions_of(type) ⇒ Array<Rucoa::Definitions::MethodDefinition>

Examples:

supports simple singleton method

source = Rucoa::Source.new(
  content: <<~RUBY,
    class A
      def self.foo
      end
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.singleton_method_definitions_of('A')
expect(subject.map(&:qualified_name)).to include('A.foo')

supports super class’s singleton method

source = Rucoa::Source.new(
  content: <<~RUBY,
    class A
      def self.foo
      end
    end

    class B < A
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.singleton_method_definitions_of('B')
expect(subject.map(&:qualified_name)).to include('A.foo')

supports extended module’s instance method

source = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      def foo
      end
    end

    class B
      def self.foo
      end
    end

    class C < B
      extend A
    end
  RUBY
  uri: 'file:///path/to/example.rb'
)
definition_store = Rucoa::DefinitionStore.new
definition_store.update_from(source)
subject = definition_store.singleton_method_definitions_of('C')
expect(subject.map(&:qualified_name)).to eq(%w[A#foo])

Parameters:

  • type (String)

Returns:



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/rucoa/definition_store.rb', line 289

def singleton_method_definitions_of(type)
  class_or_module_definition = find_definition_by_qualified_name(type)
  return [] unless class_or_module_definition

  method_definitions = self.method_definitions
  ancestors_of(class_or_module_definition).flat_map do |ancestor|
    method_definitions.select do |method_definition|
      method_definition.namespace == ancestor.qualified_name &&
        method_definition.singleton_method?
    end + ancestor.extended_module_qualified_names.flat_map do |extended_module_qualified_name|
      method_definitions.select do |method_definition|
        method_definition.namespace == extended_module_qualified_name &&
          method_definition.instance_method?
      end
    end
  end.uniq(&:method_name)
end

#update_from(source) ⇒ void

This method returns an undefined value.

Examples:

resolves super class name from definitions

definition_store = Rucoa::DefinitionStore.new
foo = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      class Foo
      end
    end
  RUBY
  uri: 'file:///path/to/a/foo.rb',
)
definition_store.update_from(foo)
bar = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      class Bar < Foo
      end
    end
  RUBY
  uri: 'file:///path/to/a/bar.rb',
)
definition_store.update_from(bar)
definition = definition_store.find_definition_by_qualified_name('A::Bar')
expect(definition.super_class_qualified_name).to eq('A::Foo')

resolves included module names from definitions

definition_store = Rucoa::DefinitionStore.new
foo = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      module Foo
      end
    end
  RUBY
  uri: 'file:///path/to/a/foo.rb',
)
definition_store.update_from(foo)
bar = Rucoa::Source.new(
  content: <<~RUBY,
    module A
      class Bar
        include Foo
      end
    end
  RUBY
  uri: 'file:///path/to/a/bar.rb',
)
definition_store.update_from(bar)
definition = definition_store.find_definition_by_qualified_name('A::Bar')
expect(definition.included_module_qualified_names).to eq(%w[A::Foo])

Parameters:



358
359
360
361
362
# File 'lib/rucoa/definition_store.rb', line 358

def update_from(source)
  delete_definitions_in(source)
  add_definitions_in(source)
  resolve_constants_in(source)
end