class SystemNavigation

SystemNavigation is a class that provides some introspection capabilities. It is based on a Smalltalk class with a similar name. This is the only public class in this library.

@api public @since 0.1.0

Constants

VAR_TEMPLATE
VERSION
VERSION_FILE

The VERSION file must be in the root directory of the library.

Public Class Methods

default() click to toggle source

Creates a new instance of SystemNavigation. It is added for compatibility with Smalltalk users.

@example

require 'system_navigation'

sn = SystemNavigation.default
# File lib/system_navigation.rb, line 48
def self.default
  self.new
end
new() click to toggle source
# File lib/system_navigation.rb, line 55
def initialize
  @environment = SystemNavigation::RubyEnvironment.new
end

Public Instance Methods

all_accesses(to:, from: nil, only_get: nil, only_set: nil) click to toggle source

Query methods for instance/global/class variables in descending (subclasses) and ascending (superclasses) fashion.

@note This is a very costly operation, if you don't provide the from

argument

@note This method does not perform global queries,

only relative to +from+

@example Global scope (start search from BasicObject)

class A
  def initialize
    @foo = 1
  end
end

class B
  attr_reader :foo
end

sn.all_accesses(to: :@foo)
#=> [#<UnboundMethod: A#initialize>, #<UnboundMethod: B#foo>]

@example Local scope

class A
  def initialize
    @foo = 1
  end
end

class B
  attr_reader :foo
end

sn.all_accesses(to: :@foo, from: B)
#=> [#<UnboundMethod: B#foo>]

@example Only get invokations

class A
  def initialize
    @foo = 1
  end
end

class B
  attr_reader :foo
end

sn.all_accesses(to: :@foo, only_get: true)
#=> [#<UnboundMethod: B#initialize>]

@example Only set invokations

class A
  def initialize
    @foo = 1
  end
end

class B
  attr_reader :foo
end

sn.all_accesses(to: :@foo, only_set: true)
#=> [#<UnboundMethod: A#initialize>]

@example Accesses to global variables

sn.all_accesses(to: :$DEBUG)
#=> [#<UnboundMethod: Gem::Specification#inspect>, ...]

@example Accesses to class variables

sn.all_accesses(to: :@@required_attributes)
#=> [#<UnboundMethod: Gem::Specification#validate>, ...]

@param to [Symbol] The name of the instance/global/class variable to search

for

@param from [Class] The class that limits the scope of the query. Optional.

If omitted, performs the query starting from the top of the object
hierarchy (BasicObject)

@param only_get [Boolean] Limits the scope of the query only to methods that

write into the +var+. Optional. Mutually exclusive with +only_set+

@param only_set [Boolean] Limits the scope of the query only to methods that

read from the +var+. Optional. Mutually exclusive with +only_get+

@return [Array<UnboundMethod>] methods that access the var according to

the given scope

@raise [ArgumentError] if both :only_get and :only_set were provided @raise [TypeError] if :from is not a class @raise [ArgumentError] if :to is not a Symbol representing either of

these: class variable, instance variable, global variable
# File lib/system_navigation.rb, line 147
def all_accesses(to:, from: nil, only_get: nil, only_set: nil)
  if only_set && only_get
    fail ArgumentError, 'both only_get and only_set were provided'
  end

  if from && !from.instance_of?(Class)
    fail TypeError, "from must be a Class (#{from.class} given)"
  end

  unless to.match(VAR_TEMPLATE)
    fail ArgumentError, 'invalid argument for the `to:` attribute'
  end

  (from || BasicObject).with_all_sub_and_superclasses.flat_map do |klass|
    klass.select_methods_that_access(to, only_get, only_set)
  end
end
all_c_methods() click to toggle source

Get all methods implemented in C.

@example

sn.all_c_methods
#=> [#<UnboundMethod: #<Class:Etc>#getlogin>, ...]

@return [Array<UnboundMethod>] all methods that were implemented in C

# File lib/system_navigation.rb, line 394
def all_c_methods
  self.all_classes_and_modules.flat_map do |klassmod|
    klassmod.select_c_methods
  end
end
all_calls(on:, from: nil, gem: nil) click to toggle source

