Class: SystemNavigation

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/system_navigation.rb,
lib/system_navigation/method_hash.rb,
lib/system_navigation/method_query.rb,
lib/system_navigation/compiled_method.rb,
lib/system_navigation/expression_tree.rb,
lib/system_navigation/array_refinement.rb,
lib/system_navigation/ruby_environment.rb,
lib/system_navigation/module_refinement.rb,
lib/system_navigation/instruction_stream.rb,
lib/system_navigation/ancestor_method_finder.rb,
lib/system_navigation/instruction_stream/decoder.rb,
lib/system_navigation/instruction_stream/instruction.rb,
lib/system_navigation/instruction_stream/instruction/attr_instruction.rb

Overview

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.

Since:

  • 0.1.0

Defined Under Namespace

Modules: ArrayRefinement, ModuleRefinement Classes: AncestorMethodFinder, CompiledMethod, ExpressionTree, InstructionStream, MethodHash, MethodQuery, RubyEnvironment

Constant Summary collapse

VERSION_FILE =

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

Since:

  • 0.1.0

File.expand_path('../../VERSION', __FILE__)
VERSION =

Since:

  • 0.1.0

File.exist?(VERSION_FILE) ?
File.read(VERSION_FILE).chomp : '(could not find VERSION file)'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSystemNavigation

Returns a new instance of SystemNavigation.

Since:

  • 0.1.0



55
56
57
# File 'lib/system_navigation.rb', line 55

def initialize
  @environment = SystemNavigation::RubyEnvironment.new
end

Class Method Details

.defaultObject

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

Examples:

require 'system_navigation'

sn = SystemNavigation.default

Since:

  • 0.1.0



48
49
50
# File 'lib/system_navigation.rb', line 48

def self.default
  self.new
end

Instance Method Details

#all_accesses(to:, from: nil, only_get: nil, only_set: nil) ⇒ Array<UnboundMethod>

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

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

Examples:

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

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

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

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

Accesses to global variables

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

Accesses to class variables

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

Parameters:

  • to (Symbol)

    The name of the instance/global/class variable to search for

  • from (Class) (defaults to: nil)

    The class that limits the scope of the query. Optional. If omitted, performs the query starting from the top of the object hierarchy (BasicObject)

  • only_get (Boolean) (defaults to: nil)

    Limits the scope of the query only to methods that write into the var. Optional. Mutually exclusive with only_set

  • only_set (Boolean) (defaults to: nil)

    Limits the scope of the query only to methods that read from the var. Optional. Mutually exclusive with only_get

Returns:

  • (Array<UnboundMethod>)

    methods that access the var according to the given scope

Raises:

  • (ArgumentError)

    if both :only_get and :only_set were provided

  • (TypeError)

    if :from is not a class

  • (ArgumentError)

    if :to is not a Symbol representing either of these: class variable, instance variable, global variable

Since:

  • 0.1.0



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# 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_methodsArray<UnboundMethod>

Get all methods implemented in C.

Examples:

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

Returns:

  • (Array<UnboundMethod>)

    all methods that were implemented in C

Since:

  • 0.1.0



394
395
396
397
398
# 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) ⇒ Array<UnboundMethod>

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: ruby-doc.org/core-2.2.2/doc/syntax/literals_rdoc.html

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

Examples:

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

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

Gem

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

Parameters:

  • on (Boolean, Integer, Float, String, Symbol, Array, Hash, Range, Regexp)

    The literal to search for

  • from (Class, Module) (defaults to: nil)

    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

  • gem (String) (defaults to: nil)

    Limits the scope of the query only to methods that are defined in the RubyGem gem classes and modules. Optional.

Returns:

  • (Array<UnboundMethod>)

    methods that call the given literal

Raises:

  • (ArgumentError)

    if both keys (from: and gem:) are given

Since:

  • 0.1.0



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# 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) ⇒ Array<Class, Module>

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.

Query gems for classes and modules they implement.

Examples:

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

Returns:

  • (Array<Class, Module>)

    classes and modules that were defined by gem

Since:

  • 0.1.0



330
331
332
# 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) ⇒ Array<Class>

Query classes for methods they implement.

Examples:

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

Parameters:

  • selector (Symbol)

    the name of the method to be searched for

Returns:

  • (Array<Class>)

    classes that implement selector

Since:

  • 0.1.0



252
253
254
# 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) ⇒ Array<Class>

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.

Query gems for classes they implement.

Examples:

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

Parameters:

  • gem (String)

    The name of the gem. Case sensitive

Returns:

  • (Array<Class>)

    classes that were defined by gem

Since:

  • 0.1.0



297
298
299
# 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) ⇒ Array<Class, Module>

Query classes and modules for the methods they implement.

Examples:

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

Parameters:

  • selector (Symbol)

    the name of the method to be searched for

Returns:

  • (Array<Class, Module>)

    classes and modules that implement selector

Since:

  • 0.1.0



278
279
280
281
282
# 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_methodsArray<UnboundMethod>

Get all methods defined in the current Ruby process.

Examples:

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

Returns:

  • (Array<UnboundMethod>)

    all methods that exist

Since:

  • 0.1.0



342
343
344
345
346
# 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) ⇒ Hash

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

Examples:

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

Parameters:

  • behavior (Module, Class)

    where to read methods from

Returns:

  • (Hash)

    a hash with UnboundMethods, divided into groups and scopes

Since:

  • 0.2.1



454
455
456
# 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) ⇒ Array<UnboundMethod>

Note:

This is a very costly operation

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

Examples:

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

Parameters:

  • string (String)

    The string to be searched for

  • match_case (Boolean) (defaults to: true)

    Whether to match case or not. Optional

Returns:

  • (Array<UnboundMethod>)

    methods that matched string

Since:

  • 0.1.0



378
379
380
381
382
383
384
# 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) ⇒ Array<Class>

Query modules for the methods they implement.

Examples:

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

Parameters:

  • selector (Symbol)

    the name of the method to be searched for

Returns:

  • (Array<Class>)

    modules that implement selector

Since:

  • 0.1.0



265
266
267
# 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) ⇒ Array<Class>

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.

Query gems for modules they implement.

Examples:

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

Returns:

  • (Array<Class>)

    modules that were defined by gem

Since:

  • 0.1.0



313
314
315
# 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_methodsArray<UnboundMethod>

Get all methods implemented in Ruby.

Examples:

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

Returns:

  • (Array<UnboundMethod>)

    all methods that were implemented in Ruby

Since:

  • 0.1.0



408
409
410
411
412
# 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) ⇒ Array<UnboundMethod>

Get all methods that implement message.

Examples:

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

Parameters:

  • message (Symbol)

    The name of the method you’re interested in

Returns:

  • (Array<UnboundMethod>)

    all methods that send +message

Since:

  • 0.1.0



423
424
425
426
427
# 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_messagesArray<Symbol>

Note:

This is a very costly operation

Get all messages that all methods send.

Examples:

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

Returns:

  • (Array<Symbol>)

    all unique messages

Since:

  • 0.1.0



438
439
440
441
442
# 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