Inside a class definition (e.g. class User ...
), def encrypt ...
defines an instance method, whereas def self.encrypt ...
defines a class method.
Defining instance methods and class methods
Suppose we defined the User class like this:
class User
def initialize(name)
@name_instance_variable = name
end
# This is an instance method definition, creating a method called `encrypt`
# on instances of the User class.
def encrypt
puts "Received User#encrypt instance method.\n" +
"- `self` is an instance of #{ self.class }.\n" +
"- `@name_instance_variable` is #{ @name_instance_variable.inspect }.\n"
end
# This is a class method definition, creating a method called `encrypt` on
# the User class itself.
def self.encrypt
puts "Received User.encrypt class method.\n" +
"- `self` is an instance of #{ self.class }.\n" +
"- `@name_instance_variable` is #{ @name_instance_variable.inspect }.\n"
end
end
Invoking an instance method
Now we can create an instance of User with the constructor User.new
, which (automatically) invokes initialize
above:
my_user = User.new("Tim")
In initialize
, the value we gave to the constructor is assigned to the instance variable @name_instance_variable
.
Now we can invoke instance methods on my_user
, our instance of the User class:
my_user.encrypt
# => Received User#encrypt instance method.
# - `self` is an instance of User.
# - `@name_instance_variable` is "Tim".
We can create another instance and give a different value to the constructor:
User.new("Jordan").encrypt
# => Received User#encrypt instance method.
# - `self` is an instance of User.
# - `@name_instance_variable` is "Jordan".
Invoking a class method:
We can also invoke class methods on the User class:
User.encrypt
# => Received User.encrypt class method.
# - `self` is an instance of Class.
# - `@name_instance_variable` is nil.
We get nil
for @name_instance_variable
because the class method does not have access to any of the instances we created, and we haven't done anything that would assign a value to it.
Another class method, and using one class method inside another
After we've created the User class, we can also define a new class method like this:
def User.create_remember_token
puts "Received User.create_remember_token class method."
# Now let's use the User.encrypt class method from within this method.
encrypt
# We could also write this, because inside this class method `self` refers
# to the User class itself:
#
# self.encrypt
#
# This is equivalent too--almost (the difference appears when you use
# inheritance, i.e. create a subclass of User, but that's beyond the scope
# of this answer):
#
# User.encrypt
#
end
...that's equivalent (but not necessarily preferable) to:
class User
def self.create_remember_token
# ...
end
end
Now we have a User.create_remember_token
class method, which works like this:
User.create_remember_token
# => Received User.create_remember_token class method.
# Received User.encrypt class method.
# - `self` is an instance of Class.
# - `@name_instance_variable` is nil.
Defining another instance method, and using instance methods inside other instance methods
Suppose we defined another instance method:
class User
# An instance method this time:
def create_remember_token
puts "Received User#create_remember_token instance method.\n"
"- `@name_instance_variable` is #{ @name_instance_variable }."
encrypt
# We could also write this, because inside this instance method `self`
# refers to this instance of User:
#
# self.encrypt
#
end
end
Now we have a User#create_remember_token
instance method, which does this:
my_user = User.new("Alice")
my_user.create_remember_token
# => Received User#create_remember_token instance method.
# - `@name_instance_variable` is "Alice."
# Received User#encrypt instance method.
# - `self` is an instance of User.
# - `@name_instance_variable` is "Alice".
Using class methods within instance methods
Finally, there are two ways to invoke class methods from within instance methods. Suppose we defined our instance method like this instead:
class User
def create_remember_token
puts "Received User#create_remember_token instance method."
"- `@name_instance_variable` is #{ @name_instance_variable }."
# Since `self` refers to this instance of User, `self.class` equals
# the User class itself.
self.class.encrypt
# That's is (almost) equivalent to this:
#
# User.encrypt
#
# ("Almost" again because of the inheritance issue mentioned above.)
#
end
end
Now it will work like this:
User.new("Bob").create_remember_token
# => Received User#create_remember_token instance method.
# - `@name_instance_variable` is "Alice."
# Received User.encrypt class method.
# - `self` is an instance of Class.
# - `@name_instance_variable` is nil.
Note that even though we invoked the class method User.encrypt
inside of the instance method User#encrypt
, the instance variable @name_instance_variable
is still not available inside the class method.
When to use a class method
So why do class methods exist? Because you aren't always working with an instance.
There are good examples of this in Rails. Suppose we have a "Comment" model. Comment#update_attributes
is an instance method because it works with an instance of Comment. It knows about the instance's attributes and its internal state. When you use update_attributes
you know that you're making changes to that instance of Comment.
Comment.find
, however, is a class method. When you use Comment.find
you don't have an instance of Comment (yet). If find
was an instance method we wouldn't be able to use it without first creating an instance:
id = 6
comment = Comment.new.find(id) # => #<Comment id: 6, ...>
...and that doesn't make much sense.
Rails could have done this another way, like defining a CommentFinder class, which makes a little more sense:
comment_finder = CommentFinder.new
comment = comment_finder.find(id) # => #<Comment id: 6, ...>
...and is actually pretty common in some languages (and even in some Ruby libraries; the popular factory pattern does this). But in Rails they've decided to make this simpler by making this functionality a class method of the Comment class, so we can just do this:
comment = Comment.find(id) # => #<Comment id: 6, ...>
Final notes
You might've noticed that I keep referring to instance methods like this:
User#encode
and class methods like this:User.encode
. This is a convention you'll see throughout Ruby and Rails' documentation and books and articles about both. A#
after the class name means it's an instance method and a.
means it's a class method.Ruby is a "message-passing" language. You don't really need to worry about the distinction, but knowing this is handy because of some of the terminology you might run into. When we write
my_user.encode_video
we're technically sending the "message"encode_video
to themy_user
object.my_user
receives the messageencode_video
.Colloquially, we often say "I called the
encode
method" or "this code invokesencode
," and it's fine to use those terms instead (and in truth more developers will probably understand them). You'll probably find code before too long, though, that does something likemy_user.send(:encode_video)
, or eventype = "video"; ... my_user.send("encode_#{type}")
which makes more sense when you understand that "send" means "call" or "invoke."Ruby is a very flexible language. Above in some of the output I wrote, "
self
is an instance of Class". That's not a mistake--in Ruby, even classes are objects, and any class you create will be an instance of the class named Class. There are even ways to define instance methods and instance variables on a class itself (rather than instances of it), but that's way beyond the scope of this answer. If you're dying of curiosity, google "eigenclass."
I hope that's helpful.