Query methods for literals they call. The supported literals:

* Hashes (only simple Hashes that consist of literals itself)
* Arrays (only simple Arrays that consist of literals itself)
* +true+, +false+ and +nil+
* Integers (same Integers represented with different notations are treated
  as the same number)
* Floats
* Strings
* Ranges

@note This is a very costly operation, if you don't provide the from

argument

@note The list of supported literals can be found here:

http://ruby-doc.org/core-2.2.2/doc/syntax/literals_rdoc.html

@example Global scope (every behaviour in this process)

class A
  def foo
    :hello
  end
end

class B
  def bar
    :hello
  end
end

sn.all_calls(on: :hello)
#=> [#<UnboundMethod: A#foo>, #<UnboundMethod: B#bar>]

@example Local scope

class A
  def foo
    :hello
  end
end

class B
  def bar
    :hello
  end
end

sn.all_calls(on: :hello, from: A)
#=> [#<UnboundMethod: A#foo>]

@example Gem

sn.all_calls(on: :singleton, gem: 'system_navigation')
#=> [...]

@param on [Boolean, Integer, Float, String, Symbol, Array, Hash, Range,

Regexp] The literal to search for

@param from [Class, Module] The behaviour that limits the scope of the

query. If it's present, the search will be performed from top to bottom
(only subclasses). Optional

@param gem [String] Limits the scope of the query only to methods

that are defined in the RubyGem +gem+ classes and modules. Optional.

@return [Array<UnboundMethod>] methods that call the given literal @raise [ArgumentError] if both keys (from: and gem:) are given

# File lib/system_navigation.rb, line 226
def all_calls(on:, from: nil, gem: nil)
  if from && gem
    fail ArgumentError, 'both from and gem were provided'
  end

  subject = if from
              from.with_all_subclasses
            elsif gem
              self.all_classes_and_modules_in_gem_named(gem)
            else
              self.all_classes_and_modules
            end

  subject.flat_map { |behavior| behavior.select_methods_that_refer_to(on) }
end
all_classes_and_modules_in_gem_named(gem) click to toggle source

Query gems for classes and modules they implement.

@note This method is not precise. If a class/method in the given gem does

not implement any methods, it won't be included in the result.

@example

sn.all_classes_and_modules_in_gem_named('pry-theme')
#=> [PryTheme::Preview, ..., PryTheme::Color256]

@!macro [new] gem.param @return [Array<Class, Module>] classes and modules that were defined by

+gem+
# File lib/system_navigation.rb, line 330
def all_classes_and_modules_in_gem_named(gem)
  self.all_classes_and_modules.select { |klassmod| klassmod.belongs_to?(gem) }
end
all_classes_implementing(selector) click to toggle source

Query classes for methods they implement.

@example

sn.all_classes_implementing(:~)
#=> [Regexp, Bignum, Fixnum]

@!macro [new] selector.param

@param selector [Symbol] the name of the method to be searched for

@return [Array<Class>] classes that implement selector

# File lib/system_navigation.rb, line 252
def all_classes_implementing(selector)
  self.all_classes.select { |klass| klass.includes_selector?(selector) }
end
all_classes_in_gem_named(gem) click to toggle source

Query gems for classes they implement.

@note This method is not precise. If a class/method in the given gem does

not implement any methods, it won't be included in the result.

@example

sn.all_classes_in_gem_named('system_navigation')
#=> [SystemNavigation::AncestorMethodFinder, ..., SystemNavigation]

@!macro [new] gem.param

@param gem [String] The name of the gem. Case sensitive

@return [Array<Class>] classes that were defined by gem

# File lib/system_navigation.rb, line 297
def all_classes_in_gem_named(gem)
  self.all_classes.select { |klass| klass.belongs_to?(gem) }
end
all_implementors_of(selector) click to toggle source

Query classes and modules for the methods they implement.

@example

sn.all_implementors_of(:select)
#=> [Enumerator::Lazy, IO, ..., #<Class:Kernel>]

@!macro selector.param @return [Array<Class, Module>] classes and modules that implement selector

# File lib/system_navigation.rb, line 278
def all_implementors_of(selector)
  self.all_classes_and_modules.select do |klass|
    klass.includes_selector?(selector)
  end
