Why do some modules have inner classes? - ruby

I know how a module can be used in a class in Ruby:
module Calculator
def add(a,b)
a+b
end
end
class Watch
include Calculator
def time
Time.now
end
end
w = Watch.new()
puts w.time # 2020-03-11 22:34:01 +0000
puts w.add(3,5) # 8
But, sometimes I can see some module has a class inside. For instance, in Rails, helpers:
module MyModule
class MyClass
def foo
puts 'foo'
end
end
end
What's the point of this?
Why would I have a class inside a module?

Modules can be used that way for scoping purposes. In your example you can refer to MyClass from outside the module by MyModule::MyClass.
There are several uses for this, like grouping together related classes under a common namespace.
It's better to get more information about this from some Ruby tutorials (for examples and stuff). Check out "Ruby - Modules and Mixins" for more information.

Related

Ruby Class namespacing with modules: Why do I get NameError with double colons but not module blocks?

I am working with a lot of pre-existing files, classes, and modules and trying to come up with better namespacing for the different components of the framework. I've been using modules as a way to namespace mainly because this seems like the standard convention (and being able to 'include' different parts of the framework could be useful).
The problem is that there was a ton of classes underneath the global namespace that should exist underneath a module. For example, let's say there is a class that was simply defined as:
class FirstClass
def meth
puts "HELLO"
end
end
But now I want to have this class within a module:
Using Double Colons:
module Foo; end
class Foo::FirstClass
def meth
puts 'HELLO'
end
end
Using Module Blocks:
module Foo
class FirstClass
def meth
puts 'HELLO'
end
end
Using double colons is a lot cleaner and also a lot easier to implement since I am changing many class definitions. Both of these ways work and I thought that they are both effectively the same thing, but evidently they are not. The double colon method seems to result in a different namespace within each class compared to the module block. For instance, with two classes underneath "Foo":
Using Module Blocks:
module Foo
class FirstClass
def meth
puts 'HELLO'
end
end
class SecondClass
def meth
FirstClass.new.meth
end
end
end
Foo::SecondClass.new.meth
Using Double Colons:
module Foo; end
class Foo::FirstClass
def meth
puts 'HELLO'
end
end
class Foo::SecondClass
def meth
FirstClass.new.meth
end
end
Foo::SecondClass.new.meth
The code works when using module blocks, but doesn't work with double colons. With the double colons, NameError is raised because it resolves FirstClass as Foo::SecondClass::FirstClass (instead of Foo::FirstClass), which doesn't exist.
This can easily be solved by including Foo in SecondClass, but how come this isn't done by default?
Note: I'm using Ruby 2.1.5, which I know is outdated, but I get the same results on repl.it with ruby 2.5.5p157: https://repl.it/#joep2/Colon-vs-Block-Namespacing
It may seem counter-intuitive, but constant lookup in Ruby is done using current lexical scope, i.e. the current lexical nesting level (location in the source code), not the semantic nesting level.
This can be tested by inspecting Module.nesting, which prints the current lexical scope:
class Foo::SecondClass
pp Module.nesting # -> [Foo::SecondClass]
end
module Foo
class SecondClass
pp Module.nesting # -> [Foo::SecondClass, Foo]
end
end
Since Ruby uses this nesting level for symbol lookup, it means in the situation where you try to look up FirstClass within nesting [Foo::SecondClass], Ruby will not find it.
However when you try to look it up within nesting [Foo::SecondClass, Foo], it will find FirstClass under Foo, just like you expect.
To get around this, you could do:
class Foo::SecondClass
def meth
Foo::FirstClass.new.meth
end
end
Which will now work as you expect, since you provided the necessary lookup hint for FirstClass, and told Ruby it is inside Foo.

ruby class instance variable configuration pattern

