Rails

Learning Rails was very very difficult for me. It required me to learn lot of concepts like Model View Controller (MVC) and other things. I failed many times before I could learn enough to call myself a Rails developer.

I would compare Rails with a rail way system. You need a lot to get it running, one must lay tracks, power lines, the infrastructure and so on, but once done, you have got a very efficient system of transport at your disposal.

Programming using PHP and other languages is like walking, you can start walking right now, but imagine how long it would take to walk from Chennai to Delhi? Would you prefer to walk or take a train?

You need to invest time and energy to learn Rails, but once done, you will have the power to create web apps @ lightning speed.

Where Rails is Effective?

Though you can build a simple 5 page website with Rails, Rails is not good for for that. Rails is good for building medium to large web projects that relies on Data Base (RDMS) to get its data, and to get user input and store it in Data Base.

Usually you would see use of Rails in apps which need be got to market fast, as Rails has got lot of batteries included. It has got libraries for data manipulation, like defining tables, adding modifying and removing columns from them. It has got way to wrap Ruby classes around tables, specifying relationship between those classes. It has got components to specify how your HTML page should be built and rendered, and it has got ways to manage your assets like images, Javascript’s and CSS etc. I would have left one or two here and there. In all Rails is a huge library that helps you to build web pages very fast.

Rails today is used for building SAAS app, and/or an app that provides JSON backend. It’s usually used by small teams to build products that compete with big companies that employ tens of people.

Apart from building apps, Rails has also got ways to test the web app you are building. by writing automated tests, you would minimize the errors you will create in the future. you can refractor code with confidence and hence maintain the app top notch.

There are services like Heroku that would help you to deploy Rails app quickly thus reducing your IT cost and time to market.

In short if you want to build a fairly complex to advanced RDBMS driven web app, Rails is the right choice for you.

Getting this book

One can get this book here https://rails-7.gitlab.io

Once can get code for this book here https://gitlab.com/rails-7/code

Hardware Requirements

Almost all web apps today are deployed on GNU/Linux machines for stability. So its better to learn Rails on a GNU/Linux system. I have deployed all my Rails app on Ubuntu, so its better to get a Ubuntu laptop to learn rails. Make sure it has got 8GB+ of RAM.

Getting Help

One of the strong features of popular free software language is that one would find plenty of help. One of the advantage of Ruby programming language is that many people like to code with it, and many people would like to offer help to people who code with it. In this section you will find places where you can seek help.

Ruby Mailing List

One of the best places to see help for Ruby programming language is the Ruby mailing list. Visit https://www.ruby-lang.org/en/community/mailing-lists/, scroll down to see a form like this:

getting help 4a123

Fill in your email and submit it to join the mailing list.

Rails Forum

Ruby on Rails has a discussion forum, one can find it here https://discuss.rubyonrails.org/, register and try to answer people who have doubts, and ask questions about Rails here.

Stack Overflow

If you are a programmer, you would have heard about stack overflow https://stackoerflow.com, you can ask your doubts there too. Personally I would prefer Ruby mailing list and Ruby on Rails forum.

Reddit

Reddit has a passionate Ruby community https://www.reddit.com/r/ruby/ and Rails community https://www.reddit.com/r/rails/, you can discuss and ask doubts here too.

Ruby & Rails API

You might get doubt, or find a way to do something short using Ruby or Rails, both Ruby language and Rails framework has got a rich set of API. One can find Ruby API here https://ruby-doc.org/ and Rails api here https://api.rubyonrails.org/. These links will be useful when you are well acquainted with Ruby and Rails.

Prerequisite

I don’t think one can start with Rails just like that. In fact I would say that one already has to know web development to get comfortable with Rails. This section explains what you need to know to get started with Rails.

Writing Code

One should know to write code if you want to start to learn Rails. Don’t think that by not even knowing how to write code, and not having prior experience of writing code, you can somehow get started with Rails. It does not work.

GNU/Linux

You must have knowledge of GNU/Linux, have a good GNU/Linux system, and try to learn about it. You can learn GNU/Linux from this excellent website https://linuxjourney.com

HTML & CSS

You need to know how to create web pages to start learning Rails. You need to know about HTML and CSS, one can learn about HTML here https://www.w3schools.com/html/ and CSS here https://www.w3schools.com/CSS/

JavaScript

JavaScript is the only practical language that works on browser now. There is another thing called web assembly, but for today it’s just a curiosity. You can learn JavaScript here https://www.w3schools.com/js/, or get this book JavaScript: The Definitive Guide here https://www.amazon.in/dp/B088P9Q6BB/

