7

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.

4
  • 1
    Have you read guides.rubyonrails.org/…? If so, maybe you can ask about things that aren't already covered there. Commented May 11, 2016 at 21:47
  • Please use English punctuation when you write in English. Commented May 11, 2016 at 23:01
  • @sawa i'm sorry for my bad English Commented May 12, 2016 at 4:17
  • @DaveSchweisguth I didn't ask what that code do , I just asked how that codes works or the structure of that code . I know that code for creating table in database and I have knowledge about migration , seeding etc Commented May 12, 2016 at 8:10

3 Answers 3

9

Brace for it :)

  1. 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.

  2. 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 ->.

  3. |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.

  4. 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.

  5. 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 }.

    • Not only parenthesis are optional, but also curly braces are optional when passing hash as last or only parameter to a method.
    • 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
Sign up to request clarification or add additional context in comments.

4 Comments

"null: false, is a shortcut" albeit a shortcut, it's not designed as one, it is simply the newer ruby 2.x syntax for hashes
@Koen. Technically 1.9 and newer support that, so it's been around for a while. It takes some getting used to if you're still rocking the 1.8 style but it's a lot less fuss.
@Koen. not entirely. It is a shortcut syntax for hashes when the key is symbol. You can not make a hash where key is not a symbol (e.g. string, number, object with the "newer" syntax. So it is still a syntactic sugar.
Thanks @quarterdome for your explanation , It's now crystal clear :)
4

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 yielding 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.

2 Comments

You could use another variable name within the last block (for example b) to highlight that it is independent from the yielded variable name.
Thanks @Aetherus this is what i'm looking for :)
3

One of the struggles with learning Ruby is that, like smalltalk et al, you get to pass code around as well as data. One way you can pass code to a method is with a code block. You can then call the code block in the method definition with yield which says "insert this block of code in place of yield":

def do_it
  yield
end

do_it { 2 + 4 }
=> 6 

You also get to send parameters into the code block from the method definition. That's where the |t| comes in:

def do_it_with_ten
  yield 10
end

do_it_with_ten { |t| (2 + 4) * t }
=> 60 

Note that the curly braces are equivalent to do..end.

I'm guessing that this is the code you found with yield in it:

def create_table(name, options = {})
  table_definition = TableDefinition.new(self)
  table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false

  yield table_definition

  if options[:force]
    drop_table(name) rescue nil
  end

  create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
  create_sql << "#{name} ("
  create_sql << table_definition.to_sql
  create_sql << ") #{options[:options]}"
  execute create_sql
end

This is exactly what you're looking for. This is the definition of the create_table method we are calling. You can see the yield with the parameter table_definition.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.