In Ruby or Rails, Why Is "Include" Sometimes Inside the Class and Sometimes Outside the Class

In Ruby or Rails, why is include sometimes inside the class and sometimes outside the class?

Yes, include Foo inside a class adds Foo to that class's ancestors and thus makes all of Foo's instance methods available to instances of those class.

Outside of any class definition include Foo will add Foo to the ancestors of Object. I.e. it is the same as if you did include Foo inside the definition of the Object class. The use doing this is that all of Foo's instance methods are now available everywhere.

How does include module works inside vs outside the class

From the other answer:

Outside of any class definition include Foo will add Foo to the ancestors of Object. [...] all of Foo's instance methods are now available everywhere.

By including a module at the top level, you are effectively saying:

Object.include(Human)

# or

class Object
include Human
end

What you are missing is that modules and classes are objects, too.

The methods an object responds to come from its ancestors, e.g.:

1.0.class.ancestors
#=> [Float, Numeric, Comparable, Object, Kernel, BasicObject]

'abc'.class.ancestors
#=> [String, Comparable, Object, Kernel, BasicObject]

or, for your module:

Human.class.ancestors
#=> [Module, Object, Kernel, BasicObject]

When you include Human at the top level, it adds that module to every object's ancestors:

include Human

1.0.class.ancestors
#=> [Float, Numeric, Comparable, Object, Human, Kernel, BasicObject]
# ^^^^^

'abc'.class.ancestors
#=> [String, Comparable, Object, Human, Kernel, BasicObject]
# ^^^^^

Human.class.ancestors
#=> [Module, Object, Human, Kernel, BasicObject]
# ^^^^^

Some examples:

1.0.living   #=> true
'abc'.living #=> true
Float.living #=> true
Human.living #=> true

Why are global methods allowed to be defined outside of a class in Ruby?

When you define a function in Ruby at the global scope in this way, it technically becomes a private method of the Object class, which is the base class that everything inherits from in Ruby. Everything in Ruby is an object, so it is indeed true that you have defined a method.

def say_goodnight(name)
result = "Goodnight, " + name
return result
end

Object.private_methods.include? :say_goodnight
=> true

Because it is defined as a method with private visibility on Object, it can only be called inside objects of the class on which it's defined or subclasses. So why does it appear to be available globally?

Basically, the Ruby program itself defines an instance of Object called main, which serves as the top-level scope where your method was defined. So if you think of your program as running inside main (which is an Object) its private methods are available for use.

# In irb:
self
=> main
self.class
=> Object
self.private_methods.include? :say_goodnight
=> true

Addendum:
This answer which further explains how main is defined and implemented.

Update for Ruby >= 2.3

Noted in the comment thread, later versions of Ruby would define the method Object#say_goodnight in this example with public visibility rather than private. This behavior appears to have changed between Ruby 2.2.x and 2.3.x, but does not affect method exposure.

Instance variables in Ruby; why do I have to (sometimes) specify `self`?

As you have shown, you can always access an attribute directly using the instance variable (@counter). However your issue here is relating to the getter/setter methods generated by attr_accessor.

The getter method does not require self unless you have a local variable with the same name. Setter methods are different. You always need to use self with setters.

For example:

def test_method
# directly set instance var. this will always work
@counter = 1

# define local variable with same name.
# this does not call the setter because you don't use self
counter = 0

puts counter
# prints 0
# The getter method is never called because you have a local variable
# with the same name.

puts self.counter
# prints 1
# you can force the getter to be called by using self
end

I think the idiomatic way to write your method would be:

def increment_v5
self.counter += 1
end

However you could also write it like this:

def increment_v6
self.counter = counter + 1
# \ calls getter
end

and there are many other ways to write it.

What is the difference between include and extend in Ruby?

What you have said is correct. However, there is more to it than that.

If you have a class Klazz and module Mod, including Mod in Klazz gives instances of Klazz access to Mod's methods. Or you can extend Klazz with Mod giving the class Klazz access to Mod's methods. But you can also extend an arbitrary object with o.extend Mod. In this case the individual object gets Mod's methods even though all other objects with the same class as o do not.

Difference between a class and a module

The first answer is good and gives some structural answers, but another approach is to think about what you're doing. Modules are about providing methods that you can use across multiple classes - think about them as "libraries" (as you would see in a Rails app). Classes are about objects; modules are about functions.

For example, authentication and authorization systems are good examples of modules. Authentication systems work across multiple app-level classes (users are authenticated, sessions manage authentication, lots of other classes will act differently based on the auth state), so authentication systems act as shared APIs.

You might also use a module when you have shared methods across multiple apps (again, the library model is good here).



Related Topics



Leave a reply



Submit