end
all_methods() click to toggle source

Get all methods defined in the current Ruby process.

@example

sn.all_methods
#=> [#<UnboundMethod: Gem::Dependency#name>, ...]

@return [Array<UnboundMethod>] all methods that exist

# File lib/system_navigation.rb, line 342
def all_methods
  self.all_classes_and_modules.map do |klassmod|
    klassmod.own_methods.as_array
  end.flatten
end
all_methods_in_behavior(behavior) click to toggle source

Get all methods (public, private, protected) that are defined on behavior.

@since 0.2.1 @example

sn.all_methods_in_behavior(System)
#=> {:public => {:instance => [...], :singleton => [...]}, ...}

@param behavior [Module, Class] where to read methods from @return [Hash] a hash with UnboundMethods, divided into groups and scopes

# File lib/system_navigation.rb, line 454
def all_methods_in_behavior(behavior)
  behavior.own_methods.to_h
end
all_methods_with_source(string:, match_case: true) click to toggle source

Search for a string in all classes and modules including their comments and names.

@example

class A
  def foo
    :hello_hi
  end
end

class B
  def bar
    'hello_hi'
  end
end

module M
  # hello_hi
  def baz
  end
end

sn.all_methods_with_source(string: 'hello_hi')
#=> [#<UnboundMethod: B#bar>, #<UnboundMethod: A#foo>, #<UnboundMethod: M#foo>]

@param string [String] The string to be searched for @param match_case [Boolean] Whether to match case or not. Optional @return [Array<UnboundMethod>] methods that matched string @note This is a very costly operation

# File lib/system_navigation.rb, line 378
def all_methods_with_source(string:, match_case: true)
  return [] if string.empty?

  self.all_classes_and_modules.flat_map do |klassmod|
    klassmod.select_matching_methods(string, match_case)
  end
end
all_modules_implementing(selector) click to toggle source

Query modules for the methods they implement.

@example

sn.all_classes_implementing(:select)
#=> [Enumerable, Kernel, #<Module:0x007f56daf92918>]

@!macro selector.param @return [Array<Class>] modules that implement selector

# File lib/system_navigation.rb, line 265
def all_modules_implementing(selector)
  self.all_modules.select { |mod| mod.includes_selector?(selector) }
end
all_modules_in_gem_named(gem) click to toggle source

Query gems for modules they implement.

@note This method is not precise. If a class/method in the given gem does

not implement any methods, it won't be included in the result.

@example

sn.all_modules_in_gem_named('pry-theme')
#=> [PryTheme::Theme::DefaultAttrs, ..., PryTheme]

@!macro [new] gem.param @return [Array<Class>] modules that were defined by gem

# File lib/system_navigation.rb, line 313
def all_modules_in_gem_named(gem)
  self.all_modules.select { |mod| mod.belongs_to?(gem) }
end
all_rb_methods() click to toggle source

Get all methods implemented in Ruby.

@example

sn.all_rb_methods
#=> [#<UnboundMethod: Gem::Dependency#name>, ...]

@return [Array<UnboundMethod>] all methods that were implemented in Ruby

# File lib/system_navigation.rb, line 408
def all_rb_methods
  self.all_classes_and_modules.flat_map do |klassmod|
    klassmod.select_rb_methods
  end
end
all_senders_of(message) click to toggle source

Get all methods that implement message.

@example

sn.all_senders_of(:puts)
#=> []

@param message [Symbol] The name of the method you're interested in @return [Array<UnboundMethod>] all methods that send +message

# File lib/system_navigation.rb, line 423
def all_senders_of(message)
  self.all_classes_and_modules.flat_map do |klassmod|
    klassmod.select_senders_of(message)
  end
end
all_sent_messages() click to toggle source

Get all messages that all methods send.

@example

sn.all_sent_messages
#=> [:name, :hash, ..., :type]

@return [Array<Symbol>] all unique messages @note This is a very costly operation

# File lib/system_navigation.rb, line 438
def all_sent_messages
  self.all_classes_and_modules.flat_map do |klassmod|
    klassmod.all_messages.as_array
  end.uniq
end