I'm trying to make a DSL like configuration for classes that include a module but to have the configured variable available to both class and instance methods seems to require littering the module with access methods. Is there a more elegant way to do this?
module DogMixin
class << self
def included(base)
base.extend ClassMethods
end
end
module ClassMethods
def breed(value)
#dog_breed = value
end
def dog_breed
#dog_breed
end
end
end
class Foo
include DogMixin
breed :havanese
end
puts Foo.dog_breed
# not implemented but should be able to do this as well
f = Foo.new
f.dog_breed
Your example is a bit weird I think :)
Anyway, one way to avoid writing the accessors (the assignment - accessor is problematic in my eyes - especially in the given example) is to define constants, as in the example below. If however you need runtime-assignments, please edit your question (and thus render this answer invalid :) except you want to mess with runtime constant assignment, which is possible but messy).
module DogMixin
# **include** DogMixin to get `Class.dog_breed`
class << self
def included(base)
def base.dog_breed
self::DOG_BREED || "pug"
end
end
end
# **extend** DogMixin to get `instance.dog_breed`
def dog_breed
self.class.const_get(:DOG_BREED) || "pug"
end
end
class Foomer
DOG_BREED = 'foomer'
extend DogMixin
include DogMixin
end
f = Foomer.new
puts Foomer.dog_breed
puts f.dog_breed
# If I understand you correctly, this is the most important (?):
f.dog_breed == Foomer.dog_breed #=> true
It took some reading of (In Ruby) allowing mixed-in class methods access to class constants to get the Instance-And-Class Constant lookup from a module, but it works. I am not sure if I really like the solution though. Good question, although you could add a little detail.

Ruby and module inheritance

This should be an easy one for a ruby dev. I'm playing around with a gem and I need help with inheriting module variables. Code should speak better than me:
module SomeModule
extend ActiveSupport::Concern
attr_accessor :bbonified
class Railtie < Rails::Railtie
initializer "some_module.study" do
Rails.application.eager_load!
# => I WANT TO ACCESS HERE
puts #bbonified
end
end
module ClassMethods
def bbonify(*columns)
# => WHAT I DEFINE HERE
#bbonified = columns
end
end
end
ActiveRecord::Base.send(:include, SomeModule)
You're not going to be able to access #bbonified directly, that's a local variable in the class that imports this module.
You need to define a separate accessor method to retrieve it:
module ClassMethods
def bbonified
#bbonified
end
end
Then later you need to refer to this somehow, but as you're just talking about a module it will depend on what class has been extended.
Rails.application.eager_load!
SomeClass.bbonified

How can I refer to a module method without referring to the entire namespace?