SQL

Rails is used to create webpages with Database (DB) backend. For that you need to know SQL, you may learn it here https://www.w3schools.com/sql/

I would also one to install PostgreSQL https://www.w3schools.com/sql/ on ones system and practice it.

Ruby

Finally, Ruby programming language is the hero of Ruby on Rails. Rails is built using this language. I would recommend my book I Love Ruby https://i-love-ruby.gitlab.io/ to learn it.

git

Today almost no one codes in isolation, you need to work with some one, you may make a big mistake in your code which need to be reversed. git is tool initially created by Linus Tovarlds, the same guy who created GNU/Linux, and its the version control tool for programmers. You can learn git here https://www.tutorialspoint.com/git/index.htm

Installing Rails

First check if you have Ruby installed. In your terminal check it using ruby -v as shown.

$ ruby -v
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin21]

Make sure that you have installed Ruby using rvm https://rvm.io/ or other similar version manager.

Now let’s install Rails. If you are familiar with Ruby, they you would have heard about Ruby Gems https://rubygems.org/, which are nothing but distributable Ruby libraries zipped into a file. It’s centrally available in https://rubygems.org/ for the world to use. You can get the rails gem by typing gem install rails in terminal.

this would generate lot of output as shown below:

$ gem install rails
Fetching thor-1.2.1.gem
Fetching method_source-1.0.0.gem
Fetching tzinfo-2.0.4.gem
Fetching crass-1.0.6.gem
Fetching zeitwerk-2.5.3.gem
Fetching activesupport-7.0.1.gem
Fetching nokogiri-1.13.0-x86_64-darwin.gem
Fetching loofah-2.13.0.gem
Fetching rails-dom-testing-2.0

.... and lot's of things

Installing ri documentation for actionmailbox-7.0.1
Parsing documentation for websocket-extensions-0.1.5
Installing ri documentation for websocket-extensions-0.1.5
Parsing documentation for websocket-driver-0.7.5
Installing ri documentation for websocket-driver-0.7.5
Parsing documentation for nio4r-2.5.8
Installing ri documentation for nio4r-2.5.8
Parsing documentation for actioncable-7.0.1
Installing ri documentation for actioncable-7.0.1
Parsing documentation for rails-7.0.1
Installing ri documentation for rails-7.0.1
Done installing documentation for zeitwerk, thor, method_source, tzinfo, activesupport, nokogiri, crass, loofah, rails-html-sanitizer, rails-dom-testing, rack, rack-test, erubi, builder, actionview, actionpack, railties, mini_mime, marcel, activemodel, activerecord, globalid, activejob, activestorage, actiontext, mail, actionmailer, actionmailbox, websocket-extensions, websocket-driver, nio4r, actioncable, rails after 37 seconds
33 gems installed

Don’t bother much about them, let’s check if Rails works. First check the rails version by typing rails -v in terminal. I got this output

$ rails -v
Rails 7.0.1

So I have got a Rails version of 7 installed on my system.

For that let’s create a dummy app. Type rails new checking_rails in your terminal:

$ rails new checking_rails
      create
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
      create  Gemfile
         run  git init from "."


      create  vendor/javascript
       create  vendor/javascript/.keep
    Ensure JavaScript files are in the Sprocket manifest
       append  app/assets/config/manifest.js
    Configure importmap paths in config/importmap.rb
       create  config/importmap.rb
    Copying binstub
       create  bin/importmap
        rails  turbo:install stimulus:install
    Import Turbo
       append  app/javascript/application.js
    Pin Turbo
       append  config/importmap.rb
    Run turbo:install:redis to switch on Redis and use it in development for turbo streams
    Create controllers directory
       create  app/javascript/controllers
       create  app/javascript/controllers/index.js
       create  app/javascript/controllers/application.js
       create  app/javascript/controllers/hello_controller.js
    Import Stimulus controllers
       append  app/javascript/application.js
    Pin Stimulus
       append  config/importmap.rb

Once again it will throw a lot of stuff, don’t bother about it. When its over you would see a folder named checking_rails, cd into it and type rails server or rails s.

$ cd checking_rails
$ rails s

Once done you would see some output like this

$ rails s
=> Booting Puma
=> Rails 7.0.1 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.5.2 (ruby 3.1.0-p0) ("Zawgyi")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 10515
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000

