views:

351

answers:

10

I'm trying to learn Ruby as well as Ruby on Rails right now. I'm following along with Learning Rails, 1st edition, but I'm having a hard time understanding some of the code.

I generally do work in C, C++, or Java, so Ruby is a pretty big change for me.

I'm currently stumped with the following block of code for a database migrator:

  def self.up
    create_table :entries do |t|
      t.string :name
      t.timestamps
    end
  end

Where is the t variable coming from? What does it actually represent? Is it sort of like the 'i' in a for(i=0;i<5;i++) statement?

Also, where is the :entries being defined at? (entries is the name of my controller, but how does this function know about that?)

+1  A: 

create_table is method that accepts lambda expression (some sort of the delegate), t is argument of the delegate. So when you execute create_table it execute

t.string :name
t.timestamps

something like pseudo code

delegate d = funciton (t) {
   t.string :name
   t.timestamps
}
create_table(d);

Direct analog in java is anonimous classes..

addReturnBackListener(new Listener<EventObject>() {
  public void handle(EventObject e) {
     refreshAndShowAndList();
  }
});

":entries" is not defined at all, it's just identifier. You can treat it as simple string (but without spending memory on keeping chars).

Mike Chaliy
Lambada expression? ;-)
John Topley
yep, need something to fix my mistakes :)
Mike Chaliy
+2  A: 

I'll take the t thing. The create_table method is like a C function that takes a function pointer that takes one argument, the table definition object (forgive my non-existent C skills):

void entries_table_constructor(TableDef table_definition) {
  table_def_add_string_column(table_definition, "name");
  table_def_add_timestamps_column(table_definition);
}    

create_table("entries", entries_table_constructor);

But in Ruby, the definition of the passed function can be done at the moment the create_table method is called. So the bit between the do and end is like the entries_table_constructor function, and the t variable is like the table_definition argument.

There is a big difference between function pointers in C and blocks in Ruby however. In Ruby, all the local variables outside the block are available inside the block:

a = 10
create_table :entries do |t|
  puts a
  ...
end

Check out the yield keyword in Ruby to see how to write your own methods like create_table.

Daniel Lucraft
A: 

That's typical usage of blocks in Ruby. The method create_table is defined in ActiveRecord like:

def create_table(table_name)
  table_object = some_table_setup
  yield(table_object) # your code block which was passed implicitly is invoked here
  create_table(table_object)
end
Hemant Kumar
+9  A: 

:entries is a symbol literal, it's a literal value like 7 or "a string". There's nothing to define (incidentally, the function doesn't know about the name of your controller).

t is the parameter to the block you've passed in to the create_tables method. What you've written here is roughly analogous to something like:

void anonymous_block(Table *t) {
   t->string("name");
   t->timestamps();
}

...

create_table("entries", &anonymous_block);

in C++. create_table calls your block and passes it a parameter, which you've named t. I would suggest you get an introductory book for ruby as opposed to rails. I recommend Ruby For Rails by David A. Black.

Logan Capaldo
+1  A: 

:entries is reference to the entries table in Rails.

The migrator would have known about it when the 'generate controller' command was given as far as I understand (worked with Rails professionally for a year but, still learning).

As for the |t| that is a block. To quote the Pickaxe book (which you should get a copy of either pdf or dead-tree version immediately):

Blocks can be used to define a chunk of code that must be run under some kind of transactional control. For example, you'll often open a file, do something with its contents, and then want to ensure that the file is closed when you finish. Although you can do this using conventional code, there's an argument for making the file responsible for closing itself. We can do this with blocks.

So, what's going on with above is that the |t| is the block which handles setting up the connection to the database, creating the rows according to their specific types, and then closing the connection.

Here's another example:

output_string = "Let's print this to file"

File.open('outputfile.txt','w') do |f| #f for file
  f.print output_string
end

As for your iterator, yes you can do that as well:

an_array = [1,2,3,4]
an_array.each do |line|#line is the block for the elements of the array during iteration
   puts "Now we are at: #{line.to_s}!" 
end
Cuervo's Laugh
Here's a link to the first edition of the "Programming Ruby" book (aka the Pickaxe book)http://www.rubycentral.com/book
Cuervo's Laugh
A: 

The create_table method is what is known as a block in Ruby and t is a variable that is local to that block (t is just a convention in Rails' migrations that stands for "table"). This is another more obvious example of a Ruby block:

10.times do |i|
  print "i is #{i}"
end

:entries is a Ruby symbol, which is a sort of lightweight string that's used for naming things. You could have equally have used "entries". One common use of symbols is for specifying the keys in a hash. In either case, the table being created is named "entries".

John Topley
A: 

entries is the reference to your Entry model - each model assumes that the table name will be the same as its name except tableized (http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/String/Inflections.html#M001653)

the t is a block parameter passed down into the create_table method, see http://www.rubycentral.com/book/tut_containers.html for a better example. In this case t means the table that has been created (http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#M002191)

Hopefully that should be enough to help you out

Omar Qureshi
+1  A: 

I happen to be working on an app that also has an Entry model / entries table. Here's my migration:

class CreateEntries < ActiveRecord::Migration
  def self.up
    create_table :entries do |t|
      t.string :title
      t.text :entry
      # ...
    end
  end

  def self.down
    drop_table :entries
  end
end

Very similar to what you're looking at.

Fist off, first line, declaring a class called CreateEntries which extends ActiveRecord::Migration.

Next, declaring a class method called up(). A class method, as opposed to an instance method belongs to the class rather than to specific objects of the class. It's the "self" keyword that causes it to be a class method.

Next calling create_table() and passing it two things:

  1. A symbol (":entries") which, as mentioned above, is like a String literal. This tells ActiveRecord what the table it creates should be called. Let's say you typed this code by hand -- forget about generators for a minute. You typed ":entries" because you know that by convention tables in a Rails app are named with plural nouns, and you know that the model class that hooks up to this table will be called Entry.

  2. Also passing a block.

A block can be enclosed by...

`do ... end`

or by

`{ ... }`

A block can take parameters enclosed by two "|"s. In this case the create_table method is passing to the block an object of class TableDefinition, so to answer one of your questions, t is the var holding that object. Then inside the block, we're calling various instance methods of TableDefinition.

Where did the TableDefinition object come from? That happens in the create_table() method. It contains the code that instantiates a new TableDefinition object and "yields" it to the block....

ActiveRecord source code...

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

  yield table_definition

  # ...

end
Ethan
A: 

:entries is being defined right there. The code is calling the method create_table with two arguments - the desired name of the table, and a block of code.

create_table will construct a TableDefinition object and then yield to the block of code, supplying that object to it. Within the block of code it will be named t. And finally that block of code calls some methods on t to build columns.

A: 

Since, coming from Python, I struggled to understand that construction myself, I will give the unpythonic translation of your code snippet.

First, you would have to define a function create_table as this one (it is just a skeleton):

def create_table(name, setup):
    t = Table(name)
    setup(t)

Then, for each table, you would create a setup function as:

def setup_entries(t): # <-- here is your |t|, just a function argument
    t.string("name")
    t.timestamps()

And finally you would create the table by calling:

create_table("entries", setup_entries)

This is not the way it would be done with Python. If you are interested in how to create table in Python you should look at how django, or sqlalchemy handle that.

Olivier