I have a module defined as:
module MyApp
module Utility
def Utility.my_method
I want to use that method in several other classes. But I don't want to have to call:
MyApp::Utility.my_method
I would rather just call:
Utility.my_method
Is that reasonable? I've tried include MyApp::Utility and include MyApp to no avail.
Well, just assign any alias you want, e.g.:
ShortNameGoesHere = MyApp::Utility
ShortNameGoesHere.my_method
Here is an example of mixing in my_method to a class:
#myapp.rb
module MyApp
module Utility
def my_method
"called my_method"
end
end
end
#test.rb
require './myapp'
class MyClass
include MyApp::Utility
end
if __FILE__ == $0 then
m = MyClass.new
puts m.my_method
end
It sounds like you want to maintain the namespace of the module on the mixed-in method. I have seen attempts to do so (https://stackoverflow.com/a/7575460/149212) but it seems pretty messy.
If you need my_method to be namespaced, you could simply include a module identifier in the method name:
module MyApp
module Utility
def util_my_method
"called my_method"
end
end
end

Can I invoke an instance method on a Ruby module without including it?

Background:
I have a module which declares a number of instance methods
module UsefulThings
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
And I want to call some of these methods from within a class. How you normally do this in ruby is like this:
class UsefulWorker
include UsefulThings
def do_work
format_text("abc")
...
end
end
Problem
include UsefulThings brings in all of the methods from UsefulThings. In this case I only want format_text and explicitly do not want get_file and delete_file.
I can see several possible solutions to this:
Somehow invoke the method directly on the module without including it anywhere
I don't know how/if this can be done. (Hence this question)
Somehow include Usefulthings and only bring in some of it's methods
I also don't know how/if this can be done
Create a proxy class, include UsefulThings in that, then delegate format_text to that proxy instance
This would work, but anonymous proxy classes are a hack. Yuck.
Split up the module into 2 or more smaller modules
This would also work, and is probably the best solution I can think of, but I'd prefer to avoid it as I'd end up with a proliferation of dozens and dozens of modules - managing this would be burdensome
Why are there lots of unrelated functions in a single module? It's ApplicationHelper from a rails app, which our team has de-facto decided on as the dumping ground for anything not specific enough to belong anywhere else. Mostly standalone utility methods that get used everywhere. I could break it up into seperate helpers, but there'd be 30 of them, all with 1 method each... this seems unproductive
I think the shortest way to do just throw-away single call (without altering existing modules or creating new ones) would be as follows:
Class.new.extend(UsefulThings).get_file
If a method on a module is turned into a module function you can simply call it off of Mods as if it had been declared as
module Mods
def self.foo
puts "Mods.foo(self)"
end
end
The module_function approach below will avoid breaking any classes which include all of Mods.
module Mods
def foo
puts "Mods.foo"
end
end
class Includer
include Mods
end
Includer.new.foo
Mods.module_eval do
module_function(:foo)
public :foo
end
Includer.new.foo # this would break without public :foo above
class Thing
def bar
Mods.foo
end
end
Thing.new.bar
However, I'm curious why a set of unrelated functions are all contained within the same module in the first place?
Edited to show that includes still work if public :foo is called after module_function :foo
Another way to do it if you "own" the module is to use module_function.
module UsefulThings
def a
puts "aaay"
end
module_function :a
def b
puts "beee"
end
end
def test
UsefulThings.a
UsefulThings.b # Fails! Not a module method
end
test
If you want to call these methods without including module in another class then you need to define them as module methods:
module UsefulThings
def self.get_file; ...
def self.delete_file; ...
def self.format_text(x); ...
end
and then you can call them with
UsefulThings.format_text("xxx")
or
UsefulThings::format_text("xxx")
But anyway I would recommend that you put just related methods in one module or in one class. If you have problem that you want to include just one method from module then it sounds like a bad code smell and it is not good Ruby style to put unrelated methods together.
To invoke a module instance method without including the module (and without creating intermediary objects):
class UsefulWorker
def do_work
UsefulThings.instance_method(:format_text).bind(self).call("abc")
...
end
end
Not sure if someone still needs it after 10 years but I solved it using eigenclass.
module UsefulThings
def useful_thing_1
"thing_1"
end
class << self
include UsefulThings
end
end
class A
include UsefulThings
end
class B
extend UsefulThings
end
UsefulThings.useful_thing_1 # => "thing_1"
A.new.useful_thing_1 # => "thing_1"
B.useful_thing_1 # => "thing_1"
Firstly, I'd recommend breaking the module up into the useful things you need. But you can always create a class extending that for your invocation:
module UsefulThings
def a
puts "aaay"
end
def b
puts "beee"
end
end
def test
ob = Class.new.send(:include, UsefulThings).new
ob.a
end
test
A. In case you, always want to call them in a "qualified", standalone way (UsefulThings.get_file), then just make them static as others pointed out,
module UsefulThings
def self.get_file; ...
def self.delete_file; ...
def self.format_text(x); ...
# Or.. make all of the "static"
class << self
def write_file; ...
def commit_file; ...
end
end
B. If you still want to keep the mixin approach in same cases, as well the one-off standalone invocation, you can have a one-liner module that extends itself with the mixin:
module UsefulThingsMixin
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
module UsefulThings
extend UsefulThingsMixin
end
So both works then:
UsefulThings.get_file() # one off
class MyUser
include UsefulThingsMixin
def f
format_text # all useful things available directly
end
end
IMHO it's cleaner than module_function for every single method - in case want all of them.
As I understand the question, you want to mix some of a module's instance methods into a class.
Let's begin by considering how Module#include works. Suppose we have a module UsefulThings that contains two instance methods:
module UsefulThings
def add1
self + 1
end
def add3
self + 3
end
end
UsefulThings.instance_methods
#=> [:add1, :add3]
and Fixnum includes that module:
class Fixnum
def add2
puts "cat"
end
def add3
puts "dog"
end
include UsefulThings
end
We see that:
Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
#=> [:add2, :add3, :add1]
1.add1
2
1.add2
cat
1.add3
dog
Were you expecting UsefulThings#add3 to override Fixnum#add3, so that 1.add3 would return 4? Consider this:
Fixnum.ancestors
#=> [Fixnum, UsefulThings, Integer, Numeric, Comparable,
# Object, Kernel, BasicObject]
When the class includes the module, the module becomes the class' superclass. So, because of how inheritance works, sending add3 to an instance of Fixnum will cause Fixnum#add3 to be invoked, returning dog.
Now let's add a method :add2 to UsefulThings:
module UsefulThings
def add1
self + 1
end
def add2
self + 2
end
def add3
self + 3
end
end
We now wish Fixnum to include only the methods add1 and add3. Is so doing, we expect to get the same results as above.
Suppose, as above, we execute:
class Fixnum
def add2
puts "cat"
end
def add3
puts "dog"
end
include UsefulThings
end
What is the result? The unwanted method :add2 is added to Fixnum, :add1 is added and, for reasons I explained above, :add3 is not added. So all we have to do is undef :add2. We can do that with a simple helper method:
module Helpers
def self.include_some(mod, klass, *args)
klass.send(:include, mod)
(mod.instance_methods - args - klass.instance_methods).each do |m|
klass.send(:undef_method, m)
end
end
end
which we invoke like this:
class Fixnum
def add2
puts "cat"
end
def add3
puts "dog"
end
Helpers.include_some(UsefulThings, self, :add1, :add3)
end
Then:
Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
#=> [:add2, :add3, :add1]
1.add1
2
1.add2
cat
1.add3
dog
which is the result we want.
After almost 9 years here's a generic solution:
module CreateModuleFunctions
def self.included(base)
base.instance_methods.each do |method|
base.module_eval do
module_function(method)
public(method)
end
end
end
end
RSpec.describe CreateModuleFunctions do
context "when included into a Module" do
it "makes the Module's methods invokable via the Module" do
module ModuleIncluded
def instance_method_1;end
def instance_method_2;end
include CreateModuleFunctions
end
expect { ModuleIncluded.instance_method_1 }.to_not raise_error
end
end
end
The unfortunate trick you need to apply is to include the module after the methods have been defined. Alternatively you may also include it after the context is defined as ModuleIncluded.send(:include, CreateModuleFunctions).
Or you can use it via the reflection_utils gem.
spec.add_dependency "reflection_utils", ">= 0.3.0"
require 'reflection_utils'
include ReflectionUtils::CreateModuleFunctions
This old question comes to me today when I am studing Ruby and found interesting so I want to answer with my new knowlege.
Assume that you have the module
module MyModule
def say
'I say'
end
def cheer
'I cheer'
end
end
then with the class so call Animal I can take cheer method from MyModule as following
class Animal
define_method(:happy, MyModule.method(:cheer))
end
This is so called unbound method, so you can take a callable object and bind it to another place(s).
From this point, you can use the method as usual, such as
my_dog = Animal.new
my_dog.happy # => "I cheer"
Hope this help as I also learned something new today.
To learn further, you can use irb and take a look at Method object.

Resources