Good morning!
Open a terminal window. Type “irb” and hit enter.
Try entering a few things:
1
4 + 3
"hat"
[1, 2, 3, "happy"].each { |i| puts i }
>> is your prompt to enter text and => will appear at the start of each response.
Use the up and down arrow keys to navigate back and forth through the previous commands you’ve entered.
| Numbers | 1, 2, 3 |
| Strings | "chicken" |
| Booleans | true, false |
| Symbols | :chicken |
| Arrays | [1, 7, "cake"] |
| Hashes | {:chicken => "chicken", :sillyexample => :chicken} |
Type these at the command line! What happens?
That’s the data, where are the computations?
2 + 2 |
||
3 * 3 |
||
"chicken".reverse |
||
"chicken".length |
||
2 + 2 == 4 |
||
2 + 2 == 5 |
||
[1 , 2, 3].include?(2) |
2 + 2 |
4 |
|
3 * 3 |
||
"chicken".reverse |
||
"chicken".length |
||
2 + 2 == 4 |
||
2 + 2 == 5 |
||
[1 , 2, 3].include?(2) |
2 + 2 |
4 |
|
3 * 3 |
9 |
|
"chicken".reverse |
||
"chicken".length |
||
2 + 2 == 4 |
||
2 + 2 == 5 |
||
[1 , 2, 3].include?(2) |
2 + 2 |
4 |
|
3 * 3 |
9 |
|
"chicken".reverse |
"nekcihc" |
|
"chicken".length |
||
2 + 2 == 4 |
||
2 + 2 == 5 |
||
[1 , 2, 3].include?(2) |
2 + 2 |
4 |
|
3 * 3 |
9 |
|
"chicken".reverse |
"nekcihc" |
|
"chicken".length |
7 |
|
2 + 2 == 4 |
||
2 + 2 == 5 |
||
[1 , 2, 3].include?(2) |
2 + 2 |
4 |
|
3 * 3 |
9 |
|
"chicken".reverse |
"nekcihc" |
|
"chicken".length |
7 |
|
2 + 2 == 4 |
true |
|
2 + 2 == 5 |
||
[1, 2, 3].include?(2) |
2 + 2 |
4 |
|
3 * 3 |
9 |
|
"chicken".reverse |
"nekcihc" |
|
"chicken".length |
7 |
|
2 + 2 == 4 |
true |
|
2 + 2 == 5 |
false |
|
[1, 2, 3].include?(2) |
2 + 2 |
4 |
|
3 * 3 |
9 |
|
"chicken".reverse |
"nekcihc" |
|
"chicken".length |
7 |
|
2 + 2 == 4 |
true |
|
2 + 2 == 5 |
false |
|
[1, 2, 3].include?(2) |
true |
What do you think "chicken".reverse.length does?
What about "puppy".include?('p')?
var1 = 4
var1
var2 = "chicken"
var2
var2 = var1
var2
Something you might want to do is print out a string that has a value contained within it.
For example, try typing
"Our array is #{[1,2,3]}"
Most useful when dealing with variables!
var = [1,2,3,4,5,6]
"Our array is #{var}"
Now back to variables!
Try typing the following, noting what happens at each step
thing = "chicken"
thing
thing.reverse
thing
thing = thing.reverse
thing.reverse didn’t permanently reverse the string!Try
awesomelist = [5,2,1,8]
awesomelist.sort!
awesomelist
How did that happen? Actions that end with a ! change the data!
This is a Ruby convention, but a good one to follow.
Let’s consider a book rating system:
Fill in a hash with some books, e.g.
books = { "Left Hand of Darkness" => 5,
"The Word for World Is Forest" => 5,
"Nevermind the Pollacks" => 0,
"Only Revolutions" => :not_rated }
We can retrieve the rating for a particular book like so
books["Left Hand of Darkness"]
We can also set values like so
books["Only Revolutions"] = 3
How can we add a rating for a new book? Any guesses?
We can retrieve the rating for a particular book like so
books["Left Hand of Darkness"]
We can also set values like so
books["Only Revolutions"] = 3
How can we add a rating for a new book? Any guesses?
We set the value of the book just like before!
books["White Teeth"] = 4
Now type books to see the whole hash.
40.reverse
What happens?
40.reverse
What happens?
Ruby just reported NoMethodError: undefined method `reverse' for 40:Fixnum
That means that reverse is not something you can do to the number 40.
Most computations in Ruby are performed by methods.
Type 40.methods to see which methods are available for basic numbers.
Notice +, -, et al. in the list?
What about "chicken".methods ?
An object is data along with the methods, computations, that can operate on it.
Everything in Ruby is an object: numbers, strings, hashes, etc.
How do you know what kind of object something is? Ask!
40.class
What is a class?
Let’s learn the syntax by example:
class Counter
end
What can we do with Counter?
c = Counter.new
Let’s learn the syntax by example:
class Counter attr_accessor :valueend
Now try
c = Counter.new
c.value
c.value = 10
c.value
What happened? What does attr_accessor :value do?
Let’s learn the syntax by example:
class Counter attr_accessor :valuedef initialize @value = 0 endend
Now, again, try
c = Counter.new
c.value
initialize gives new instructions on how to create an object
Let’s learn the syntax by example:
class Counter attr_accessor :valuedef initialize @value = 0 enddef increment @value = @value + 1 endend
Let’s use our Counter class:
count = Counter.new
count.increment
count.increment
count.value
count.class
count.methods
Try count.respond_to?("increment"). What did you see?
Let’s add a Counter.increment_by method that takes an argument for how much to increment.
Don’t need to start all over – we open the class instead
class Counterdef increment_by(n) # fill in here endend
Test your code as follows:
c = Counter.new
c.increment_by(5)
c.value
You can add methods to existing classes as well:
class String
def chicken?
self == "chicken"
end
end
"chicken".chicken?
"puppy".chicken?
self is a way of referring to the object that the method is being called on.
In "puppy".chicken?, self is "puppy".
Add a method to String that will test to see if a string is a palindrome.
A palindrome is any string that is the same read forwards or backwards.
To get you started type the following at the command line:
class String
def palindrome?
and finish the rest! Test it on "abba".palindrome? and "puppy".palindrome?
http://rubydoc.info/stdlib/core/
Good places to start: Array, Enumerable, Fixnum, Float, Hash, NilClass, String, Time
Some methods take blocks.
list.each {|p| code} runs code on every element of list
list = [1,2,3,4]
list.each { |n| puts (n + 1) }
{ |arg1, arg2, ..| code }{ |v| v.palindrome? }{ |x, y| x * y }A more complicated example of using each:
reviews = Hash.new(0)
books.values.each { |rate| reviews[rate] = reviews[rate] + 1 }
reviews
reviews is a count of how many reviews you gave with a particular rating
reviews[5]
There’s another way to write blocks. This is commonly used for multi-line expressions.
reviews = Hash.new(0)
books.values.each do |rate|
reviews[rate] = reviews[rate] + 1
# more code can go here...
end
Ruby provides control structures for writing more complicated code.
If statements are a switch on whether the argument is true or false.
if true
1
else
2
end
if false
1
else
2
end
Case statements evaluate a statement and execute the code in the corresponding branch:
case favorite_color
when "green"
puts "Grass is green"
when "blue"
puts "Skies are blue"
when "red"
puts "Tomatoes are red"
else
puts "Are you sure that's a color?"
end
For-in statements allow iteration over a structure such as a list
list = [1,2,3,4,5]
sum = 0
for n in list
sum = sum + n
end
sum
To get started:
bundle installrails serverrails consoleRant and User classesUser class has id and name attributesRant class has id, message, and user_id attributesHow can we look up a rant after it’s been created?
r = Rant.find(1)
r.message
r.user_id
What about modifying a rant?
r.message = "RANTING!"
r.save
We need to save the changes when we’re done editing.
Rails models have built-in finder methods that help you retrieve records from the database.
Try:
Rant.allRant.order("created_at DESC")Rant.firstRant.where("user_id = 1")Rant.where(:user_id => 1)Let’s try creating a new Rant, for user_id 1
user = User.find(1)
rant = user.rants.build
rant.message = "WHAT IS TH"
rant.save
Note that we didn’t need to set the id field of the message!
That was automatically set by new.
We save the record when we’re done, in order to create it, just like when we were editing a rant before.
Creating a rant that way was verbose. Is there a better way?
User.find(1).rants.create(:message => "E POINT!?")
Notice that you don’t need to save when you use create—it combines new and save into a single action.
Open up the user.rb and rant.rb model files.
class Rant < ActiveRecord::Basebelongs_to :uservalidates_presence_of :user, :message validates_length_of :message, :maximum => 10endclass User < ActiveRecord::Basehas_many :rantsvalidates_presence_of :nameend
Some people are writing in lowercase! This won’t do!
Let’s write a method that can convert all lowercase rants to uppercase. First, go find the rant.rb file in your Rails models directory.
class Rant < ActiveRecord::Base
before_save :convert_message_to_uppercase
def convert_message_to_uppercase
...
Rails provides many extensions to Ruby classes to make common tasks easier. Look in http://api.rubyonrails.org/classes/ActiveSupport/Multibyte/Chars.html to find the helper you need for this exercise.
Once you’ve added the code, try creating a lowercase rant and see what happens.
Sometimes you want to work with your data in a different format. Go back to the Rails console and write a program that creates a hash that contains rant messages paired with user names.
How do you create a new Hash again?
Hash.new, but {} is a simpler way.new_hash = {}The result should look like:
=> {"RANTING!"=>"caylee", "WTF"=>"audrey"}
Can you use an iterator to print this out as a series of formatted strings?
The before_save code we wrote earlier uses a callback. Rails defines several callbacks that allow you to change the input to a model before or after actions like save, create, update, and delete.
before_save :geocode_address
after_create :notify_admins
before_validate :check_over_some_other_details
Validations provide an easy wrapper for checking that a particular attribute is present, in the right format, or other requirements before the application will save the data. The microblog application already contains some of these. Look at the Rant and User models to see the ones we’ve included.
Can you think of other validations you’d like to add? Try changing the length of a valid Rant message.
Rails will automatically create views with content when you use the scaffold command. Go to /app/views and have a look around.
These files are making use of a templating system called ERB that converts Ruby statements in the view into static HTML to display. A typical ERB statement might look like:
Documentation on ERB syntax: http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
The scaffolding system is quick to use, but not always pretty. Our list of rants lists the user_id for the person who said it, and not their name. Let’s change that.
Go to /app/views/rants/ and open the index.html file. By default, the scaffolder makes the index view show a list of all records of the related model’s type. Find the line that displays the user id and change it to show the user’s name instead.
On the users index page, let’s add a column to the table to show how many rants each person has written. ActiveRecord makes this easy by providing a method called count that you can add to a collection of records to get the database count instead of the full records. For example, User.count or Rants.count.
These also work through has_many associations, so given a particular user record, you could try user.kittens.count or user.books.count (if those associations existed) or …
How does Rails know what page to go to when you visit http://localhost:3000/rants/new ?
Open /config/routes.rb to see how this works.
Now that we’ve seen and edited models and views, let’s take a quick look at the controller files in our project.
What does a controller do?
microrant directoryrails new betterrantbundle install
rails generate scaffold User name:string
rails generate scaffold Rant user_id:integer message:string
rake db:migrate
At this point you can start the server and take a look around.