If you are a complete beginner to Rails, you will surely have been fascinated by the beauty of Rails that how it creates relations for you, how it creates a table in the database by just writing a migration for it, and similarly other things that do a lot for you but take only a few lines of code. Well, fascination does come at a price, and you will notice that there is code that doesn’t make any sense for a beginner. So, in this article, we shall try to break down that code, and explain it with an understanding of Ruby.
First, how does code for validation work in Ruby:
class User < ActiveRecord::Base validates :email, presence: true end
So you may be wondering how does it work. Well, in Ruby as well as in Rails, there is a keyword that always exists no matter where you are in your code, and that is: self . So if you are just in a class, and you write puts self , it is gonna print the value/name of the class. But as you get into an instance method, self immediately starts reflecting the current instance, so if you do puts self , it is gonna print the value of the instance.
class User puts "The value of self: #{self}" end
If you load this class by requiring it in IRB console, you will see the following output.
The value of self: User
But if you print the value of self in an instance method, you will get the following output:
class User def full_name puts "The value of self: #{self}" end end User.new.full_name
The value of self: #<User:0x007fdcb2a14f50>
So here is the summary: If you call a method inside the class definition and do not use any prefix
with it, it will be called on the self keyword.
So that makes writing validates :email, presence: true equivalent to self.validates :email, presence: true , and sure enough, you can explicitly write the keyword self , and your code will work with no errors:
class User < ActiveRecord::Base self.validates :email, presence: true end
Now, this makes sense. validates is simply a class method that we invoke, and pass different arguments in it.
Moving on to how does the code for migration work in Rails generically, let’s have a look at it:
class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :first_name t.string :last_name t.integer :age end end end
Now, since create_table is being written in change that is an instance method, so we can surely write self.create_table instead of create_table , but since self refers to the current instance, so leaving off the keyword self makes more sense.
Following is how create_table has been defined in Rails:
def create_table(table_name, options = {}) td = create_table_definition table_name, options[:temporary], options[:op tions], options[:as] if options[:id] != false && !options[:as] pk = options.fetch(:primary_key) do Base.get_primary_key table_name.to_s.singularize end td.primary_key pk, options.fetch(:id, :primary_key), options end yield td if block_given? if options[:force] && table_exists?(table_name) drop_table(table_name, options) end result = execute schema_creation.accept td td.indexes.each_pair { |c, o| add_index(table_name, c, o) } unless suppor ts_indexes_in_create? result end
You can also notice this thing that the first argument that it takes is table_name , and then you can pass different options like id: false that creates a table without id.
yield td if block_given? written in the definition of create_table tells us that it takes a block, and sure enough, it does.
So now, you can understand that how writing a line has_many :books works in Rails. Having this kind of core knowledge of Ruby surely helps you be more confident in writing and debugging Rails code. If you would like to have further info on how does ActiveRecord do all the magic, you can head over to the book: Metaprogramming Ruby 2: Program Like the Ruby Pros.
If there is something else you would like us to explore, let us know in comments section below, and never forget to have fun while learning!