I'm new in Ruby and trying to understand this syntax:
create_table :posts do |t|
t.string :title
t.string :content
t.string :likes
t.string :comments
t.timestamps null: false
end
I fully understand what this code is doing, but I don't understand how it works. More specifically, I understand that create_table
is a method and :posts
is a parameter, but I don't understand the rest of the code.
Brace for it :)
create_table
is a method. :posts
is a symbol that is passed as parameter. Parenthesis are optional, so it looks odd but it is a simple method call.
Everything between do
and end
is a code block. It is one of the many ways how to pass code as an argument to a method. Code blocks are great for common case. Other similar (but different) ways to do it is to use Proc
or lambda
or ->
.
|t|
is an argument passed into the code block by create_table
. create_table
will execute your code block, and will pass a table object as single argument to it. You chose to name that object t
.
Now inside your code block you are calling method string
on object t
and passing it symbol as an argument. You are doing it four times. Again parenthesis are optional.
You are calling timestamps
method on same object t
. Here you are passing it one parameter, which is a Hash with the value { :null => false }
.
null: false
, is a shortcut syntax for { :null => false }
. So all of the above is equivalent to:
create_table(:posts) do |t|
t.string(:title)
t.string(:content)
t.string(:likes)
t.string(:comments)
t.timestamps({:null => false})
end
Let's first forget about Active Record and focus on the code structure itself. Here is a super simple version of that structure.
class MyBuilder
def initialize
# keys are property names, values are options
@properties = {}
end
def property(name, options={})
@properties[name] = options
end
def build
# For simplicity, just return all properties
@properties
end
end
def create_thing(name)
puts "Begin creating #{name}"
builder = MyBuilder.new
puts "Let user use the builder to define properties"
yield builder
puts "Consume the builder"
properties = builder.build
puts "Persist changes to #{name}..."
# For simplicity just print them out
p properties
puts 'done'
end
create_thing :bar do |builder|
builder.property :counter, color: 'brown'
builder.property :wine_storage, texture: 'wood'
end
Please type the code above by hand to grab some feel.
Although the code above has nothing to do with Active Record, it has the same structure as the migration.
When ever create_table
is called, it instantiates a builder (of type TableDefinition
), and "pushes" that builder to the block (by yield
ing it) in order to let user define the tables columns. The builder is consumed later by create_table
when the user is done defining the columns.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With