Sorry, the title is kind of confusing but I just discovered something that's quite mindboggling. When you call class methods inside class methods, you don't have to put a self
before it in order for it to execute. I don't understand why this works.
For instance, suppose you have a model Food
class with the following schema:
create_table "foods", force: :cascade do |t|
t.string "name", null: false
t.string "food_type", null: false
t.datetime "expiration_date", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
class Food < ApplicationRecord
def self.output_new
new(name: 'marshmallow', food_type: 'snack')
end
end
Instead of writing Food.new
or self.new
, simply writing new
returns a newly created instance. Writing the methods using the Eigenclass syntax also yields the same results. This isn't limited to new
but it works with practically all applicable class methods like create, delete, instance_methods
, etc.
I've searched why this is the case, but I don't really understand. Would someone please explain why this is happening to ensure I'm not hallucinating?
Thank you!
Whenever you call a method, Ruby first checks to see if it's defined on self
. There is only one exception to this that I know of - if you have a local variable with the same name as the method, it will reference the local variable instead:
def num; 1; end
num = 2
num # => 2
Class methods are the same as instance methods in this regard.
It's easier to think of the cases where you can't omit the self:
When using a setter method e.g. self.foo = "bar"
it's required because Ruby needs to know you're not trying to just set a local variable (as foo = "bar"
would):
class Test
attr_writer :foo
def initialize
self.foo = 1 # calls the setter method
foo = 2 # creates a local variable
end
end
When there's ambiguity between a local variable and a method (both have the same name) and you want to call the method:
class Foo
def self.a; 1; end
def self.b
a = 2
a # references local variable
self.a # calls method
a() # can also call the method by writing out parens (removes ambiguity)
end
end
When self
is not what you want it to be in that scope.
class Foo
def self; a; 1
end
a # obviously doesn't work, it isn't defined here
Foo.a # works
When the method name is the same as a reserved word (e.g. class
)
def class_name
class.name # syntax error, it thinks you want to define a class
self.class.name # works
end