If you read it, you might have noticed that Rails is running and is listening in port 3000. Open up your browser and type https://localhost:3000 in browser, press enter and you should get something like this:

book 691c9

If you see your terminal you will see something like this one:

$ rails s
=> Booting Puma
=> Rails 7.0.1 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.5.2 (ruby 3.1.0-p0) ("Zawgyi")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 10515
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop
Started GET "/" for 127.0.0.1 at 2022-01-08 10:50:41 +0530
   (2.4ms)  SELECT sqlite_version(*)
Processing by Rails::WelcomeController#index as HTML
  Rendering /Users/mindaslab/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/railties-7.0.1/lib/rails/templates/rails/welcome/index.html.erb
  Rendered /Users/mindaslab/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/railties-7.0.1/lib/rails/templates/rails/welcome/index.html.erb (Duration: 2.0ms | Allocations: 882)
Completed 200 OK in 17ms (Views: 7.0ms | ActiveRecord: 0.0ms | Allocations: 7970)

Notice these logs in the terminal:

Started GET "/" for 127.0.0.1 at 2022-01-08 10:50:41 +0530
   (2.4ms)  SELECT sqlite_version(*)
Processing by Rails::WelcomeController#index as HTML
  Rendering /Users/mindaslab/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/railties-7.0.1/lib/rails/templates/rails/welcome/index.html.erb
  Rendered /Users/mindaslab/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/railties-7.0.1/lib/rails/templates/rails/welcome/index.html.erb (Duration: 2.0ms | Allocations: 882)
Completed 200 OK in 17ms (Views: 7.0ms | ActiveRecord: 0.0ms | Allocations: 7970)

It tells that you have visited the home page:

Started GET "/" for 127.0.0.1 at 2022-01-08 10:50:41 +0530

It gives a hint that Rails use sqlite DB https://sqlite.org/index.html out of the box:

(2.4ms)  SELECT sqlite_version(*)

And finally it says that your request has been completed:

Completed 200 OK in 17ms (Views: 7.0ms | ActiveRecord: 0.0ms | Allocations: 7970)

It also says it took 17ms to complete this request. If other things are not understandable, never bother, you will get it.

To terminate rails press ctrl+c in your keyboard and it will end. If all went well, you have successfully installed Rails and verified it. During exit you might get a message as shown:

^C- Gracefully stopping, waiting for requests to finish
=== puma shutdown: 2022-01-08 10:54:57 +0530 ===
- Goodbye!
Exiting

Which says Rails has exited well.

Your Rails Development Environment

Hello Rails

In this section let’s relax and build our first Rails app, let’s name it hello_rails, let’s first start by creating a new Rails app in our terminal.

Creating a new Rails app

step_0

$ rails new hello_rails

When you execute the above command, you will see a lot of output generated as shown below, never mind them.

      create
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
      create  Gemfile
         run  git init from "."
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: 	git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: 	git branch -m <name>
Initialized empty Git repository in /Users/mindaslab/code/rails_7/hello_rails/.git/
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb

....


create  config/importmap.rb
Copying binstub
create  bin/importmap
 rails  turbo:install stimulus:install
Import Turbo
append  app/javascript/application.js
Pin Turbo
append  config/importmap.rb
Run turbo:install:redis to switch on Redis and use it in development for turbo streams
Create controllers directory
create  app/javascript/controllers
create  app/javascript/controllers/index.js
create  app/javascript/controllers/application.js
create  app/javascript/controllers/hello_controller.js
Import Stimulus controllers
append  app/javascript/application.js
Pin Stimulus
append  config/importmap.rb

Once that’s done, cd into hello_rails you can run rails s or rails server in the terminal and open https://localhost:3000 in terminal and you will get the Rails first screen as shown below.

book 691c9

Press Ctrl+C to exit Rails in terminal.

Static pages

Let’s create something called static pages, that is these are web pages who’s content does not change with time. for that type this in the terminal.

$ rails g controller welcome hello wish

You should be getting an output like the one shown below.

      create  app/controllers/welcome_controller.rb
       route  get 'welcome/hello'
              get 'welcome/wish'
      invoke  erb
      create    app/views/welcome
      create    app/views/welcome/hello.html.erb
      create    app/views/welcome/wish.html.erb
      invoke  test_unit
      create    test/controllers/welcome_controller_test.rb
      invoke  helper
      create    app/helpers/welcome_helper.rb
      invoke    test_unit

Now start the rails server by punching rails s in terminal, and visit http://localhost:3000/welcome/hello and you will be getting a screen like this one.

hello rails 94679

When you visit the url http://localhost:3000/welcome/wish, you will be getting a display as shown below in the browser.

hello rails a19e4

What happened?

To an external observer this might look magical, it looks as though some request has been sent to the Rails (red box shown below), and a response had been got, but how does that happen?

rails request tesponse

So, if you see in routes.rb, you will see a code like this:

routes.rb
Rails.application.routes.draw do
  get 'welcome/hello'
  get 'welcome/wish'
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  # root "articles#index"
end

So when you hit this url http://localhost:3000/welcome/hello, notice that welcome/hello in the url, and it’s been defined in routes.rb, this tells rails to accept this route.

If you can split this route, you would get 'welcome`, a / and hello, now take a look at welcome_controller.rb

welcome_controller.rb
class WelcomeController < ApplicationController
  def hello
  end

  def wish
  end
end

The WelcomeController is a Ruby class that inherits from ApplicationController. ApplicationController does lot of stuff that is beyond the scope of this book, so let’s for get about it. But in your url, you did have hello in it. If you see the code in above welcome_controller.rb, you have a method called hello: which is defined as follows:

def hello
end

So one could infer get 'welcome/hello' allows a URL like http://localhost:3000/welcome/hello to be received by Rails. The welcome in URL gets mapped to WelcomeController which resides in a file app/controllers/welcome_controller.rb, and hello in the URL get’s mapped function WelcomeController#hello.

Now checkout the file hello.html.erb, you can see its in app/views/welcome/hello.html.erb an it reads as follows

app/views/welcome/hello.html.erb
<h1>Welcome#hello</h1>
<p>Find me in app/views/welcome/hello.html.erb</p>

It’s this content that get’s displayed when you type http://localhost:3000/welcome/hello in the browser.

So this is what happens, first your request hits the route, the route sees if the URL cold be allowed in, then it routes to the right controller and action as shown below.

route controller view

The the URL contains welcome/hello, by convention Rails knows that there should be a controller named WelcomeController should be defined in a file in app/controllers/welcome_controller.rb and that file must have an action WelcomeController#hello. By convention Rails knows that view for the file should be present in app/views/hello.html.erb, so the controller pulls the view of the action and renders it out as response and you see a display like this

hello rails 94679

in your browser.

Changing views, linking pages

step_2

Now let’s change our view files and link then so that we have some form of navigation. Open app app/views/welcome/hello.html.erb, and change it as shown:

hello.html.erb
<h1>
  Hello from Rails!
</h1>

<p>
  Hello and welcome to Rails.
</p>

<p>
  <%= link_to "Wish Me", {action: :wish} %>
</p>

Notice the <%= link_to "Wish Me", {action: :wish} %>, the file name is hello.html.erb, here the .html says it will be rendered as HTML, but the .erb means Embedded Ruby. So we can put a valid Ruby between <%= and %>, and it will be embedded in the web page.

We put the following thing link_to "Wish Me", {action: :wish} in it, this is nothing but link_to method that’s defined in the rails gem, you can get its documentation here https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to.

The first argument we pass to link to the text that must appear in the web page here its "Wish Me", the second argument should be the URL, so here we tell it to navigate to action wish by passing this hash {action: :wish} , we embed the code in HTML using this ERB syntax: <%= link_to "Wish Me", {action: :wish} %>. This generates a web page as shown:

hello rails fb2e7

Similarly in app/views/welcome/wish.html.erb, let’s put the following content into it:

wish.html.erb
<h1>
  Have a nice day!
</h1>

<p>
  <%= link_to "Go Back", {action: :hello} %>
</p>

Here <%= link_to "Go Back", {action: :hello} %> generates a hyperlink with text Go Bak, and it will be linked to action hello.

Fire up your rails server in terminal by typing rails s, when you hit the URL http://localhost:3000/welcome/hello, you will see a page as shown:

hello rails fb2e7

When you click Wish Me, you will be taken to a page that displays the content in wish me as shown below:

hello rails 982a6

You could click Go Back to go to your hello page once again.

Dynamic pages

step_3

In the last section we have seen how to add static pages to Rails applications. Static pages are web pages that do not change with time, but what if we want to show pages who content is dynamic? We will see that here.

To do that open routes.rb and add this get 'welcome/time' as shown below:

routes.rb
Rails.application.routes.draw do
  get 'welcome/hello'
  get 'welcome/wish'
  get 'welcome/time'
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  # root "articles#index"
end

We tell Rails to respond to another route which is http://localhost:3000/welcome/time, when user hits that URL from his browser we will show him tthe time.

Now let’s add an action called time in WelcomeController as shown.

welcome_controller.rb
class WelcomeController < ApplicationController
  def hello
  end

  def wish
  end

  def time
  end
end

Once added app/controllers/welcome_controller.rb should look as shown above.

Now create a file called app/views/welcome/time.html.erb as shown below

time.html.erb
<h1>
  Time
</h1>

<p>
  The time now is <%= Time.now %>
</p>

Note these lines:

<p>
  The time now is <%= Time.now %>
</p>

In the above piece of code note <%= Time.now %>, we are getting an instance of Time using Time.now and embedding into the web page by placing it between <%= and %>.

Now visit http://localhost:3000/welcome/time, you should be getting output similar to the image shown below:

hello rails 64e9a

Refresh the page, the time should have changed. We created a dynamic page using Rails.

Moving code to action

step_4

It’s okay to have some code in view, but views should be more of pure views as much as possible, it’s better to move any code in view to corresponding action in the controller. So delete <%= Time.now %> in app/views/welcome/time.html.erb and replace it with <%= @time %>

time.html.erb
<h1>
  Time
</h1>

<p>
  The time now is <%= @time %>
</p>

As you see @time is an instance variable, and where do we define it? We define it in action time in WelcomeController as shown below:

class WelcomeController < ApplicationController
  def hello
  end

  def wish
  end

  def time
    @time = Time.now
  end
end

Once done, refresh the page, the functionality would be the same.

hello rails 33c05

But do remember that you have made the code bit better.

A common navigation

step_5

When you visit http://localhost:3000/welcome/home, you get a link to navigate to wish page and when you visit wish page, you get a link to go back to home. Now we have added a page to show time too, we have increased complexity of our app. It would be great if we have a common navigation in all pages where we can jump to any other page.

So in app/views/welcome/, create a file called _nav.html.erb, and fill it with content as shown below:

app/views/welcome/_nav.html.erb
<ul>
  <li>
    <%= link_to "Hello Rails!", {action: :hello} %>
  </li>

  <li>
    <%= link_to "Wish Me", {action: :wish} %>
  </li>

  <li>
    <%= link_to "Time", {action: :time} %>
  </li>
</ul>

So in the code above we have put three link_to which point to all three actions. The file has an underscore at the beginning to denote that it’s a partial. That is this not a a full view code, but it’s a piece code that can be reused in other places.

Now let’s embed this _nav.html.erb in hello.html.erb as shown:

app/views/welcome/hello.html.erb
<h1>
  Hello from Rails!
</h1>

<p>
  Hello and welcome to Rails.
</p>

<p>
  <%= render 'nav' %>
</p>

Notice the <%= render 'nav' %> which looks for _nav.html.erb in app/views/welcome/ and embeds its content between the <p> and </p> tags.

Similarly let’s embed _nav.html.erb in time.html.erb

app/views/welcome/time.html.erb
<h1>
  Time
</h1>

<p>
  The time now is <%= @time %>
</p>

<p>
  <%= render 'nav' %>
</p>

and in wish.html.erb as shown:

app/views/welcome/wish.html.erb
<h1>
  Have a nice day!
</h1>

<p>
  <%= render 'nav' %>
</p>

Now when you visit http://localhost:3000/welcome/hello, you will see a screen with navigation links as shown:

hello rails f8031

These links appear in http://localhost:3000/welcome/wish

hello rails f67e7
hello rails 85d00

Happy hoppin pages!

Changing default landing page

step_6

When you type https://localhost:3000 in your browser, you get default Rails 7 page as shown:

book 691c9

Won’t it be great if it directly routs to welcome/hello when we type https://localhost:3000 in browser?

To do that add root "welcome#hello" in routes.rb as shown below:

config/routes.rb
Rails.application.routes.draw do
  get 'welcome/hello'
  get 'welcome/wish'
  get 'welcome/time'

  root "welcome#hello"
end

Now type https://localhost:3000 or refresh the browser and you will get hello page displayed as shown:

hello rails 2469a

Layouts and Styling

Your first blogging app

App from scratch

Routes

Controllers

Views

Form input

Database stuff

File and Image Uploads

Trix Editor

Action Cable

Scaffolding

Migrations

Test Driven Development

Contact manager app

Rails API

Deploying

Heroku

Dokku

Capistrano

Docker

Bibliography