When to use self in module's methods
Asked Answered
F

4

17

My module definition looks like this:

module RG::Stats
  def self.sum(a, args = {})
    a.inject(0){ |accum, i| accum + i }
  end
end

To use this method I simply require the file containing this definition so that I can do:

RG::Stats.sum(array)

and also

RG::Stats.method(:sum)

However, if I need to know the list of methods using RG::Stats.instance_methods I get an empty array. This is because I have used self. If I omit self then RG::Stats.instance_methods gives the list of methods, but I cannot access them anymore.

The question is: how to use self in module's method definition?

Filemon answered 24/9, 2015 at 1:4 Comment(0)
D
30

Use self in each method definition if you want the methods to be defined only in the singleton class of the module (where the methods defined using self live). Omit self and extend self if you want the methods of the module to be defined as instance methods and singleton methods at the same time.

For instance, you can call the method using RG::Stats.sum(array) and still have it listed by the instance_methods method if you do this:

module RG::Stats
  extend self

  def sum(a, args = {})
    a.inject(0){ |accum, i| accum + i }
  end
end

This way, the sum method is defined as an instance method and it is included in the singleton class of the module after using extend self.

You can check the instance methods of RG::Stats module to verify this:

RG::Stats.instance_methods
=> [:sum]

With this technique you don't have to worry about defining the method without the self keyword because modules can't have instances so it cannot be called like an instance method of RG::Stats module. It can only be called as a singleton method RG::Stats.sum(array) thanks to the extend self statement.

Disarticulate answered 24/9, 2015 at 1:13 Comment(0)
N
6

If you use self, then you can use the module method directly using: RG::Stats.sum(array) as you mentioned above.

If you don't use self to define the method inside your module, then the method will be included as instance_method for a class where you include the module.

So, you can define the sum instance method inside your RG::Stats module:

module RG::Stats
  def sum(a, args = {})
    a.inject(0){ |accum, i| accum + i }
  end
end

Then, you can include RG::Stats module inside a class and then the sum method will be available as a instance_method for objects of the class:

class YourClass
  include RG::Stats

  . . .
  . . .
end

And you can call the sum method on an instance of YourClass: YourClass.new.sum

To answer your question, you can use self to define your method as a class method inside your module, and without self to define the method as instance method and it depends on your need and usecase that when you want what type of behaviour from your module methods.

Noncooperation answered 24/9, 2015 at 1:9 Comment(0)
W
3

It is because def self.sum does not define an instance method; you are using the wrong method to select it. Try this instead:

RG::Stats.methods(false)
# => [:sum]
Woodenware answered 24/9, 2015 at 1:12 Comment(0)
A
0

Class: RuboCop::Cop::Style::ModuleFunction rule recommends to use module_function to make all methods self.

ref: https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/ModuleFunction

# bad
module Test
  extend self
  # ...
end

# good
module Test
  module_function
  # ...
end
Avens answered 24/5, 2022 at 6:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.