An array is a way of storing a list of objects. Unlike in some other languages in ruby you don’t need to specify which sort of object the array will hold - it can even hold objects of different types.
Arrays are written with square brackets.
my_array = [1,2,3]
empty_array = [] # an array with no elements
Working with a partner, guess what each of the following expressions will give, then try them out in irb
:
a = [3, 39, 1, 0, 5]
a[1]
a.first
a[0]
a[5]
a[-1]
a[-5]
a.last
a[1..3]
a[1..-1]
b = []
a.empty?
b.empty?
a.length
b.length
a.include?(39)
a.include?(49)
a.reverse
a # has this changed a?
a.reverse!
a # has this changed a?
a.sort
a # has this changed a?
a.sort!
a # has this changed a?
a = [1, 2, 3, 4, 5]
a[3] = 6
a # What is a now?
a[7] = 7
a # What else has been added?
a << 9 # This one's important - you'll see it a lot
Here are some things you learnt in the last exercise:
a[0]
a[-1]
nil
(instead of an error in other languages)a[1..-1]
is everything but the first elementArrays have many useful (and largely self-explanatory) methods, including:
empty?
- does the array have any elements, or is it the empty array []
length
include?(x)
- is x
in the arrayreverse
, reverse!
- as with strings the !
permanenty changes the arrayYou can add to an array by setting a given element explicitly a[5] = 6
If the array isn’t long enough the gaps are filled with nil
:
a = [1, 2]
a[6] = 5
a #=> [1, 2, nil, nil, nil, nil, 5]
You can also add to the end of an array by doing a << 5
. This is very ‘rubyish’ and you’ll see it a lot.
Most programming language have some way of looping, or iterating, over a range of values.
Ruby’s approach to this is slightly unusual - instead of you taking elements from the array one-by-one, the ruby array gives the elements to you one-by-one. This might seem like a small distinction to make, but it has a significant effect on the feel of the ruby language and how you think about coding with it.
The way you get an array to give you its elements is by calling its each
method:
array = [1, 2, 3, 4, 5]
sum = 0
array.each do |n|
sum = sum + n
end
sum #=> 15
The each
method accepts a block - in the example above, this is everything between do ... end
.
each
method sends the elements of the array one-by-one to the block.n
.In the example above, each element is added to the sum
. The example shows one method that you can use to sum the values in an array.
Below is another example, which iterates over the elements of an array, separating them into one of two lists:
all_words = ['hello', 'how', 'are', 'you', 'today']
long_words = []
short_words = []
all_words.each do |word|
if word.length >= 5
long_words << word
else
short_words << word
end
end
long_words #=> ['hello', 'today']
short_words #=> ['how', 'are', 'you']
Please note: the summing and categorising examples above can be made much neater with the use of the array methods inject
and select
. We'll leave that for another day though
Take another look through the code examples above. Check you really understand how they work. Try them out in irb
, to check they work as advertised!
So far we have only inserted single values and words into an already existing erb
template. We will now look at using ruby to generate more of the template dynamically.
Suppose you have a ruby array that you want to turn into an html list:
# in app.rb
get '/fruit' do
@fruits = ["Bananas", "Apples", "Raspberries"]
erb :fruits_view
end
We can do this by using the @fruits
array’s each
method to iterate over the elements and put them into the page one-by-one.
<!-- in views/fruits_view.erb -->
<ul>
<% @fruits.each do |fruit| %>
<li><%= fruit %></li>
<% end %>
</ul>
You might notice that we’ve used a different type of erb tag for the do ... end
block: <% %>
instead of <%= %>
. This is really important:
<%= %>
if you want the result of executing that line of ruby to be added to the page.<% %>
if you don’t want the result of executing that line of ruby to be added to the page.Here, the bits we want adding are the elements of the array, so only they have the <%= %>
tags. The code will cause the following html to be generated:
<ul>
<li>Bananas</li>
<li>Apples</li>
<li>Raspberries</li>
</ul>
You can also use the same technique to generate tables and other html (lists of divs etc.).
todo.erb
, so that the @todos
display as an ordered list.schedule.erb
, so that the items in the @schedule
display as rows in a table. Note that @schedule
is an array of arrays - you'll need to iterate over the first array, and within the loop use the normal ways of accessing elements of an array pull out the items.get '/rsvps'
block. Try the CSV.read
bit in irb, to see what it does. Have a go at writing the code to categorise rsvps into acceptances/rejections and count them.rsvps.erb
view, so that the right information is displayed.Last week we linked Twitter Bootstrap into your view templates, by linking to the online hosted version <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css">
. What if we want to link to our own files?
When we were building static sites, this was simple: we just created a main.css
file in our site folder, and linked to it. The problem is that a sinatra site folder is not like a static site folder. We’ve already seen this: app.rb
controls the url structure, not the folders in the app folder. (Just try going to ‘/app.rb’ or ‘/views/index.erb’). Because of this, just dropping a main.css
into the sinatra folder and trying to link to it won’t work.
Thankfully sinatra has a solution to this problem: if you create a folder called public
, any files you put in there will be available from the root url. For example, if I have a file public/main.css
, I can then link to this from my template files using <link rel="stylesheet" href="/main.css">
. The two important things to note here are:
/
). If you don’t do this, you’ll have problems if you ever call that template from a different url route.main.css
, not to public/main.css
.public
folder will be served at the root of the site. This is where you should put your own stylesheets/images etc.erb :template_name
.template_name.erb
in the views
folder.@
) will be shared with the template.<%= @my_variable >
public/dist/css/bootstrap.css
into the head
of todo.erb
and schedule.erb
.One of the most important principles in software engineering is to stay DRY: don’t repeat yourself.
The basic idea is that you don’t want to be copying and pasting the same code into multiple places. If you’re doing this, you’re making a lot of work for yourself should you ever decide to change that code.
One way of staying DRY that we’ll meet soon is writing functions: if there’s a task that you do repeatedly, write a function to put the logic for that task in one place and then call the function when you need to do the task.
Another way, that we’ll be looking at today, is in the form of layouts.
In the exercise today, you’ve been using a lot of erb view templates. You have 4 different templates, which all share the same basic outline:
<!-- in views/index.erb -->
<!DOCTYPE html>
<html>
<head>
<title>Event Manager 2.0</title>
</head>
<body>
<h1>Picture Unveiling Evening - Event Managment</h1>
<ul>
<li><a href='/todos'>Todo list</a></li>
<li><a href='/schedule'>Event schedule</a></li>
<li><a href='/rsvps'>RSVPs</a></li>
</ul>
</body>
</html>
<!-- in views/todo.erb -->
<!DOCTYPE html>
<html>
<head>
<title>Event Manager 2.0</title>
</head>
<body>
<h1>Todo list</h1>
<ul>
<% @todos.each do |todo| %>
<li><%= todo %></li>
<% end %>
</ul>
</body>
</html>
These files are almost the same - a lot of copy and paste has gone on.
Sinatra gets round this problem by allowing you to have a layout.erb
file in your views
folder:
<!-- views/layout.erb -->
<!DOCTYPE html>
<html>
<head>
<title>Event Manager 2.0</title>
</head>
<body>
<%= yield %>
</body>
</html>
You can then write only the bits that change in the other two views:
<!-- in views/index.erb -->
<h1>Picture Unveiling Evening - Event Managment</h1>
<ul>
<li><a href='/todos'>Todo list</a></li>
<li><a href='/schedule'>Event schedule</a></li>
<li><a href='/rsvps'>RSVPs</a></li>
</ul>
<!-- in views/todo.erb -->
<h1>Todo list</h1>
<ul>
<% @todos.each do |todo| %>
<li><%= todo %></li>
<% end %>
</ul>
Sinatra knows that if you have a file called layout.erb
it should use that as a layout. If you call erb :index
:
layout.erb
file.<%= yield %>
.index.erb
at that point.You can find more about Sinatra layouts on the internet.
views/layout.erb
Sinatra will treat it as a layout file: whenever it renders another template, it will try and insert the output into the <%= yield %>
part of the layout file.public
folder will be served at the root of the site. This is where you should put your own stylesheets/images etc.erb :template_name
.template_name.erb
in the views
folder.@
) will be shared with the template.<%= @my_variable %>
Refactor your views: