Ruby modules

From wikinotes
Revision as of 01:53, 11 October 2021 by Will (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Ruby modules behave like namespaces, and/or as an alternative to inheritance.
By convention, they normally match the directory name.

  • Modules may be added to by several files
  • Modules generally mimic their parent directory name.
  • When a module is included in a class, it's static-methods are added to the namespace of the class.

Tutorials

nested module traps https://www.honeybadger.io/blog/avoid-these-traps-when-nesting-ruby-modules/

Basics

  • Module.nesting prints the current nested namespace scope
  • modules are namespaces. modules can be referenced/added to in multiple files.
  • module functions can be included within classes, as an alternative to inheritance
  • module members are accessed using :: ex: Namespace::Member
  • ::Member refers to top-level namespace members. Useful when there are scope conflicts.
  • Nested modules defined within the same namespace inherit constants from their parents.
    But only if module is stated above, and not module A::B
    Unless you are using rails autoload.
    You do not collect scope from other modules nested under the same toplevel module. (ex: A::B2 does not inherit items from A::B1)

Module Scope

See excellent article: https://www.honeybadger.io/blog/avoid-these-traps-when-nesting-ruby-modules/

  • Inside of rails, C from A::B::C will always have all objects defined in A::B and A.
  • Outside of rails, C from A::B::C may or may not have all objects defined in A::B and A. It depends on how sourcecode is written.

Modules defined inside other modules inherit their parent's scope

module A
  foo = 1
  module B
    def print_foo
      puts foo
    end
  end
end

A::B.print_foo()
#> 1

Modules inheriting from a namespaced class do not inherit parent module scope

module A
  foo = 1
end

module A::B
  def print_foo
    puts foo          # <-- OUT OF SCOPE! (unless using rails autoload)
  end
end

Modules can expanded from multiple places however


# fileA
module A
  foo = 1
end
# fileB
module A
  module B
    def print_foo
      puts foo       # <-- works!
    end
  end
end

static functions

Functions defined like class-methods on an object can be used with their module's namespace.

## trig_and_moralaction
module Trig
  def Trig.sin
  end
end

module MoralAction
  def MoralAction.sin
  end
end
require "trig_and_moralaction"

MoralAction.sin()
Trig.sin()

classes

classes within module namespaces are referred to using ::.

module Foo
  class Bar
    def baz
    end
  end
end

bar = Foo::Bar.new()
bar.baz()

multi-file modules

Rather are namespace-like objects that can contain code.

  • module can be changed in multiple files
  • good practice for module-name to match directory
# my_module/my_class.rb
module MyModule
  class MyClass
    def print_hi
      puts "hi"
    end
  end
end

# my_module/other_class.rb
module MyModule
  class OtherClass
  end
end

Modules within Classes

You can use modules within classes to help compose subclasses.

class Person
  def handshake
    :sloppy
  end

  def mind_contents
    :spam
  end

  module Proper
    def handshake
      :firm
    end
  end

  module Clever
    def mind_contents
      :theories
    end
  end
end

class Professor < Person
  include Proper
  include Clever

  # ...
end

See https://stackoverflow.com/questions/3544111/why-would-we-put-a-module-inside-a-class-in-ruby

include

include produces the effect of a modules functions/attributes being merged into a class as instance methods/variables.

Under the hood, this is done by inserting the module immediately before your class in ruby's ancestor chain (inheritance order).

module MyModule
  def foo
    puts "hello"
  end
end


class MyClass
  include MyModule
end


myclass = MyClass.new
myclass.foo()

extend

Same as include, but adds module as class methods/variables (if I understand correctly).

module filepaths

Modules are defined in multiple files.
You can find them using introspection.

$LOADED_FEATURES.select { |file| File.read(file).include?('module Foo') rescue false }