Now that you have painstakingly read your way through Ruby Basics we can move on to a more exciting bit. In this chapter, we will create our first small Ruby on Rails project.
By default a Rails project offers three different environments:
-
Development
-
Test
-
Production
In this chapter, we are only working with the Development environment. Once you have gained a better feeling for Rails, we will start using tests and then we will need the corresponding environment (where, for example, the Test database is populated when you start a test and then cleared). Later, I will explain the various scenarios to show how you can roll out your Rails application from the Development environment to the Production environment.
The Development environment has everything you need for developing, apart from an editor and a web browser. So you do not need to install a special web server, but can use the integrated Rails web server. It does not exactly have extremely high performance, but you do not need that for developing. Later, you can switch to big web servers like Apache or Nginx. The same applies to the database.
In terms of the database, the main focus in this chapter is once more not on optimum performance, but on showing you a simple and quick way of getting started. That’s why Rails uses the SQLite-3 database. You already have everything you need installed and you don’t need to worry about anything. Later I will explain how you can use other databases (e.g. MySQL).
If you are not a native English speaker, you should try to accept and
even adopt Rails' love for the English language. Much of it will then be
much easier and more logical. Most of the code then reads just like a
normal English sentence. For example, many mechanisms automagically use
plural or singular forms of normal English words. If you get used to
naming database fields and tables with English terms (even if you are
programming in a different language), then you can make use of the whole
power of this magic. This mechanism is referred to as Inflector
or
Inflections.
If you are programming in a language other than English, it still makes sense to use English names for variables, classes and methods. You can write the comments in your own language, but if you take part in international projects, you should obviously write the comments in English as well. Yeah, sure … well written code does not need any comments. ;-)
Let’s first create a new Rails project.
Before we even get going, please check that you are using Rails 5.1:
$ rails -v
Rails 5.1.0
That’s looking good. If you have an older version of Ruby or Rails installed, please install the 5.1 version before you read any further.
Now we start by creating a new Rails project with the name
testproject
. Ruby on Rails is a framework, so we first need to set up
the corresponding directory structure and basic configuration, including
several scripts. Easy as pie, just use the command
rails new testproject
to create everything you need:
$ rails new testproject
create
create README.md
create Rakefile
create config.ru
[...]
Next, we check if the new Rails application is working by launching the integrated web server.
Tip
|
Depending on the operating system (for example, Mac OS X) and on your firewall settings, you may see a popup window when first starting a Rails application, asking you if the firewall should permit the corresponding connection. |
$ rails server
=> Booting Puma
=> Rails 5.1.0 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.8.2 (ruby 2.4.0-p0), codename: Sassy Salamander
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
The start of the Rails application is looking good. It tells us:
=> Rails 5.1.0 application starting in development on http://localhost:3000
So let’s go to the URL http://localhost:3000 in your web browser.
Looks good. Rails works fine.
Important
|
With a the key combination Ctrl+C you can stop the web server.
|
There are certain static pages, images and JavaScript files that are
automatically delivered by Rails. Remember part of the output of the
command rails new testproject
:
[...]
create public
create public/404.html
create public/422.html
create public/500.html
create public/apple-touch-icon-precomposed.png
create public/apple-touch-icon.png
create public/favicon.ico
create public/robots.txt
[...]
The directory name public
and the files it contains already look very
much like static pages. Let’s have a go and create the file
public/hello-world.html
with the following content:
<html>
<head>
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
<p>An example page.</p>
</body>
</html>
Now start the Rails web server with rails server
$ rails server
=> Booting Puma
=> Rails 5.1.0 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.8.2 (ruby 2.4.0-p0), codename: Sassy Salamander
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
We can have a look at this web page at the URL http://localhost:3000/hello-world
No output in the log means: This page was not handled by the Rails framework. It was delivered directly from the webserver.
Note
|
We can of course also use the URL
http://localhost:3000/hello-world.html. But Rails regards
HTML and therefore the file ending .html as standard output
format, so you can omit the .html here.
|
Now you know how you can integrate fully static pages in Rails. This is
useful for pages that never change and that you want to work even if
Rails is not currently working, for example because of an update. In a
production environment, you would usually put a classic web server such
as Apache or Nginx in front
of the Rails server. Which is capable of autonomously delivering static
files from the public
directory.
The content of an erb
file will probably seem familiar to you. It
is a mixture of HTML and Ruby code (erb
stands for *e*mbedded *R*uby).
erb pages are rendered as Views. This is the first time for us to get in
touch with the MVC model. We need a controller to use a view. That can
be created it via the generator rails generate controller
. Let’s have
a look at the onboard help of this generator:
$ rails generate controller
Running via Spring preloader in process 11125
Usage:
rails generate controller NAME [action action] [options]
[...]
Description:
Stubs out a new controller and its views. Pass the controller name, either
CamelCased or under_scored, and a list of views as arguments.
[...]
Example:
`rails generate controller CreditCards open debit credit close`
CreditCards controller with URLs like /credit_cards/debit.
Controller: app/controllers/credit_cards_controller.rb
Test: test/controllers/credit_cards_controller_test.rb
Views: app/views/credit_cards/debit.html.erb [...]
Helper: app/helpers/credit_cards_helper.rb
Nice! We are kindly provided with an example further down:
rails generate controller CreditCard open debit credit close
Doesn’t really fit the bill for our case but I am feeling brave and
suggest that we simply try rails generate controller Example test
$ rails generate controller Example test
Running via Spring preloader in process 11197
create app/controllers/example_controller.rb
route get 'example/test'
invoke erb
create app/views/example
create app/views/example/test.html.erb
invoke test_unit
create test/controllers/example_controller_test.rb
invoke helper
create app/helpers/example_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/example.coffee
invoke scss
create app/assets/stylesheets/example.scss
Phew… that’s a lot of stuff being created. Amongst others, the file
app/views/example/test.html.erb
. Let’s have a closer look at it:
<h1>Example#test</h1>
<p>Find me in app/views/example/test.html.erb</p>
It’s HTML, but for it to be a valid HTML page, something is "missing" at
the top and bottom. The missing part can be found in the file
app/views/layouts/application.html.erb
. We are going to have a look into it
later.
Please launch the web server to test it:
$ rails server
and have a look at the web page in the browser at the URL http://localhost:3000/example/test
In the log file log/development.log
we find the following lines:
Started GET "/example/test" for 127.0.0.1 at 2017-03-22 13:58:41 +0100
Processing by ExampleController#test as HTML
Rendering example/test.html.erb within layouts/application
Rendered example/test.html.erb within layouts/application (0.7ms)
Completed 200 OK in 930ms (Views: 811.4ms)
An HTTP GET request for the URI
/example/test
. That was then apparently rendered as HTML by the
controller ExampleController
using the method test.
Now we just need to find the controller. Good thing you bought this
book. ;-) All controllers are in the directory app/controllers
, and
there you go, we indeed find the corresponding file
app/controllers/example_controller.rb.
$ tree app/controllers
app/controllers
├── application_controller.rb
├── concerns
└── example_controller.rb
Please open the file app/controllers/example_controller.rb
with your
favorite editor:
class ExampleController < ApplicationController
def test
end
end
That is very clear. The controller ExampleController
is a descendant
of the ApplicationController
and contains currently just one method
with the name test
. This method contains currently no program logic.
You will probably ask yourself how Rails knows that for the URL path
/example/test
it should process the controller ExampleController
and
the method test
. This is not determined by some magical logic, but by
a routing configuration. All routings can be listed with the command
rails routes
$ rails routes
Prefix Verb URI Pattern Controller#Action
example_test GET /example/test(.:format) example#test
These routes are configured in the file config/routes.rb
which has
been auto-filled by the controller generator with a route to
example/test
. The line which is important for us is the
second one:
Rails.application.routes.draw do
get 'example/test'
# For details on the DSL available within this file, see
# http://guides.rubyonrails.org/routing.html
end
Alter on we are going to dive more into Routes.
Important
|
A static file in the directory public always has higher
priority than a route in the config/routes.rb ! So if we
were to save a static file public/example/test that file
will be delivered.
|
Erb
pages can contain Ruby code. You can use it to program and give
these pages dynamic content.
Let’s start with something very simple: adding 1 and 1. First we try out
the code in irb
:
$ irb
>> 1 + 1
=> 2
>> exit
That was easy.
Important
|
If you want to output the result of Ruby code in erb,
enclose the code within a <%= … %> .
|
We fill the erb
file app/views/example/test.html.erb
as follows:
<h1>First experiment with erb</h1>
<p>Addition:
<%= 1 + 1 %>
</p>
Then use rails server
to launch the web server.
$ rails server
Visit that page with the URL http://localhost:3000/example/test
You may ask yourself: how can the result of adding two Integer`s be
displayed as a String? Let’s first look up in `irb
if it really is a
Integer
:
$ irb
>> 1.class
=> Integer
>> (1 + 1).class
=> Integer
Yes, both the number 1 and the result of 1 + 1 is an Integer
. What
happened? Rails is intelligent enough to automatically call all
objects in a view (that is the file test.html.erb
) that aren’t
already strings via the method .to_s
, which always converts the
content of the object to a string. Once more, a brief
trip to irb
:
>> (1 + 1).to_s
=> "2"
>> (1 + 1).to_s.class
=> String
>> exit
You are now going to learn the finer points of erb
step by step. Don’t
worry, it’s neither magic nor rocket science.
In a `.html.erb`file, there are two kinds of Ruby code instructions in addition to the HTML elements:
-
<% … %>
Executes the Ruby code it contains, but does not output anything (unless you explicitly use something like
print
orputs
in special ways). -
<%= … %>
Executes the Ruby code it contains and outputs the result as a String. If it’s not a String the method
to_s
will be called.
Important
|
The output of <%= … %>` is automatically escaped.
So you don’t need to worry about "dangerous" HTML.
|
Let’s use an example, to make sure it all makes sense. We use each
to
iterate through the Range (0..5)
. Edit the
app/views/example/test.html.erb
as follows:
<p>Loop from 0 to 5:
<% (0..5).each do |i| %>
<%= "#{i}, " %>
<% end %>
</p>
Open this view in the browser:
Let’s now have a look at the HTML source code in the browser:
<!DOCTYPE html>
<html>
<head>
<title>Testproject</title>
[...]
</head>
<body>
<p>Loop from 0 to 5:
0,
1,
2,
3,
4,
5,
</p>
</body>
</html>
Now you understand how Ruby code is used in the view.
-
I don’t understand anything. I can’t cope with the Ruby code. Could you please explain it again?
Is it possible that you have not completely worked your way through Ruby Basics? Please do take your time with it and have another thorough look. Otherwise, the rest won’t make any sense here.
-
I can understand the Ruby code and the HTML output. But I don’t get why some HTML code was rendered around it if I didn’t even write that HTML code. Where does it come from, and can I influence it?
Excellent question! We will get to that in the next section.
The erb
file in the directory app/views/example/
only forms the core
of the later HTML page. By default, an automatically generated
app/views/layouts/application.html.erb
is always rendered around it.
Let’s have a closer look at it:
<!DOCTYPE html>
<html>
<head>
<title>Testproject</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= yield %>
</body>
</html>
The interesting bit is the line
<%= yield %>
With <%= yield %>
the view file is included here. The lines with the
stylesheets, the JavaScript and the csrf_meta_tags
can stay as they are
for now. We’ll have a look into that in
Asset pipeline. No need to
bother with that right now.
The file app/views/layouts/application.html.erb
enables you to
determine the basic layout for the entire Rails application. If you want
to enter a <hr>
for each page and above it a header text, then you can do
this between the <%= yield %>
and the <body>
tag:
<!DOCTYPE html>
<html>
<head>
<title>Testproject</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<h1>My Header</h1>
<hr>
<%= yield %>
</body>
</html>
You can also create other layouts in the directory app/views/layouts/
and apply these layouts depending on the relevant situation. But let’s
leave it for now. The important thing is that you understand the basic
concept.
One of the cardinal sins in the MVC model is to put too much program
logic into the view. That’s more or less what used to be done frequently
in PHP programming in the past. I’m guilty of having done it myself. But
one of the aims of MVC is that any HTML designer can create a view
without having to worry about the programming. Yeah, yeah, … if only it
was always that easy. But let’s just play it through in our minds: if I
have a value in the controller that I want to display in the view, then
I need a mechanism for this. This is referred to as instance variable
and always starts with a @
. If you are not 100 % sure any more which
variable has which scope, then please have another quick look at
Scope of Variables.
In the following example, we insert an instance variable for the current
time which we get by Time.now
in the controller and then insert it in the
view. We’re taking programming intelligence from the view to the controller.
The controller file app/controllers/example_controller.rb
looks like
this:
class ExampleController < ApplicationController
def test
@current_time = Time.now
end
end
In the view file app/views/example/test.html.erb
we can then access
this instance variable:
<p>
The current time is
<%= @current_time %>
</p>
With the controller and the view, we now have a clear separation of
programming logic and presentation logic. Now we can automatically
adjust the time in the controller in accordance with the user’s time
zone, without the designer of the page having to worry about it. As
always, the method to_s
is automatically applied in the view.
I am well aware that no-one will now jump up from their chair and shout: “Thank you for enlightening me! From now on, I will only program neatly in accordance with MVC.” The above example is just the first small step in the right direction and shows how we can easily get values from the controller to the view with instance variables.
Even with small web projects, there are often elements that appear
repeatedly, for example a footer on the page with contact info or a
menu. Rails gives us the option of encapsulate this HTML code in form of
partials and then integrating it within a view. A partial is also
stored in the directory app/views/example/
. But the file name must
start with an underscore (_
).
As an example, we now add a mini footer to our page in a separate
partial. Copy the following content into the new file
app/views/example/_footer.html.erb
:
<hr>
<p>
Copyright 2009 - <%= Date.today.year %> the Easter Bunny.
</p>
Note
|
Yes, this is not the MVC way of doing it right. Date.today.year
should be defined in the Controller. I’m glad that you
caught this mistake.
|
We edit the file app/views/example/test.html.erb
as follows and insert
the partial via the command render
:
<p>Loop from 0 to 5:
<% (0..5).each do |i| %>
<%= "#{i}, " %>
<% end %>
</p>
<%= render "footer" %>
So now we have the following files in the directory app/views/example
:
$ tree app/views/example/
app/views/example/
├── _footer.html.erb
└── test.html.erb
The new web page now looks like this:
Important
|
The name of a partial in the code is always specified
without the preceding underscore (_ ) and
without the file extension .erb and .html . But
the actual file must have the underscore at the
beginning of the file name and end with the file
extension .erb and .html .
|
Partials can also be integrated from other areas of the subdirectory
app/views
. For example, you can create a directory app/views/shared
for recurring and shared content and create a file _footer.html.erb
in
this directory. You would then integrate this file into the erb
code
via the line
<%= render "shared/footer" %>
Partials are great in the sense of the DRY (*D*on’t *R*epeat *Y*ourself)
concept. But what makes them really useful is the option of passing
variables. Let’s stick with the copyright example. If we want to pass
the start year as a value, we can integrate this by adding the following
in the file app/views/example/_footer.html.erb
:
<hr>
<p>
Copyright <%= start_year %> - <%= Date.today.year %> the Easter Bunny.
</p>
So let’s change the file app/views/example/test.html.erb
as follows:
<p>Loop from 0 to 5:
<% (0..5).each do |i| %>
<%= "#{i}, " %>
<% end %>
</p>
<%= render partial: "footer", locals: {start_year: '2000'} %>
If we now go to the URL http://localhost:3000/example/test, we see the 2000:
Sometimes you need a partial that partially uses a local variable and somewhere else you may need the same partial, but without the local variable. We can take care of this in the partial itself with an if statement:
<hr>
<p>
Copyright
<%= "#{start_year} - " if defined? start_year %>
<%= Date.today.year %>
the Easter Bunny.
</p>
Note
|
defined? can be used to check if an expression has been defined.
|
Now you can call this partial with
<%= render partial: "footer", locals: {start_year: '2000'} %>
and with
<%= render 'footer' %>
.
We have really only barely scratched the surface here. Partials are very powerful tools. You can find the official Ruby on Rails documentation on partials at: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials
The console in Rails is nothing more than an irb
(see
section "irb") built around
the Rails environment. The console is very useful both for developing
and for administration purposes, because the whole Rails environment is
represented and available.
I’ll show you how to work with it in this example application:
$ rails new pingpong
[...]
$ cd pingpong
$ rails generate controller Game ping pong
[...]
$
Start the Rails console with the command rails console
:
$ rails console
Running via Spring preloader in process 18395
Loading development environment (Rails 5.1.0)
irb(main):001:0>
And you can use exit
to get back out:
irb(main):001:0> exit
$
Many readers use this ebook on small mobile devises. For them I try to
keep any code or terminal output width to a minimum. To save the real
estate which is by default occupied by irb(main):001:0>
I use this
configuration file:
IRB.conf[:PROMPT_MODE] = :SIMPLE
In the console, you have access to all variables that are also available later in the proper application:
$ rails console
Running via Spring preloader in process 19371
Loading development environment (Rails 5.1.0)
>> Rails.env
=> "development"
>> Rails.root
=> #<Pathname:/Users/stefan/pingpong>
>> exit
$
In chapter "ActiveRecord" we are going to be working lots with the console and will soon begin to appreciate the debugging possibilities it offers.
Tip
|
One of my best buddies when developing Rails applications is the Tab key. Whenever you are looking for a method for a particular problem, recreate it in the Rails console and then press the Tab key twice to list all available methods. The names of the methods are usually self-exlanatory. |
app
is useful if you want to analyze things to do with routing:
$ rails console
Running via Spring preloader in process 19799
Loading development environment (Rails 5.1.0)
>> app.url_for(controller: 'game', action: 'ping')
=> "http://www.example.com/game/ping"
>> app.get '/game/ping'
Started GET "/game/ping" for 127.0.0.1 at 2017-03-22 17:29:54 +0100
Processing by GameController#ping as HTML
Rendering game/ping.html.erb within layouts/application
Rendered game/ping.html.erb within layouts/application (1.7ms)
/Users/stefan/.rvm/gems/ruby-2.4.0/gems/sass-3.4.23/lib/sass/util.rb:1109: warning: constant ::Fixnum is deprecated
Completed 200 OK in 616ms (Views: 590.1ms)
=> 200
>> exit
We have already used the command rails generate controller
. It starts
the generator with the name controller
. There are other generators as
well. You can use the command rails generate
to display a list of
available generators:
$ rails generate
Running via Spring preloader in process 19901
Usage: rails generate GENERATOR [args] [options]
[...]
Rails:
assets
channel
controller
generator
helper
integration_test
jbuilder
job
mailer
migration
model
resource
scaffold
scaffold_controller
system_test
task
Coffee:
coffee:assets
Js:
js:assets
TestUnit:
test_unit:generator
test_unit:plugin
What does a generator do? A generator makes a programmer’s job easier by doing some of the mindless tasks for you. It creates files and fills them with default code, depending on the parameters passed. You could do the same manually, without the generator. So you do not have to use a generator. It is primarily intended to save you work and avoid potential errors that can easily arise from mindless repetitive tasks.
Tip
|
Someday you might want to create your own generator. Have a look at http://guides.rubyonrails.org/generators.html to find a description of how to do that. |
A helper method takes care of recurring tasks in a view. For example, if
you want to display stars (*) for rating a restaurant and not a number
from 1 to 5, you can define the following helper in the file
app/helpers/application_helper.rb
:
module ApplicationHelper
def render_stars(value)
output = ''
if (1..5).include?(value)
value.times { output += '*'}
end
output
end
end
With this helper, we can then apply the following code in a view:
<p>
<b>Rating:</b> <%= render_stars(5) %>
</p>
You can also try out the helper in the console:
$ rails console
Running via Spring preloader in process 23849
Loading development environment (Rails 5.1.0)
>> helper.render_stars(5)
=> "*****"
>> helper.render_stars(3)
=> "***"
>> exit
There are lots of predefined helpers in Rails and we will use some of
them in the next chapters. But you can also define your own custom
helpers. Any of the helpers from the file
app/helpers/application_helper.rb
can be used in any view. Helpers
that you want to be only available in certain views must be defined for
each controller. When creating a controller, a file for helpers of that
controller is automatically created in app/helpers
. This gives you the
option of defining helpers only for this controller or for the views of
this controller.
All helpers are in the directory app/helpers/
Rails provides a couple of debug tools to make the developer’s live easier.
In any view you can use the debug
helper to render an object with the
YAML format within a <pre>
tag. To display the value of @foo
you can
use the following line in your view:
<%= debug @foo %>
The web-console
gem provides a way to render a rails console views. So
when you browser to a specific URL at the end of that page you’ll get a
console.
Let me show you this by example with this simple rails application:
$ rails new testapp
[...]
$ cd testapp
$ rails generate controller page index
In the app/controllers/page_controller.rb
we add the following code:
class PageController < ApplicationController
def index
@foo = 'bar'
end
end
And in the view app/views/page/index.html.erb
we’ll add the console
command:
<h1>Page#index</h1>
<p>Find me in app/views/page/index.html.erb</p>
<%= console %>
After starting the rails application with rails server
and browsing to
the URL http://localhost:3000/page/index
we get a web console at the
bottom of the page. In it we have access to the instance variable
@foo
.
There are a couple of other build in debugging tools which are out of the scope of this introduction. Please have a look at http://guides.rubyonrails.org/debugging_rails_applications.html to get an overview.
Here you find a couple of words which you’ll often find in the Ruby on Rails universe.
Many Rails programmers are big fans of DRY. DRY means purely and simply that you should try to place repeated programming logic into separate methods.
You often hear the word refactoring in the context of DRY. This involves functioning applications that are further improved. The application in itself remains unchanged in its interface. But its core is optimized, amongst others through DRY.
Convention over configuration (also known as coding by convention, see http://en.wikipedia.org/wiki/Convention_over_configuration) is an important pillar of a Rails application. It states that the programmer does not need to decide in favour of certain features when starting a project and set these via configuration parameters. It specifies an underlying basic consensus and this is set by default. But if you want to work outside of this conventional basic consensus, then you will need to change the corresponding parameters.
You have now already created a simple Rails application and in the next chapter you will dive deeply into the topic ActiveRecord. So now is a good time to very briefly introduce a few terms that often surface in the world of Rails.
According to Wikipedia http://en.wikipedia.org/wiki/Model–view–controller, MVC is a design pattern that separates the representation of information from the user’s interaction with it.
MVC is a structure for software development. It was agreed that it makes sense to have one part of the software in one place and another part of the software in another place. Nothing more, nothing less.
This agreement has the enormous advantage that once you are used to this concept, you know exactly where you can find or need to integrate a certain functionality in a Rails project.
"Model" in this case means data model. By default, Rails applications are an ActiveRecord data model (see chapter "ActiveRecord").
All models can be found in the directory app/models/
.
The "view" is responsible for the presentation of the application. It takes care of rendering the web page, an XML or JSON file. A view could also render a PDF or an ASCII text. It depends entirely on your application.
You will find all the views in the directory app/views/.
Once a web page call has ended up in a route (see chapter "Routes"), it goes from there to the controller. The route specifies a certain method (action) as target. This method can then fulfil the desired tasks (such as finding a specific set of data and saving it in an instance variable) and then renders the desired view.
All controllers can be found in the directory app/controllers/.
There are a handful of abbreviations that make your life as a developer much easier. In the rest of this book, I have always used the full version of these commands, to make it clearer for beginners, but in practice, you will soon find that the abbreviations are easier to use.
-
rails console
Shorthand notation:
rails c
-
rails server
Shorthand notation:
rails s
-
rails generate scaffold
Shorthand notation:
rails g scaffold