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 addFoo
to the ancestors ofObject
. [...] all ofFoo
'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
Regex to Validate String Having Only Characters (Not Special Characters), Blank Spaces and Numbers
Ruby Google_Drive Gem Oauth2 Saving
Ruby 1.9.2 - Read and Parse a Remote CSV
Rails: How to Check If a Column Has a Value
Kill Process and Sub-Processes in Ruby on Windows
Ruby Map Method Syntax Question
Another Way Instead of Escaping Regex Patterns
Understanding Ruby Splat in Ranges and Arrays
Ruby - Extracting the Unique Values Per Key from an Array of Hashes
Exception_Notification for Delayed_Job
How to 'Unload' ('Un-Require') a Ruby Library
What Do 'Def +@' and 'Def -@' Mean
Rails How to Change Attribute Name When Rendering JSON