Reputation: 58637
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?)
Upvotes: 6
Views: 501
Reputation: 60099
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.
First 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:
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
.
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
Upvotes: 1
Reputation: 49126
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.
Upvotes: 0
Reputation: 807
: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.
Upvotes: 0
Reputation: 115312
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".
Upvotes: 1
Reputation: 26648
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).
Upvotes: 1
Reputation: 2008
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
Upvotes: 0
Reputation: 9093
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
Upvotes: 0
Reputation: 206
: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
Upvotes: 1
Reputation: 40336
: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.
Upvotes: 9
Reputation: 7356
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
.
Upvotes: 2