The Ultimate Intermediate Ruby on Rails Tutorial: Let’s Create an Entire App!
By: Domantas G
Tags: Ruby on Rails, Software Development, Programming, Startup, Web Development

There are plenty tutorials online which show how to create your first app. This tutorial will go a step further and explain line-by-line how to create a more complex Ruby On Rails application.

Throughout the whole tutorial, I will gradually introduce new techniques and concepts. The idea is that with every new section you should learn something new.

The following topics will be discussed throughout this guide:

Ruby On Rails basics

Refactoring: helpers, partials, concerns, design patterns

Testing: TDD/BDD (RSpec & Capybara), Factories (Factory Girl)

Action Cable

Active Job

CSS, Bootstrap, JavaScript, jQuery

So what the app is going to be about?

It’s going to be a platform where you could search and meet like-minded people.

Main functionalities which the app will have:

Authentication (with Devise)

Ability to publish posts, and search and categorize them

Instant messaging (popup windows and a separate messenger), with the ability to create private and group conversations.

Ability to add users to contacts

Real time notifications

You can see how the complete application is going to look.

And you can find the complete project’s source code on GitHub.

Table of Contents

Prerequisites

I will try to explain every line of code and how I came up with the solutions. I think it is entirely possible for a total beginner to complete this guide. But keep in mind that this tutorial covers some topics which are beyond the basics.

So if you are a total beginner, it’s going to be harder, because your learning curve is going to be pretty steep. I will provide links to resources where you could get some extra information about every new concept we touch.

Ideally, it’s best if you are aware of the fundamentals of:

Setup

I assume that you have already set up your basic Ruby On Rails development environment. If not, check RailsInstaller.

I had been developing on Windows 10 for a while. At first it was okay, but after some time I got tired of overcoming mystical obstacles which were caused by Windows. I had to keep figuring out hack ways to make my applications work. I’ve realized that it isn’t worth my time. Overcoming those obstacles didn’t give me any valuable skills or knowledge. I was just spending my time by duct taping Windows 10 setup.

So I switched to a virtual machine instead. I chose to use Vagrant to create a development environment and PuTTY to connect to a virtual machine. If you want to use Vagrant too, this is the tutorial which I found useful.

Create a new app

We are going to use PostgreSQL as our database. It is a popular choice among Ruby On Rails community. If you haven’t created any Rails apps with PostgreSQL yet, you may want to check this tutorial.

Once you are familiar with PostgreSQL, navigate to a directory where you keep your projects and open a command line prompt.

To generate a new app run this line:

rails new collabfield --database=postgresql

Collabfield , that’s how our applications is going to be called. By default Rails uses SQlite3, but since we want to use PostgreSQL as our database, we need to specify it by adding:

--database=postgresql

Now we should’ve successfully generated a new application.

Navigate to a newly created directory by running the command:

cd collabfield

And now we can run our app by entering:

rails s

We just started our app. Now we should be able to see what we got so far. Open a browser and go to http://localhost:3000. If everything went well, you should see the Rails signature welcome page.

Layout

Time to code. Where should we start? Well, we can start wherever we want to. When I build a new website, I like to start by creating some kind of basic visual structure and then build everything else around that. Let’s do just that.

Home page

When we go to http://localhost:3000, we see the Rails welcome page. We’re going to switch this default page with our own home page. In order to do that, generate a new controller called Pages . If you are not familiar with Rails controllers, you should skim through the Action Controller to get an idea what the Rails controller is. Run this line in your command prompt to generate a new controller.

rails g controller pages

This rails generator should have created some files for us. The output in the command prompt should look something like this:

We are going to use this PagesController to manage our special and static pages. Now open the Collabfield project in a text editor. I use Sublime Text, but you can use whatever you want to.

Open a file pages_controller.rb

app/controllers/pages_controller.rb

We’ll define our home page here. Of course we could define home page in a different controller and in different ways. But usually I like to define the home page inside the PagesController .

When we open pages_controller.rb , we see this:

controllers/pages_controller.rb

It’s an empty class, named PagesController , which inherits from the ApplicationController class. You can find this class’s source code in app/controllers/application_controller.rb .

All our controllers, which we will create, are going to inherit from ApplicationController class. Which means that all methods defined inside this class are going to be available across all our controllers.

We’ll define a public method named index , so it can be callable as an action:

controllers/pages_controller.rb

As you may have read in the Action Controller, routing determines which controller and its public method (action) to call. Let’s define a route, so when we open our root page of the website, Rails knows which controller and its action to call. Open a routes.rb file in app/config/routes.rb .

If you don’t know what Rails routes is, it is a perfect time to get familiar by reading the Rails Routing.

Insert this line:

root to: 'pages#index'

Your routes.rb file should look like this:

config/routes.rb

Hash symbol # in Ruby represents a method. As you remember an action is just a public method, so pages#index says “call the PagesController and its public method (action) index .”

If we went to our root path http://localhost:3000, the index action would be called. But we don’t have any templates to render yet. So let’s create a new template for our index action. Go to app/views/pages and create an index.html.erb file inside this directory. Inside this file we can write our regular HTML+ Embedded Ruby code. Just write something inside the file, so we could see the rendered template in the browser.

<h1>Home page</h1>

Now when we go to http://localhost:3000, we should see something like this instead of the default Rails information page.

Now we have a very basic starting point. We can start introducing new things to our website. I think it’s time to create our first commit.

In your command prompt run:

git status

And you should see something like this:

If you don’t already know, when we generate a new application, a new local git repository is initialized.

Add all current changes by running:

git add -A

Then commit all changes by running:

git commit -m "Generate PagesController. Initialize Home page"

If we ran this:

git status

We would see that there’s nothing to commit, because we just successfully committed all changes.

Bootstrap

For the navigation bar and the responsive grid system we’re going to use the Bootstrap library. In order to use this library we have to install the

bootstrap-sass gem. Open the Gemfile in your editor.

collabfield/Gemfile

Add a bootstrap-sass gem to the Gemfile. As the documentation says, you have to ensure that sass-rails gem is present too.

...

gem 'bootstrap-sass', '~> 3.3.6'

gem 'sass-rails', '>= 3.2'

...

Save the file and run this to install newly added gems:

bundle install

If you are still running the application, restart the Rails server to make sure that new gems are available. To restart the server simply shutdown it by pressing Ctrl + C and run rails s command again to boot the server.

Go to assets to open the application.css file:

app/assets/stylesheets/application.css

Below all the commented text add this:

...

@import "bootstrap-sprockets";

@import "bootstrap";

Now change the application.css name to application.scss . This is necessary in order to use Bootstrap library in Rails, also it allows us to use Sass features.

We want to control the order in which all .scss files are rendered, because in the future we might want to create some Sass variables. We want to make sure that our variables are going to be defined before we use them.

To accomplish it, remove those two lines from the application.scss file:

*= require_self

*= require_tree .

We’re almost able to use Bootstrap library. There’s a one more thing which we have to do. As the bootstrap-sass docs says, Bootstrap JavaScript is dependent on jQuery library. To use jQuery with Rails, you have to add jquery-rails gem.

gem 'jquery-rails'

Run…

bundle install

…again, and restart the server.

Last step is to require Bootstrap and jQuery in the application’s JavaScript file. Go to application.js

app/assets/javascripts/application.js

Then add the following lines in the file:

//= require jquery

//= require bootstrap-sprockets

Commit the changes:

git add -A

git commit -m "Add and configure bootstrap gem"

Navigation bar

For the navigation bar we’ll use Bootstrap’s navbar component as the starting point and then quite modify it. We will store our navigation bar inside a partial template.

We’re doing this because it’s better to keep every component of the app in separate files. It allows to test and manage app’s code much easier. Also we can reuse those components in other parts of the app, without duplicating the code.

Navigate to:

views/layouts

Create a new file:

_navigation.html.erb

For partials we use underscore prefix, so the Rails framework can distinguish it as a partial. Now copy and paste navbar component from Bootstrap docs and save the file. To see the partial on the website, we have to render it somewhere. Navigate to views/layouts/application.html.erb . This is the default file where everything gets rendered.

Inside the file we see the following method:

<%= yield %>

It renders the requested template. To use ruby syntax inside the HTML file, we have to wrap it around with <% %> (embedded ruby allows us to do that). To quickly learn the differences between ERB syntax, checkout this StackOverflow answer.

In Home page section we set the route to recognize the root URL. So whenever we send a GET request to go to a root page, PagesController‘sindex action gets called. And that respective action (in this case the index action) responds with a template which gets rendered with the yield method. As you remember, our template for a home page is located at app/views/pages/index.html.erb .

Since we want to have a navigation bar across all pages, we’ll render our navigation bar inside the default application.html.erb file. To render a partial file , simply use the render method and pass partial’s path as an argument. Do it just above the yield method like this:

...

<%= render 'layouts/navigation' %>

<%= yield %>

...

Now go to http://localhost:3000 and you should be able to see the navigation bar.

As mentioned above, we’re going to modify this navigation bar. First let’s remove all <li> and <form> elements. In the future we’ll create our own elements here. The _navigation.html.erb file should look like this now.

layouts/_navigation.html.erb

We have a basic responsive navigation bar now. It’s a good time to create a new commit. In command prompt run the following commands:

git add -A

git commit -m "Add a basic navigation bar"

We should change the navigation bar’s name from Brand to collabfield . Since Brand is a link element, we should use a link_to method to generate links. Why? Because this method allows us to easily generate URI paths. Open a command prompt and navigate to the project’s directory. Run the the following command:

rails routes

This command outputs our available routes, which are generated by the routes.rb file. As we see:

Currently, we have only one route, the one which we’ve defined before. If you look at the given routes, you can see a Prefix column. We can use those prefixes to generate a path to a wanted page. All we have to do is use a prefix name and add _path to it. If we wrote the root_path , that would generate a path to the root page. So let’s use the power of link_to method and routes.

Replace this line:

<a class="navbar-brand" href="#">Brand</a>

With this line:

<%= link_to 'collabfield', root_path, class: 'navbar-brand' %>

Remember that whenever you don’t quite understand how a particular method works, just Google it and you will probably find its documentation with an explanation. Sometimes documentations are poorly written, so you might want to Google a little bit more and you might find a blog or a StackOverflow answer, which would help.

In this case we pass a string as our first argument to add the <a> element’s value, the second argument is needed for a path, this is where routes helps us to generate it. The third argument is optional, which is accumulated inside the options hash. In this case we needed to add navbar-brand class to keep our Bootstrap powered navigation bar to function.

Let’s do another commit for this small change. In the upcoming section we’ll start changing our app’s design, starting from the navigation bar.

git add -A

git commit -m "Change navigation bar's brand name from Brand to collabfield"

Style sheets

Let me introduce you how I structure my style sheet files. From what I know there aren’t any strong conventions on how to structure your style sheets in Rails. Everyone is doing it slightly differently.

This is how I usually structure my files.

Base directory — This is where I keep Sass variables and styles which are used throughout the whole app. For instance default font sizes and default elements’ styles.

— This is where I keep Sass variables and styles which are used throughout the whole app. For instance default font sizes and default elements’ styles. Partials — Most of my styles go there. I keep all styles for separate components and pages in this directory.

— Most of my styles go there. I keep all styles for separate components and pages in this directory. Responsive — Here I define different style rules for different screen sizes. For example, styles for a desktop screen, tablet screen, phone screen, etc.

First, let’s create a new repository branch by running this:

git checkout -b "styles"

We’ve just created a new git branch and automatically switched to it. From now on this is how we’re going to implement new changes to the code.

The reason for doing this is that we can isolate our currently functional version (master branch) and write a new code inside a project’s copy, without being afraid to damage anything.

Once we are complete with the implementation, we can just merge changes to the master branch.

Start by creating few directories:

app/assets/stylesheets/partials/layout

Inside the layout directory create a file navigation.scss and inside the file add:

app/assets/stylesheets/partials/layout/navigation.scss

With these lines of code we change navbar’s background and links color. As you may have noticed, a selector is nested inside another declaration block. Sass allows us to use this functionality. !important is used to strictly override default Bootstraps styles. The last thing which you may have noticed is that instead of a color name, we use a Sass variable. The reason for this is that we are going to use this color multiple times across the app. Let’s define this variable.

First create a new folder:

app/assets/stylesheets/base

Inside the base directory create a new file variables.scss . Inside the file define a variable:

$navbarColor: #323738;

If you tried to go to http://localhost:3000, you wouldn’t notice any style changes. The reason for that is that in the Bootstrap section we removed these lines:

*= require_self

*= require_tree .

from application.scss , to not automatically import all style files.

This means that now we have to import our newly created files to the main application.scss file. The file should look like this now:

app/assets/stylesheets/application.scss

The reason for importing variables.scss file at the top is to make sure that the variables are defined before we use them.

Add some more CSS at the top of the navigation.scss file:

app/assets/stylesheets/partials/layout/navigation.scss

Of course you can put this code at the bottom of the file if you want to. Personally, I order and group CSS code based on CSS selectors’ specificity. Again, everyone is doing it slightly differently. I put less specific selectors above and more specific selectors below. So for instance Type selectors go above Class selectors and Class selectors go above ID selectors.

Let’s commit changes:

git add -A

git commit -m "Add CSS to the navigation bar"

We want to make sure that the navigation bar is always visible, even when we scroll down. Right now we don’t have enough content to scroll down, but we will in the future. Why don’t we give this feature to the navigation bar right now?

To do that use Bootstrap class navbar-fixed-top . Add this class to the nav element, so it looks like this:

<nav class="navbar navbar-default navbar-fixed-top">

Also we want to have collabfield to be to the Bootstrap Grid System’s left side boundaries. Right now it is to the viewport’s left side boundaries, because our class is currently container-fluid . To change that, change the class to container .

It should look like this:

<div class="container">

Commit the changes:

git add -A

git commit -m "

- in _navigation.html.erb add navbar-fixed-top class to nav.

- Replace container-fluid class with container"

If you go to http://localhost:3000, you see that the Home page text is hidden under the navigation bar. That’s because of the navbar-fixed-top class. To solve this issue, push the body down by adding the following to navigation.scss :

body {

margin-top: 50px;

}

At this stage the app should look like this:

Commit the change:

git add -A

git commit -m "Add margin-top 50px to the body"

As you remember, we’ve created a new branch before and switched to it. It’s time to go back to the master branch.

Run the command:

git branch

You can see the list of our branches. Currently we’re in the styles branch.

To switch back to the master branch, run:

git checkout master

To merge our all changes, which we did in the styles branch, simply run:

git merge styles

The command merged those two branches and now we can see the summary of changes we made.

We don’t need styles branch anymore, so we can delete it:

git branch -D styles

Posts

It’s almost a right time to start implementing the posts functionality. Since our app goal is to let users meet like-minded people, we have to make sure that posts’ authors can be identified. To achieve that, authentication system is required.

Authentication

For an authentication system we are going to use the devise gem. We could create our own authentication system, but that would require a lot of effort. We’ll choose an easier route. Also it’s a popular choice among Rails community.

Start by creating a new branch:

git checkout -b authentication

Just like with any other gem, to set it up we’ll follow its documentation. Fortunately, it’s very easy to set up.

Add to your Gemfile

gem 'devise'

Then run commands:

bundle install

rails generate devise:install

You probably see some instructions in the command prompt. We won’t use mailers in this tutorial, so no further configuration is needed.

At this point, if you don’t know anything about Rails models, you should get familiar with them by skimming through Active Record and Active Modeldocumentations.

Now let’s use a devise generator to create a User model.

rails generate devise User

Initialize a database for the app by running:

rails db:create

Then run this command to create new tables in your database:

rails db:migrate

That’s it. Technically our authentication system is set up. Now we can use Devise given methods and create new users. Commit the change:

git add -A

git commit -m "Add and configure the Devise gem"

By installing Devise gem, we not only get the back-end functionality, but also default views. If you list your routes by running:

rails routes

You can see that now you have a bunch of new routes. Remember, we only had a one root route until now. If something seems to be confusing, you can always open devise docs and get your answers. Also don’t forget that a lot of same questions come to other people’ s minds. There’s a high chance that you’ll find the answer by Googling too.

Try some of those routes. Go to localhost:3000/users/sign_in and you should see a sign in page.

If you went to localhost:3000/users/sign_up, you would see a sign up page too. God Damn! as Noob Noob says. If you look at the views directory, you see that there isn’t any Devise directory, which we could modify. As Devise docs says, in order to modify Devise views, we’ve to generate it with a devise generator. Run

rails generate devise:views

If you check the views directory, you’ll see a generated devise directory inside. Here we can modify how sign up and login pages are going to look like. Let’s start with the login page, because in our case this is going to be a more straightforward implementation. With the registration page, due to our wanted feature, an extra effort will be required.

Login page

Navigate to and open app/views/devise/sessions/new.html.erb .

This is where the login page views are stored. There’s just a login form inside the file. As you may have noticed, the form_for method is used to generate this form. This is a handy Rails method to generate forms. We’re going to modify this form’s style with bootstrap. Replace all file’s content with:

views/devise/sessions/new.html.erb

Nothing fancy is going here. We just modified this form to be a bootstrap form by changing the method’s name to bootstrap_form_for and adding form-control classes to the fields.

Take a look how arguments inside the methods are styled. Every argument starts in a new line. The reason why I did this is to avoid having long code lines. Usually code lines shouldn’t be longer than 80 characters, it improves readability. We’re going to style the code like that for the rest of the guide.

If we visit localhost:3000/users/sign_in, we’ll see that it gives us an error:

undefined method 'bootstrap_form_for'

In order to use bootstrap forms in Rails we’ve to add a bootstrap_form gem. Add this to the Gemfile

gem 'bootstrap_form'

Then run:

bundle install

At this moment the login page should look like this:

Commit changes:

git add -A

git commit -m "Generate devise views, modify sign in form

and add the bootstrap_form gem."

To give the bootstrap’s grid system to the page, wrap login form with the bootstrap container.

views/devise/sessions/new.html.erb

The width of the login form is 6 columns out of 12. And the offset is 3 columns. On smaller devices the form will take full screen’s width. That’s how the bootstrap gridworks.

Let’s do another commit. Quite a minor change, huh? But that’s how I usually do commits. I implement a definite change in one area and then commit it. I think doing it this way helps to track changes and understand how the code has evolved.

git add -A

git commit -m "wrap login form in the login page with a boostrap container"

It would be better if we could just reach the login page by going to /login instead of /users/sign_in . We have to change the route. To do that we need to know where the action, which gets called when we go to login page, is located. Devise controllers are located inside the gem itself. By reading Devise docs we can see that all controllers are located inside the devise directory. Not really surprised by the discovery, to be honest U_U. By using the devise_scope method we can simply change the route. Go to routes.rb file and add

devise_scope :user do

get 'login', to: 'devise/sessions#new'

end

Commit the change:

git add -A

git commit -m "change route from /users/sign_in to /login"

For now, leave the login page as it is.

Sign up page

If we navigated to localhost:3000/users/sign_up, we would see the default Devise sign up page. But as mentioned above, the sign up page will require some extra effort. Why? Because we want to add a new :name column to the users table, so a User object could have the :name attribute.

We’re about to do some changes to the schema.rb file. At this moment, if you aren’t quite familiar with schema changes and migrations, I recommend you to read through Active Record Migrations docs.

First, we have to add an extra column to the users table. We could create a new migration file and use a change_table method to add an extra column. But we are just at the development stage, our app isn’t deployed yet. We can just define a new column straight inside the devise_create_users migration file and then recreate the database. Navigate to db/migrate and open the *CREATION_DATE*_devise_create_users.rb file and add t.string :name, null: false, default: "" inside the create_table method.

Now run the commands to drop and create the database, and run migrations.

rails db:drop

rails db:create

rails db:migrate

We added a new column to the users table and altered the schema.rb file.

To be able to send an extra attribute, so the Devise controller would accept it, we’ve to do some changes at the controller level. We can do changes to Devise controllers in few different ways. We can use devise generator and generate controllers. Or we can create a new file, specify the controller and the methods that we want to modify. Both ways are good. We are going to use the latter one.

Navigate to app/controllers and create a new file registrations_controller.rb . Add the following code to the file:

controllers/registrations_controller.rb

This code overwrites the sign_up_params and account_update_params methods to accept the :name attribute. As you see, those methods are in the Devise RegistrationsController , so we specified it and altered its methods. Now inside our routes we have to specify this controller, so these methods could be overwritten. Inside routes.rb change

devise_for :users

to

devise_for :users, :controllers => {:registrations => "registrations"}

Commit the changes.

git add -A

git commit -m "

- Add the name column to the users table.

- Include name attribute to sign_up_params and account_update_params

methods inside the RegistrationsController"

Open the new.html.erb file:

app/views/devise/registrations/new.html.erb

Again, remove everything except the form. Convert the form into a bootstrap form. This time we add an additional name field.

views/devise/registrations/new.html.erb

Commit the change.

git add -A

git commit -m "

Delete everything from the signup page, except the form.

Convert form into a bootstrap form. Add an additional name field"

Wrap the form with a bootstrap container and add some text.

views/devise/registrations/new.html.erb

Commit the change.

git add -A

git commit -m "

Wrap the sign up form with a bootstrap container.

Add informational text inside the container"

Just like with the login page, it would be better if we could just open a sign up page by going to /signup instead of users/sign_up . Inside the routes.rb file add the following code:

devise_scope :user do

get 'signup', to: 'devise/registrations#new'

end

Commit the change.

git add -A

git commit -m "Change sign up page's route from /users/sign_up to /signup"

Let’s apply a few style changes before we move on. Navigate to app/assets/sytlesheets/partials and create a new signup.scss file. Inside the file add the following CSS:

assets/stylesheets/partials/signup.scss

Also we haven’t imported files from the partials directory inside the application.scss file. Let’s do it right now. Navigate to the application.scss and just above the @import partials/layout/* , import all files from the partials directory. Application.scss should look like this

assets/stylesheets/application.scss

Commit the changes.

git add -A

git commit -m "

- Create a signup.scss and add CSS to the sign up page

- Import all files from partials directory to the application.scss"

Add few other style changes to the overall website look. Navigate to app/assets/stylesheets/base and create a new default.scss file. Inside the file add the following CSS code:

assets/stylesheets/base/default.scss

Here we apply some general style changes for the whole website. font-size is set to 62.5% , so 1 rem unit could represent 10px . If you don’t know what the rem unit is, you may want to read this tutorial. We don’t want to see a label text on bootstrap forms, that’s why we set this:

.control-label {

display: none;

}

You may have noticed that the $backgroundColor variable is used. But this variable isn’t set yet. So let’s do it by opening variables.scss file and adding this:

$backgroundColor: #f0f0f0;

The default.scss file isn’t imported inside the application.scss . Import it below variables, the application.scss file should look like this:

assets/stylesheets/application.scss

Commit the changes.

git add -A

git commit -m "

Add CSS and import CSS files inside the main file - Create a default.scss file and add CSS

- Define $backgroundColor variable

- Import default.scss file inside the application.scss"

Navigation bar update

Right now we have three different pages: home, login and signup. It is a good idea to connect them all together, so users could navigate through the website effortlessly. We’ll put links to signup and login pages on the navigation bar. Navigate to and open the _navigation.html.erb file.

app/views/layouts/_navigation.html.erb

We’re going to add some extra code here. In the future we will add even more code here. This will lead to a file with lots of code, which is hard to manage and test. In order to handle a long code easier, we’re going to start splitting larger code into smaller chunks. To achieve that, we’ll use partials. Before adding extra code, let’s split the current _navigation.html.erb code into partials already.

Let me quickly introduce you how our navigation bar is going to work. We’ll have two major parts. On one part elements will be shown all the time, no matter what the screen size is. On the other part of the navigation bar, elements will be shown only on bigger screens and collapsed on the smaller ones.

This is how the structure inside the .container element will look like:

layouts/_navigation_html.erb

Inside the layouts directory:

app/views/layouts

Create a new navigation directory. Inside this directory create a new partial _header.html.erb file.

app/views/layouts/navigation/_header.html.erb

From the _navigation.html.erb file cut the whole .navbar-header section and paste it inside the _header.html.erb file. Inside the navigation directory, create another partial file named _collapsible_elements.html.erb .

app/views/layouts/navigation/_collapsible_elements.html.erb

From the _navigation.html.erb file cut the whole .navbar-collapse section and paste it inside the _collapsible_elements.html.erb . Now let’s render those two partials inside the _navigation.html.erb file. The file should look like this now.

layouts/_navigation_html.erb

If you went to http://localhost:3000 now, you wouldn’t notice any difference. We just cleaned our code a little bit and prepared it for a further development.

We are ready to add some links to the navigation bar. Navigate to and open the _collapsible_elements.html.erb file again:

app/views/layouts/_collapsible_elements.html.erb

Let’s fill this file with links, replace the file’s content with:

layouts/navigation/_collapsible_elements.html.erb

Let me briefly explain to you what is going on here. First, at the second line I changed the element’s id to navbar-collapsible-content . This is required in order to make this content collapsible. It’s a bootstrap’s functionality. The default id was bs-example-navbar-collapse-1 . To trigger this this function there’s the button with the data-target attribute inside the _header.html file. Open views/layouts/navigation/_header.html.erb and change data-target attribute to data-target="#navbar-collapsible-content" . Now the button will trigger the collapsible content.

Next, inside the _collapsible_elements.html.erb file you see some if else logic with the user_signed_in? Devise method. This will show different links based on if a user is signed in, or not. Leaving logic, such as if else statements inside views isn’t a good practice. Views should be pretty “dumb” and just spit the information out, without “thinking” at all. We will refactor this logic later with Helpers.

The last thing to note inside the file is pc-menu and mobile-menu CSS classes. The purpose of these classes is to control how links are displayed on different screen sizes. Let’s add some CSS for these classes. Navigate to app/assets/stylesheets and create a new directory responsive . Inside the directory create two files, desktop.scss and mobile.scss . The purpose of those files is to have different configurations for different screen sizes. Inside the desktop.scss file add:

assets/styleshseets/responsive/desktop.scss

Inside the mobile.scss file add:

assets/styleshseets/responsive/mobile.scss

If you aren’t familiar with CSS media queries, read this. Import files from the responsive directory inside the application.scss file. Import it at the bottom of the file, so the application.scss should look like this:

app/assets/stylesheets/application.scss

Navigate to and open navigation.scss file

app/assets/stylesheets/partials/layout/navigation.scss

and do some stylistic tweaks to the navigation bar by adding the following inside the nav element’s selector:

assets/styleshseets/partials/layout/navigation.scss

And outside the nav element, add the following CSS code:

assets/styleshseets/partials/layout/navigation.scss

At this moment, our application should look like this when a user is not logged in:

Like this when a user is logged in:

And like this when the screen size is smaller:

Commit the changes.

git add -A

git commit -m "

Update the navigation bar - Add login, signup, logout and edit profile links on the navigation bar

- Split _navigation.scss code into partials

- Create responsive directory inside the stylesheets directory and add CSS.

- Add CSS to tweak navigation bar style"

Now we have a basic authentication functionality. It satisfies our needs. So let’s merge authentication branch with the master branch.

git checkout master

git merge authentication

We can see the summary of changes again. Authentication branch is not needed anymore, so delete it.

git branch -D authentication

Helpers

When we were working on the _collapsible_elements.html.erb file, I mentioned that Rails views is not the right place for logic. If you look inside the app directory of the project, you see there’s the directory called helpers . We’ll extract logic from Rails views and put it inside the helpers directory.

app/views/pages

Let’s create our first helpers. Firstly, create a new branch and switch to it.

git checkout -B helpers

Navigate to the helpers directory and create a new navigation_helper.rb file

app/helpers/navigation_helper.rb

Inside helper files, helpers are defined as modules. Inside the navigation_helper.rb define the module.

app/helpers/navigation_helper.rb

By default Rails loads all helper files to all views. Personally I do not like this, because methods’ names from different helper files might clash. To override this default behavior open the application.rb file

config/application.rb

Inside the Application class add this configuration

config.action_controller.include_all_helpers = false

Now helpers are available for corresponding controller’s views only. So if we have the PagesController , all helpers inside the pages_helper.rb file will be available to all view files inside the pages directory.

We don’t have the NavigationController , so helper methods defined inside the NavigationHelper module won’t be available anywhere. The navigation bar is available across the whole website. We can include the NavigationHelper module inside the ApplicationHelper . If you aren’t familiar with loading and including files, read through this article to get an idea what is going to happen.

Inside the application_helper.rb file, require the navigation_helper.rb file. Now we have an access to the navigation_helper.rb file’s content. So let’s inject NavigationHelper module inside the ApplicationHelper module by using an include method. The application_helper.rb should look like this:

helpers/application_helper.rb

Now NavigationHelper helper methods are available across the whole app.

Navigate to and open the _collapsible_elements.html.erb file

app/views/layouts/navigation/ _collapsible_elements.html.erb

We’re going to split the content inside the if else statements into partials. Create a new collapsible_elements directory inside the navigation directory.

app/views/layouts/navigation/ collapsible_elements

Inside the directory create two files: _signed_in_links.html.erb and _non_signed_in_links.html.erb . Now cut the content from _collapsible_elements.html.erb file’s if else statements and paste it to the corresponding partials. The partials should look like this:

layouts/navigation/collapsible_elements/_signed_in_links.html.erb

layouts/navigation/collapsible_elements/_non_signed_in_links.html.erb

Now inside the _collapsible_elements.html.erb file, instead of if else statements, add the render method with the collapsible_links_partial_path helper method as an argument. The file should look like this

layouts/navigation/_collapsible_elements.html.erb

collapsible_links_partial_path is the method we are going to define inside the NavigationHelper . Open navigation_helper.rb

app/helpers/navigation_helper.rb

and define the method inside the module. The navigation_helper.rb file should look like this:

app/helpers/navigation_helper.rb

The defined method is pretty straightforward. If a user is signed in, return a corresponding partial’s path. If a user is not signed in, return another partial’s path.

We’ve created our first helper method and extracted logic from views to a helper method. We’re going to do this for the rest of the guide, whenever we encounter logic inside a view file. By doing this we’re making a favor to ourselves, testing and managing the app becomes much easier.

The app should look and function the same.

Commit the changes.

git add -A

git commit -m "Configure and create helpers - Change include_all_helpers config to false

- Split the _collapsible_elements.html.erb file's content into

partials and extract logic from the file into partials"

Merge the helpers branch with the master



git merge helpers git checkout mastergit merge helpers https://gist.github.com/domagude/419bba70cb97e27f4ea04fe37820194a#file-rails_helper-rb

Testing

At this point the application has some functionality. Even thought there aren’t many features yet, but we already have to spend some time by manually testing the app if we want to make sure that everything works. Imagine if the application had 20 times more features than it has now. What a frustration would be to check that everything works fine, every time we did code changes. To avoid this frustration and hours of manual testing, we’ll implement automated tests.

Before diving into tests writing, allow me to introduce you how and what I test. Also you can read through A Guide to Testing Rails Applications to get familiar with default Rails testing techniques.

What I use for testing

Framework: RSpec

When I started testing my Rails apps, I used the default Minitestframework. Now I use RSpec. I don’t think there’s a good or a bad choice here. Both frameworks are great. I think it depends on a personal preference, which framework to use. I’ve heard that RSpec is a popular choice among Rails community, so I’ve decided to give it a shot. Now I am using it most of the time.

RSpec When I started testing my Rails apps, I used the default Minitestframework. Now I use RSpec. I don’t think there’s a good or a bad choice here. Both frameworks are great. I think it depends on a personal preference, which framework to use. I’ve heard that RSpec is a popular choice among Rails community, so I’ve decided to give it a shot. Now I am using it most of the time. Sample data: factory_girl

Again, at first I tried the default Rails way — fixtures, to add sample data. I’ve found that it’s a different case than it is with testing frameworks. Which testing framework to choose is probably a personal preference. In my opinion it’s not the case with sample data. At first fixtures were fine. But I’ve noticed that after apps become larger, controlling sample data with fixtures becomes tough. Maybe I used it in a wrong way. But with factories everything was nice and peaceful right away. No matter if an app is smaller or bigger — the effort to set sample data is the same.

factory_girl Again, at first I tried the default Rails way — fixtures, to add sample data. I’ve found that it’s a different case than it is with testing frameworks. Which testing framework to choose is probably a personal preference. In my opinion it’s not the case with sample data. At first fixtures were fine. But I’ve noticed that after apps become larger, controlling sample data with fixtures becomes tough. Maybe I used it in a wrong way. But with factories everything was nice and peaceful right away. No matter if an app is smaller or bigger — the effort to set sample data is the same. Acceptance tests: Capybara

By default Capybara uses rack_test driver. Unfortunately, this driver doesn’t support JavaScript. Instead of the default Capybara’s driver, I chose to use poltergeist. It supports JavaScript and in my case it was the easiest driver to set up.

What I test

I test all logic which is written by me. It could be:

Helpers

Models

Jobs

Design Patterns

Any other logic written by me

Besides logic, I wrap my app with acceptance tests using Capybara, to make sure that all app’s features are working properly by simulating a user’s interaction. Also to help my simulation tests, I use request tests to make sure that all requests return correct responses.

That’s what I test in my personal apps, because it fully satisfies my needs. Obviously, testing standards could be different from person to person and from company to company.

Controllers, views and gems weren’t mentioned, why? As many Rails developers say, controllers and views shouldn’t contain any logic. And I agree with them. In this case there isn’t much to test then. In my opinion, user simulation tests are enough and efficient for views and controllers. And gems are already tested by their creators. So I think that simulation tests are enough to make sure that gems work properly too.

How I test

Of course I try to use TDD approach whenever is possible. Write a test first and then implement the code. In this case the development flow becomes more smoother. But sometimes you aren’t sure how the completed feature is going to look like and what kind of output to expect. You might be experimenting with the code or just trying different implementation solutions. So in those cases, test first and implementation later approach doesn’t really work.

Before (sometimes after, as discussed above) every piece of logic I write, I write an isolated test for it a.k.a. unit test. To make sure that every feature of an app works, I write acceptance (user simulation) tests with Capybara.

Set up a test environment

Before we write our first tests, we have to configure the testing environment.

Open the Gemfile and add those gems to the test group

gem 'rspec-rails', '~> 3.6'

gem 'factory_girl_rails'

gem 'rails-controller-testing'

gem 'headless'

gem 'capybara'

gem 'poltergeist'

gem 'database_cleaner'

As discussed above, rspec gem is a testing framework, factory_girl is for adding sample data, capybara is for simulating a user’s interaction with the app and poltergeist driver gives the JavaScript support for your tests.

You can use another driver which supports JavaScript if it’s easier for you to set up. If you decide to use poltergeist gem, you will need PhantomJS installed. To install PhantomJS read poltergeist docs.

headless gem is required to support headless drivers. poltergeist is a headless driver, that’s why we need this gem. rails-controller-testing gem is going to be required when we will test requests and responses with the requests specs. More on that later.

database_cleaner is required to clean the test database after tests where JavaScript was executed. Normally the test database cleans itself after each test, but when you test features which has some JavaScript, the database doesn’t clean itself automatically. It might change in the future, but at the moment, of writing this tutorial, after tests with JavaScript are executed, the test database isn’t cleaned automatically. That’s why we have to manually configure our test environment to clean the test database after each JavaScript test too. We’ll configure when to run the database_cleaner gem in just a moment.

Now when the purpose of these gems is covered, let’s install them by running:

bundle install

To initialize the spec directory for the RSpec framework run the following:

rails generate rspec:install

Generally speaking, spec means a single test in RSpec framework. When we run our specs, it means that we run our tests.

If you look inside the app directory, you will notice a new directory called spec . This is where we’re going to write tests. Also you may have noticed a directory called test . This is where tests are stored when you use a default testing configuration. We won’t use this directory at all. You can simply remove it from the project c(x_X)b.

As mentioned above, we have to set up the database_cleaner for the tests which include JavaScript. Open the rails_helper.rb file

spec/rails_helper.rb

Change this line

config.use_transactional_fixtures = true

to

config.use_transactional_fixtures = false

and below it add the following code:

spec/rails_helper.rb

I took this code snippet from this tutorial.

The last thing we’ve to do is to add some configurations. Inside the rails_helper.rb file’s configurations, add the following lines

spec/rails_helper.rb

Let’s breakdown the code a little bit.

With require methods we load files from the new added gems, so we could use their methods below.

config.include Devise::Test::IntegrationHelpers, type: :feature

This configuration allows us to use devise methods inside capybara tests. How did I come up with this line? It was provided inside the Devise docs.

config.include FactoryGirl::Syntax::Methods

This configuration allows to use factory_girl gem’s methods. Again, I found this configuration inside the gem’s documentation.

Capybara.javascript_driver = :poltergeist

Capybara.server = :puma

Those two configurations are required in order to be able to test JavaScript with capybara . Always read the documentation first, when you want to implement something you don’t know how to.

The reason why I introduced you with most of the testing gems and configurations at once and not gradually, once we meet a particular problem, is to give you a clear picture what I use for testing. Now you can always come back to this section and check majority of the configurations in one place. Rather than jumping from one place to another and putting gems with configurations like puzzle pieces together.

Let’s commit the changes and finally get our hands dirty with tests.

git add -A

git commit -m "

Set up the testing environment - Remove test directory

- Add and configure rspec-rails, factory_girl_rails,

rails-controller-testing, headless, capybara, poltergeist,

database_cleaner gems"

Helper specs

About each type of specs (tests), you can find general information by reading rspec docs and its gem docs. Both are pretty similar, but you can find some differences between each other.

Create and switch to a new branch:

git checkout -b specs

So far we’ve created only one helper method. Let’s test it.

Navigate to spec directory anhttps://gist.github.com/domagude/3c42ba6ccf31bf1c50588c59277a9146#file-navigation_helper_spec-rbd create a new directory called helpers .

spec/helpers

Inside the directory, create a new file navigation_helper_spec.rb

spec/helpers/navigation_helper_spec.rb

Inside the file, write the following code:

spec/helpers/navigation_helper_spec.rb

require ‘rails_helper' gives us an access to all testing configurations and methods. :type => :helper treats our tests as helper specs and provides us with specific methods.

That’s how the navigation_helper_spec.rb file should look like when the collapsible_links_partial_path method is tested.

spec/helpers/navigation_helper_spec.rb

To learn more about the context and it , read the basic structure docs. Here we test two cases — when a user is logged in and when a user is not logged in. In each context of signed in user and non-signed in user , we have before hooks. Inside the corresponding context, those hooks (methods) run before each our tests. In our case, before each test we run the stub method, so the user_signed_in? returns whatever value we tell it to return.

And finally, with the expect method we check that when we call collapsible_links_partial_path method, we get an expected return value.

To run all tests, simply run:

rspec spec

To run specifically the navigation_helper_spec.rb file, run:

rspec spec/helpers/navigation_helper_spec.rb

If the tests passed, the output should look similar to this:

Commit the changes.

git add -A

git commit -m "Add specs to NavigationHelper's collapsible_links_partial_path method"

Factories

Next, we’ll need some sample data to perform our tests. factory_girl gem gives us ability to add sample data very easily, whenever we need it. Also it provides a good quality docs, so it makes the overall experience pretty pleasant. The only object we can create with our app so far is the User . To define the user factory, create a factories directory inside the spec directory.

spec/factories

Inside the factories directory create a new file users.rb and add the following code:

spec/factories/users

Now within our specs, we can easily create new users inside the test database, whenever we need them, using factory_girl gem’s methods. For the comprehensive guide how to define and use factories, checkout the factory_girl gem’s docs.

Our defined factory, user , is pretty straightforward. We defined the values, user objects will have. Also we used the sequence method. By reading docs, you can see that with every additional User record, n value gets incremented by one. I.e. the first created user‘s name is going to be test0 , the second one’s test1 , etc.

Commit the changes.

git add -A

git commit -m "add a users factory"

Feature specs

In the feature specs we write code which simulates a user’s interaction with an app. Feature specs are powered by the capybara gem.

Good news is that we’ve everything set up and ready to write our first feature specs. We’re going to test the login, logout and signup functionalities.

Inside the spec directory, create a new directory called features .

spec/features

Inside the features directory, create another directory called user .

spec/features/user

Inside the user directory, create a new file called login_spec.rb

spec/features/user/login_spec.rb

That’s how the login test looks like:

spec/features/user/login_spec.rb

With this code we simulate a visit to the login page, starting from the home page. Then we fill the form and submit it. Finally, we check if we have the #user-settings element on the navigation bar, which is available only for signed in users.

feature and scenario are part of the Capybara’s syntax. feature is the same as context / describe and scenario is the same as it . More info you can find in Capybara’s docs, Using Capybara With Rspec.

let method allows us to write memorized methods which we could use across all specs within the context, the method was defined.

Here we also use our created users factory and the create method, which comes with the factory_girl gem.

js: true argument allows to test functionalities which involves JavaScript.

As always, to see if a test passes, run a specific file. In this case it is the login_spec.rb file:

rspec spec/features/user/login_spec.rb

Commit the changes.

git add -A

git commit -m "add login feature specs"

Now we can test the logout functionality. Inside the user directory, create a new file named logout_spec.rb

spec/features/user/logout_spec.rb

The implemented test should look like this:

spec/features/user/logout_spec.rb

The code simulates a user clicking the logout button and then expects to see non-logged in user’s links on the navigation bar.

sign_in method is one of the Devise helper methods. We have included those helper methods inside the rails_helper.rb file previously.

Run the file to see if the test passes.

Commit the changes.

git add -A

git commit -m "add logout feature specs"

The last functionality we have is ability to sign up a new account. Let’s test it. Inside the user directory create a new file named sign_up_spec.rb . That’s how the file with the test inside should look like:

spec/features/user/sign_up_spec.rb

We simulate a user navigating to the signup page, filling the form, submitting the form and finally, we expect to see the #user-settings element which is available only for logged in users.

Here we use the Devise’s build method instead of create . This way we create a new object without saving it to the database.

We can run the whole test suite and see if all tests pass successfully.

rspec spec

Commit the changes.

git add -A

git commit -m "add sign up features specs"

We’re done with our first tests. So let’s merge the specs branch with the master .

git checkout master

git merge specs

Specs branch isn’t needed anymore. Delete it q__o.

git branch -D specs

Main feed

On the the home page we’re going to create a posts feed. This feed is going to display all type of posts in a card format.

Start by creating a new branch:

git checkout -b main_feed

Generate a new model called Post .

rails g model post

Then we’ll need a Category model to categorize the posts:

rails g model category

Now let’s create some associations between User , Category and Post models.

Every post is going to belong to a category and its author (user). Open the models’ files and add the associations.

class Post < ApplicationRecord

belongs_to :user

belongs_to :category

end class User < ApplicationRecord

...

has_many :posts, dependent: :destroy

end class Category < ApplicationRecord

has_many :posts

end

The dependent: :destroy argument says, when a user gets deleted, all posts what the user has created will be deleted too.

Now we’ve to define data columns and associations inside the migrations files.

db/migrate/CREATION_DATE_create_posts.rb

db/migrate/CREATION_DATE_create_categories.rb

Now run the migration files:

rails db:migrate

Commit the changes:

git add -A

git commit -m "

- Generate Post and Category models.

- Create associations between User, Post and Category models.

- Create categories and posts database tables."

Specs

We can test the newly created models. Later we’ll need sample data for the tests. Since a post belongs to a category, we also need sample data for categories to set up the associations.

Create a category factory inside the factories directory.

spec/factories/categories.rb

spec/factories/categories.rb

Create a post factory inside the factories directory

spec/factories/posts.rb

spec/factories/posts.rb

As you see, it’s very easy to set up an association for factories. All we had to do to set up user and category associations for the post factory, is to write factories’ names inside the post factory.

Commit the changes.

git add -A

git commit -m "Add post and category factories"

For now we’ll only test the associations, because that’s the only thing we wrote yet inside the models.

Open the post_spec.rb

spec/models/post_spec.rb

Add specs for the associations, so the file should look like this:

spec/models/post_spec.rb

We use the described_class method to get the current context’s class. Which is basically the same as writing Post in this case. Then we use reflect_on_association method to check that it returns a correct association.

Do the same for other models.

spec/models/category_spec.rb

spec/models/user_spec.rb

Commit the changes.

git add -A

git commit -m "Add specs for User, Category, Post models' associations"

Home page layout

Currently the home page has nothing inside, only the dummy text “Home page”. It’s time to create its layout with bootstrap. Open the home page’s view file views/pages/index.html.erb and replace the file’s content with the following code to create the page’s layout:

views/pages/index.html.erb

Now add some CSS to define elements’ style and responsive behavior.

Inside the stylesheets/partials directory create a new file home_page.scss

assets/stylesheets/partials/home_page.scss

In the file add the following CSS:

assets/stylesheets/partials/home_page.scss

Inside the mobile.scss file’s max-width: 767px media query add:

assets/stylesheets/responsive/mobile.scss

Now the home page should look like this on bigger screens

and like this on the smaller screens

Commit the changes.

git add -A

git commit -m "

- Add the bootstrap layout to the home page

- Add CSS to make home page layout's stylistic and responsive design changes"

Seeds

To display posts on the home page, at first we need to have them inside the database. Creating data manually is boring and time consuming. To automate this process, we’ll use seeds. Open the seeds.rb file.

db/seeds.rb

Add the following code:

db/seeds.rb

As you see, we create seed_users , seed_categories and seed_posts methods to create User , Category and Post records inside the development database. Also the faker gem is used to generate dummy text. Add faker gem to your Gemfile

gem 'faker'

and

bundle install

To seed data, using the seeds.rb file, run a command

rails db:seed

Commit the changes.

git add -A

git commit -m "

- Add faker gem

- Inside the seeds.rb file create methods to generate

User, Category and Post records inside the development database"

Rendering the posts

To render the posts, we’ll need a posts directory inside the views.

Generate a new controller called Posts , so it will automatically create a posts directory inside the views too.

rails g controller posts

Since in our app the PagesController is responsible for the homepage, we’ll need to query data inside the pages_controller.rb file’s index action. Inside the index action retrieve some records from the posts table. Assign the retrieved records to an instance variable, so the retrieved objects are going to be available inside the home page’s views.

If you aren’t familiar with ruby variables, read this guide.

If you aren’t familiar with retrieving records from the database in Rails, read the Active Record Query Interface guide.

The index action should look something like this right now:

controllers/pages_controller.rb

Navigate to the home page’s template

views/pages/index.html.erb

and inside the .main-content element add

<%= render @posts %>

This will render all posts, which were retrieved inside the index action. Because post objects belong to the Post class, Rails automatically tries to render the _post.html.erb partial template which is located

views/posts/_post.html.erb

We haven’t created this partial file yet, so create it and add the following code inside:

views/posts/_post.html.erb

I’ve used a bootstrap card component here to achieve the desired style. Then I just stored post’s content and its path inside the element. Also I added a link which will lead to the full post.

So far we didn’t define any routes for posts. We need them right now, so let’s declare them. Open the routes.rb file and add the following code inside the routes:

routes.rb

Here I’ve used a resources method to declare routes for index , show , new , edit , create , update and destroy actions. Then I’ve declared some custom collection routes to access pages with multiple Post instances. These pages are going to be dedicated for separate branches, we’ll create them later.

Restart the server and go to http://localhost:3000. You should see rendered posts on the screen. The application should look similar to this:

Commit the changes.

git add -A

git commit -m "Display posts on the home page - Generate Posts controller and create an index action.

Inside the index action retrieve Post records

- Declare routes for posts

- Create a _post.html.erb partial inside posts directory

- Render posts inside the home page's main content"

To start styling posts, create a new scss file inside the partials directory:

assets/stylesheets/partials/posts.scss

and inside the file add the following CSS:

assets/stylesheets/partials/posts.scss

The home page should look similar to this:

Commit the change.

git add -A

git commit -m "Create a posts.scss file and add CSS to it"

Styling with JavaScript

Currently the site’s design is pretty dull. To create contrast, we’re going to color the posts. But instead of just coloring it with CSS, let’s color them with different color patterns every time a user refreshes the website. To do that we’ll use JavaScript. It’s probably a silly idea, but it’s fun c(o_u)?

Navigate to the javascripts directory inside your assets and create a new directory called posts . Inside the directory create a new file called style.js . Also if you want, you can delete by default generated .coffee , files inside the javascripts directory. We won’t use CoffeeScript in this tutorial.

assets/javascripts/posts/style.js

Inside the style.js file add the following code.

assets/javascripts/posts/style.js

With this piece of code we randomly set one of two style modes when a browser gets refreshed, by adding attributes to posts. One style has colored borders only, another style has solid color posts. With every page change and browser refresh we also recolor posts randomly too. Inside the randomColorSet() function you can see predefined color schemes.

mouseenter and mouseleave event handlers are going to be needed in the future for posts in specific pages. There posts’ style is going to be different than posts’ on the home page. When you’ll hover on a post, it will slightly change its bottom border’s color. You’ll see this later.

Commit the changes.

git add -A

git commit -m "Create a style.js file and add js to create posts' style"

To complement the styling, add some CSS. Open the posts.scss file

assets/stylesheets/partials/posts.scss

and add the following CSS:

assets/stylesheets/partials/posts.scss

Also inside the mobile.scss add the following code to fix too large text issues on smaller screens:

assets/stylesheets/responsive/mobile.scss

The home page should look similar to this right now:

Commit the changes

git add -A

git commit -m "Add CSS to posts on the home page - add CSS to the posts.scss file

- add CSS to the mobile.scss to fix too large text issues on smaller screens"

Modal window

I want to be able to click on a post and see its full content, without going to another page. To achieve this functionality I’ll use a bootstrap’s modal component.

Inside the posts directory, create a new partial file _modal.html.erb

views/posts/_modal.html.erb

and add the following code:

views/posts/_modal.html.erb

This is just a slightly modified bootstrap’s component to accomplish this particular task.

Render this partial at the top of the home page’s template.

views/pages/index.html.erb

To make this modal window functional, we have to add some JavaScript. Inside the posts directory, create a new file modal.js

assets/javascripts/posts/modal.js

Inside the file, add the following code:

assets/javascripts/posts/modal.js

With this js code we simply store selected post’s data into variables and fill modal window’s elements with this data. Finally, with the last line of code we make the modal window visible.

To enhance the modal window’s looks, add some CSS. But before adding CSS, let’s do a quick management task inside the stylesheets directory.

Inside the partials directory create a new directory called posts

assets/stylesheets/partials/posts

Inside the posts directory create a new file home_page.scss . Cut all code from the posts.scss file and paste it inside the home_page.scss file. Delete the posts.scss file. We’re doing this for a better CSS code management. It’s clearer when have few smaller CSS files with a distinguishable purpose, rather than one big file where everything is mashed together.

Also inside the posts directory, create a new file modal.scss and add the following CSS:

assets/stylesheets/partials/posts/modal.scss

Now when we click on the post, the application should look like this:

Commit the changes.

git add -A

git commit -m "Add a popup window to show a full post's content - Add bootstrap's modal component to show full post's content

- Render the modal inside the home page's template

- Add js to fill the modal with post's content and show it

- Add CSS to style the modal"

Also merge the main_feed branch with the master

git checkout master

git merge main_feed

Get rid of the main_feed branch

git branch -D main_feed

Single post

Switch to a new branch

git checkout -b single_post

Show a single post

If you try to click on the I'm interested button, you will get an error. We haven’t created a show.html.erb template nor we’ve created a corresponding controller’s action. By clicking the button I want to be redirected to a selected post’s page.

Inside the PostsController , create a show action and then query and store a specific post object inside an instance variable:

controllers/posts_controller.rb

I'm interested button redirects to a selected post. It has a href attribute with a path to a post. By sending a GET request to get a post, rails calls the show action. Inside the show action, we’ve an access to the id param, because by sending a GET request to get a specific post, we provided its id . I.e. by going to a /posts/1 path, we would send a request to get a post whose id is 1 .

Create a show.html.erb template inside the posts directory

views/posts/show.html.erb

Inside the file add the following code:

views/posts/show.html.erb

Create a show.scss file inside the posts directory and add CSS to style the page’s look:

assets/stylesheets/partials/posts/show.scss

Here I defined the page’s height to be 100vh-50px , so the page’s content is full viewport’s height. It allows the container to be colored white across the full browser’s height, no matter if there is enough of content inside the element or not. vh property means viewport’s height, so 100vh value means that the element is stretched 100% of viewport’s height. 100vh-50px is required to subtract navigation bar’s height, otherwise the container would be stretched too much by 50px .

If you click on the I'm interested button now, you will be redirected to a page which looks similar to this:

We’ll add extra features to the show.html.erb template later. Now commit the changes.

git add -A

git commit -m "Create a show template for posts - Add a show action and query a post to an instance variable

- Create a show.scss file and add CSS"

Specs

Instead of manually checking that this functionality, of modal window appearance and redirection to a selected post, works, wrap it all with specs. We’re going to use capybara to simulate a user’s interaction with the app.

Inside the features directory, create a new directory called posts

spec/features/posts

Inside the new directory, create a new file visit_single_post_spec.rb

spec/features/posts/visit_single_post_spec.rb

And add a feature spec inside. The file looks like this:

spec/features/posts/visit_single_post_spec.rb

Here I defined all steps which I would perform manually. I start by going to the home page, click on the post, expect to see the popped up modal window, click on the I'm interested button, and finally, expect to be redirected to the post’s page and see its content.

By default RSpec matchers have_selector , have_css , etc. return true if an element is actually visible to a user. So after it was clicked on a post, testing framework expects to see a visible modal window. If you don’t care if a user sees an element or not and you just care about an element’s presence in the DOM, pass an additional visible: false argument.

Try to run the test

rspec spec/features/posts/visit_single_post_spec.rb

Commit the changes.

git add -A

git commit -m "Add a feature spec to test if a user can go to a

single post from the home page"

Merge the single_post branch with the master .

git checkout master

git merge single_post

git branch -D single_post

Specific branches

Every post belongs to a particular branch. Let’s create specific pages for different branches.

Switch to a new branch

git checkout -b specific_branches

Home page’s side menu

Start by updating the home page’s side menu. Add links to specific branches. Open the index.html.erb file:

views/pages/index.html.erb

We are going to put some links inside the #side-menu element. Split file’s content into partials, otherwise it will get noisy very quickly.

Cut #side-menu and #main-content elements, and paste them into separate partial files. Inside the pages directory create an index directory, and inside the directory create corresponding partial files to the elements. The files should look like this:

views/pages/index/_side_menu.html.erb

views/pages/index/_main_content.html.erb

Render those partial files inside the home page’s template. The file should look like this:

views/pages/index.html.erb

Commit the changes.

git add -A

git commit -m "Split home page template's content into partials"

Inside the _side_menu.html.erb partial add a list of links, so the file should look like this:

views/pages/index/_side_menu.html.erb

An unordered list was added. Inside the list we render another partial with links. Those links are going to be available for all users, no matter if they are signed in or not. Create this partial file and add the links.

Inside the index directory create a side_menu directory:

views/pages/index/side_menu

Inside the directory create a _no_login_required_links.html.erb partial with the following code:

views/pages/index/side_menu/_no_login_required_links.html.erb

Here we simply added links to specific branches of posts. If you are wondering how do we have paths, such as hobby_posts_path , etc., look at the routes.rb file. Previously we’ve added nested collection routes inside the resources :posts declaration.

If you pay attention to i elements’ attributes, you will notice fa classes. With those classes we declare Font Awesome icons. We haven’t set up this library yet. Fortunately, it’s very easy to set up. Inside the main application.html.erb file’s head element, add the following line

The side menu should be present now.

Commit the changes.

git add -A

git commit -m "Add links to the home page's side menu"

On smaller screens, where width is between 767px and 1000px , bootstrap’s container looks unpleasant, it looks overly compressed. So stretch it among those widths. Inside the mobile.scss file, add the following code:

assets/stylesheets/responsive/mobile.scss

Commit the change.

git add -A

git commit -m "set .container width to 100%

when viewport's width is between 767px and 1000px"

Branch page

If you try to click on one of those side menu links, you will get an error. We haven’t set up actions inside the PostsController nor we created any templates for it.

Inside the PostsController , define hobby , study , and team actions.

controllers/posts_controller.rb

Inside every action, posts_for_branch method is called. This method will return data for the specific page, depending on the action’s name. Define the method inside the private scope.

contorllers/posts_controller.rb

In the @categories instance variable we retrieve all categories for a specific branch. I.e. if you go to the hobby branch page, all categories which belong to the hobby branch will be retrieved.

To get and store posts inside the @posts instance variable, get_posts method is used and then it is chained with a paginate method. paginate method comes from will_paginate gem. Let’s start by defining the get_posts method. Inside the PostsController ’s private scope add:

controllers/posts_controller.rb

Right now get_posts method just retrieves any 30 posts, not specific to anything, so we could move on and focus on further development. We’ll come back to this method in the near future.

Add the will_paginate gem to be able to use pagination.

gem 'will_paginate', '~> 3.1.0'

run

bundle install

All we miss now is templates. They are going to be similar to all branches, so instead of repeating the code, inside every of those branches, create a partial with a general structure for a branch. Inside the posts directory create a _branch.html.erb file.

posts/_branch.html.erb

First you see a page_title variable being printed on the page. We’ll pass this variable as an argument when we’ll render the _branch.html.erb partial. Next, a _create_new_post partial is rendered to display a link, which will lead to a page, where a user could create a new post. Create this partial file inside a new branch directory:

posts/branch/_create_new_post.html.erb

Here we’ll use a create_new_post_partial_path helper method to determine which partial file to render. Inside the posts_helper.rb file, implement the method:

helpers/posts_helper.rb

Also create those two corresponding partials inside a new create_new_post directory:

posts/branch/create_new_post/_signed_in.html.erb

posts/branch/create_new_post/_not_signed_in.html.erb

Next, inside the _branch.html.erb file we render a list of categories. Create a _categories.html.erb partial file:

posts/branch/_categories.html.erb

Inside the file, we have a all_categories_button_partial_path helper method which determines which partial file to render. Define this method inside the posts_helper.rb file:

helpers/posts_helper.rb

All categories are going to be selected by default. If the params[:category] is empty, it means that none categories were selected by a user, which means that currently the default value all is selected. Create the corresponding partial files:

posts/branch/categories/_all_selected.html.erb

posts/branch/categories/_all_not_selected.html.erb

The send method is used here to call a method by using a string, this allows to be flexible and call methods dynamically. In our case we generate different paths, depending on the current controller’s action.

Next, inside the _branch.html.erb file we render posts and call the no_posts_partial_path helper method. If posts are not found, the method will display a message.

Inside the posts_helper.rb add the helper method:

helpers/posts_helper.rb

Here I use a ternary operator, so the code looks a little bit cleaner. If there are any posts, I don’t want to show any messages. Since you cannot pass an empty string to the render method, I pass a path to an empty partial instead, in occasions where I don’t want to render anything.

Create a shared directory inside the views and then create an empty partial:

views/shared/_empty_partial.html.erb

Now create a _no_posts.html.erb partial for the message inside the branch directory.

posts/branch/_no_posts.html.erb

Finally, we use the will_paginate method from the gem to split posts into multiple pages if there are a lot of posts.

Create templates for hobby , study and team actions. Inside them we’ll render the _branch.html.erb partial file and pass specific local variables.

posts/hobby.html.erb

posts/study.html.erb

posts/team.html.erb

If you go to any of those branch pages, you will see something like this

Also if you scroll down, you will see that now we have a pagination

We’ve done quite a lot of work to create these branch pages. Commit the changes

git add -A

git commit -m "Create branch pages for specific posts - Inside the PostsController define hobby, study and team actions.

Define a posts_for_branch method and call it inside these actions

- Add will_paginate gem

- Create a _branch.html.erb partial file

- Create a _create_new_post.html.erb partial file

- Define a create_new_post_partial_path helper method

- Create a _signed_in.html.erb partial file

- Create a _not_signed_in.html.erb partial file

- Create a _categories.html.erb partial file

- Define a all_categories_button_partial_path helper method

- Create a _all_selected.html.erb partial file

- Create a _all_not_selected.html.erb partial file

- Define a no_posts_partial_path helper method

- Create a _no_posts.html.erb partial file

- Create a hobby.html.erb template file

- Create a study.html.erb template file

- Create a team.html.erb template file"

Specs

Cover helper methods with specs. The posts_helper_spec.rb file should look like this:

spec/helpers/posts_helper_spec.rb

Again, specs are pretty simple here. I used the stub method to define methods’ return values. To define params, I selected the controller and simply defined it like this controller.params[:param_name] . And finally, I assigned instance variables by using an assign method.

Commit the changes

git add -A

git commit -m "Add specs for PostsHelper methods"

Design changes

In these branch pages we want to have different posts’ design. In the home page we have the cards design. In branch pages let’s create a list design, so a user could see more posts and browse through them more efficiently.

Inside the posts directory, create a post directory with a _home_page.html.erb partial inside.

posts/post/_home_page.html.erb

Cut the _post.html.erb partial’s content and paste it inside the _home_page.html.erb partial file. Inside the _post.html.erb partial file add the following line of code:

posts/_post.html.erb

Here we call the post_format_partial_path helper method to decide which post design to render, depending on the current path. If a user is on the home page, render the post design for the home page. If a user is on the branch page, render the post design for the branch page. That’s why we cut _post.html.erb file’s content into _home_page.html.erb file.

Inside the post directory, create a new _branch_page.html.erb file and paste this code to define the posts design for the branch page.

posts/post/_branch_page.html.erb

To decide which partial file to render, define the post_format_partial_path helper method inside the posts_helper.rb

helpers/posts_helper.rb

The post_format_partial_path helper method won’t be available in the home page, because we render posts inside the home page’s template, which belongs to a different controller. To have an access to this method, inside the home page’s template, include PostsHelper inside the ApplicationHelper

include PostsHelper

Specs

Add specs for the post_format_partial_path helper method:

helpers/posts_helper_spec.rb

Commit the changes

git add -A

git commit -m "Add specs for the post_format_partial_path helper method"

CSS

Describe the posts style in branch pages with CSS. Inside the posts directory, create a new branch_page.scss style sheet file:

stylesheets/partials/posts/branch_page.scss

Inside the base/default.scss add:

assets/stylesheets/base/default.scss

To fix style issues on smaller devices, inside the responsive/mobile.scss add:

assets/stylesheets/responsive/mobile.scss

Now the branch pages should look like this:

Commit the changes.

git add -A

git commit -m "Describe the posts style in branch pages - Create a branch_page.scss file and add CSS

- Add CSS to the default.scss file

- Add CSS to the mobile.scss file"

Search bar

We want not only be able to browse through posts, but also search for specific ones. Inside the _branch.html.erb partial file, above the categories row, add:

posts/_branch.html.erb

Create a _search_form.html.erb partial file inside the branch directory and add the following code inside:

posts/branch/_search_form.html.erb

Here with the send method we dynamically generate a path to a specific PostsController ’s action, depending on a current branch. Also we send an extra data field for the category if a specific category is selected. If a user has selected a specific category, only search results from that category will be returned.

Define the category_field_partial_path helper method inside the posts_helper.rb

helpers/posts_helper.rb

Create a _category_field.html.erb partial file and add the code:

posts/branch/search_form/_category_field.html.erb

To give the search form some style, add CSS to the branch_page.scss file:

assets/stylesheets/partials/posts/branch_page.scss

The search form, in branch pages, should look likes this now

Commit the changes

git add -A

git commit -m "Add a search form in branch pages - Render a search form inside the _branch.html.erb

- Create a _search_form.html.erb partial file

- Define a category_field_partial_path helper method in PostsHelper

- Create a _category_field.html.erb partial file

- Add CSS for the the search form in branch_page.scss"

Currently our form isn’t really functional. We could use some gems to achieve search functionality, but our data isn’t complicated, so we can create our own simple search engine. We’ll use scopes inside the Post model to make queries chainable and some conditional logic inside the controller (we will extract it into service object in the next section to make the code cleaner).

Start by defining scopes inside the Post model. To warm up, define the default_scope inside the post.rb file. This orders posts in descending order by the creation date, newest posts are at the top.

models/post.rb

Commit the change

git add -A

git commit -m "Define a default_scope for posts"

Make sure that the default_scope works correctly by wrapping it with a spec. Inside the post_spec.rb file, add:

spec/models/post_spec.rb

Commit the change:

git add -A

git commit -m "Add a spec for the Post model's default_scope"

Now let’s make the search bar functional. Inside the posts_controller.rb replace the get_posts method’s content with:

controllers/posts_controller.rb

As I’ve mentioned a little bit earlier, logic, just like in views, isn’t really a good place in controllers. We want to make them clean. So we’ll extract the logic out of this method in the upcoming section.

As you see, there is some conditional logic going on. Depending on a user request, data gets queried differently using scopes.

Inside the Post model, define those scopes:

models/post.rb

The joins method is used to query records from the associated tables. Also the basic SQL syntax is used to find records, based on provided strings.

Now if you restart the server and go back to any of those branch pages, the search bar should work! Also now you can filter posts by clicking on category buttons. And also when you select a particular category, only posts from that category are queried when you use the search form.

Commit the changes

git add -A

git commit -m "Make search bar and category filters

in branch pages functional - Add by_category, by_branch and search scopes in the Post model

- Modify the get_posts method in PostsController"

Cover these scopes with specs. Inside the post_spec.rb file’s Scopes context add:

spec/models/post_spec.rb

Commit the changes

git add -A

git commit -m "Add specs for Post model's

by_branch, by_category and search scopes"

Infinite scroll

When you go to any of these branch pages, at the bottom of the page you see the pagination

When you click on the next link, it redirects you to another page with older posts. Instead of redirecting to another page with older posts, we can make an infinite scrolling functionality, similar to the Facebook’s and Twitter’s feed. You just scroll down and without any redirection and page reload, older posts are appended to the bottom of the list. Surprisingly, it is very easy to achieve. All we have to do is write some JavaScript. Whenever a user reaches the bottom of the page, AJAX request is sent to get data from the next page and that data gets appended to the bottom of the list.

Start by configuring the AJAX request and its conditions. When a user passes a certain threshold by scrolling down, AJAX request gets fired. Inside the javascripts/posts directory, create a new infinite_scroll.js file and add the code:

assets/javascripts/posts/infinite_scroll.js

The isLoading variable makes sure that only one request is sent at a time. If there is currently a request in progress, other requests won’t be initiated.

First check if pagination is present, if there are any more posts to render. Next, get a link to the next page, this is where the data will be retrieved from. Then set a threshold when to call an AJAX request, in this case the threshold is 60px from the bottom of the window. Finally, if all conditions successfully pass, load data from the next page using the getScript() function.

Because the getScript() function loads the JavaScript file, we have to specify which file to render inside the PostsController . Inside the posts_for_branch method specify respond_to formats and which files to render.

controllers/posts_controller.rb

When the controller tries to respond with the .js file, the posts_pagination_page template gets rendered. This partial file appends newly retrieved posts to the list. Create this file to append new posts and update the pagination element.

posts/_posts_pagination_page.js.erb

Create an update_pagination_partial_path helper method inside the posts_helper.rb

helpers/posts_helper.rb

Here the next_page method from the will_paginate gem is used, to determine if there are any more posts to load in the future or not.

Create the corresponding partial files:

posts/posts_pagination_page/_update_pagination.js.erb

posts/posts_pagination_page/_remove_pagination.js.erb

If you go to any of the branch pages and scroll down, older posts should be automatically appended to the list.

Also we no longer need to see the pagination menu, so hide it with CSS. Inside the branch_page.scss file add:

stylesheets/partials/posts/branch_page.scss

Commit the changes

git add -A

git commit -m "Transform posts pagination into infinite scroll - Create an infinite_scroll.js file

- Inside PostController's posts_for_branch method add respond_to format

- Define an update_pagination_partial_path

- Create _update_pagination.js.erb and _remove_pagination.js.erb partials

- hide the .infinite-scroll element with CSS"

Specs

Cover the update_pagination_partial_path helper method with specs:

spec/helpers/post_helper_spec.rb

Here I’ve used a test double to simulate the posts instance variable and its chained method next_page . You can learn more about the RSpec Mocks here.

Commit the changes:

git add -A

git commit -m "Add specs for the update_pagination_partial_path

helper method"

We can also write feature specs to make sure that posts are successfully appended, after you scroll down. Create an infinite_scroll_spec.rb file:

spec/features/posts/infinite_scroll_spec.rb

In the spec file all branch pages are covered. We make sure that this functionality works on all three pages. The per_page is will_paginate gem’s method. Here the Post model is selected and the default number of posts per page is set.

The check_posts_count method is defined to reduce the amount of code the file has. Instead of repeating the same code over and over again in different specs, we extracted it into a single method. Once the page is visited, it is expected to see 15 posts. Then the execute_script method is used to run JavaScript, which scrolls the scrollbar to the browser’s bottom. Finally, after the scroll, it is expected to see an additional 15 posts. Now in total there should be 30 posts on the page.

Commit the changes:

git add -A

git commit -m "Add feature specs for posts' infinite scroll functionality"

Home page update

Currently on the home page we can only see few random posts. Modify the home page, so we could see a few posts from all branches.

Replace the _main_content.html.erb file’s content with:

pages/index/_main_content.html.erb

We created sections with posts for every branch.

Define instance variables inside the PagesController ’s index action. The action should look like this:

controllers/pages_controller.rb

We have the no_posts_partial_path helper method from before, but we should modify it a little bit and make it more reusable. Currently it works only for branch pages. Add a posts parameter to the method, so it should look like this now:

helpers/posts_helper.rb

Here the posts parameter was added, instance variable was changed to a simple variable and the partial’s path was changed too. So move the _no_posts.html.erb partial file from

posts/branch/_no_posts.html.erb

to

posts/shared/_no_posts.html.erb

Also inside the _branch.html.erb file pass the @posts instance variable to the no_posts_partial_path method as an argument.

Add some style changes. Inside the default.scss file add:

assets/stylesheets/base/default.scss

And inside the home_page.scss add:

assets/stylesheets/partials/home_page.scss

The home page should look similar to this right now

Commit the changes

git add -A

git commit -m "Add posts from all branches in the home page - Modify the _main_content.html.erb file

- Define instance variables inside the PagesController ’s index action

- Modify the no_posts_partial_path helper method to be more reusable

- Add CSS to style the home page"

Service objects

As I’ve mentioned before, if you put logic inside controllers, they become complicated very easily and a huge pain to test. That’s why it’s a good idea to extract logic from them somewhere else. To do that I use design patterns, service objects (services) to be more specific.

Right now inside the PostsController , we have this method:

controllers/posts_controller.rb

It has a lot of conditional logic which I want to remove by using services. Service objects (services) design pattern is just a basic ruby class. It’s very simple, we just pass data which we want to process and call a defined method to get a desired return value.

In ruby we pass data to Class’s initialize method, in other languages it’s known as the constructor . And then inside the class, we just create a method which will handle all defined logic. Let’s create that and see how it looks in code.

Inside the app directory, create a new services directory:

app/services

Inside the directory, create a new posts_for_branch_service.rb file:

services/posts_for_branch_service.rb

Here, as described above, it is just a plain ruby class with an initialize method to accept parameters and a call method to handle the logic. We took this logic from the get_posts method.

Now simply create a new object of this class and call the call method inside the get_posts method. The method should look like this right now:

controllers/posts_controller.rb

Commit the changes:

git add -A

git commit -m "Create a service object to extract logic

from the get_posts method"

Specs

A fortunate thing about design patterns, like services, is that it’s easy to write unit tests for it. We can simply write specs for the call method and test each of its conditions.

Inside the spec directory create a new services directory:

spec/services

Inside the directory create a new file posts_for_branch_service_spec.rb

spec/services/posts_for_branch_service_spec.rb

At the top of the file, the posts_for_branch_service.rb file is loaded and then each of the call method’s conditions are tested.

Commit the changes

git add -A

git commit -m "Add specs for the PostsForBranchService"

Create a new post

Until now posts were created artificially, by using seeds. Let’s add a user interface for it, so a user could create posts.

Inside the posts_controller.rb file add new and create actions.

controllers/posts_controller.rb

Inside the new action, we define some instance variables for the form to create new posts. Inside the @categories instance variable, categories for a specific branch are stored. The @post instance variable stores an object of a new post, this is needed for the Rails form.

Inside the create action’s @post instance variable, we create a new Post object and fill it with data, using the post_params method. Define this method within the private scope:

controllers/posts_controller.rb

The permit method is used to whitelist attributes of the object, so only these specified attributes are allowed to be passed.

Also at the top of the PostsController , add the following line:

controllers/posts_controller.rb

The before_action is one of the Rails filters. We don’t want to allow for not signed in users to have an access to a page where they can create new posts. So before calling the new action, the redirect_if_not_signed_in method is called. We’ll need this method across other controllers too, so define it inside the application_controller.rb file. Also a method to redirect signed in users would be useful in the future too. So define them both.

controllers/application_controller.rb

Now the new template is required, so a user could create new posts. Inside the posts directory, create a new.html.erb file:

posts/new.html.erb

Create a new directory and a _post_form.html.erb partial file inside:

posts/new/_post_form.html.erb

The form is pretty straightforward. Attributes of the fields are defined and the collection_select method is used to allow to select one of the available categories.

Commit the changes

git add -A

git commit -m "Create a UI to create new posts - Inside the PostsController:

define new and create actions

define a post_params method

define a before_action filter

- Inside the ApplicationController:

define a redirect_if_not_signed_in method

define a redirect_if_signed_in method

- Create a new template for posts"

We can test if the form works by writing specs. Start by writing request specs, to make sure that we get correct responses after we send particular requests. Inside the spec directory create a couple directories.

spec/requests/posts

And a new_spec.rb file inside:

spec/requests/posts/new_spec.rb

As mentioned in the documentation, request specs provide a thin wrapper around the integration tests. So we test if we get correct responses when we send certain requests. The include Warden::Test::Helpers line is required in order to use login_as method. The method logs a user in.

Commit the change.

git add -A

git commit -m "Add request specs for a new post template"

We can even add some more request specs for the pages which we created previously.

Inside the same directory create a branches_spec.rb file:

spec/requests/posts/branches_spec.rb

This way we check that all branch pages’ templates successfully render. Also the shared_examples is used to reduce the repetitive code.

Commit the change.

git add -A

git commit -m "Add request specs for Posts branch pages' templates"

Also we can make sure that the show template renders successfully. Inside the same directory create a show_spec.rb file:

spec/requests/posts/show_spec.rb

Commit the changes.

git add -A

git commit -m "Add request specs for the Posts show template"

To make sure that a user is able to create a new post, write feature specs to test the form. Inside the features/posts directory create a new file create_new_post_spec.rb

spec/features/posts/create_new_post_spec.rb

Commit the changes.

git add -A

git commit -m "Create a create_new_post_spec.rb file with feature specs"

Apply some design to the new template.

Within the following directory:

assets/stylesheets/partials/posts

Create a new.scss file:

assets/stylesheets/partials/posts/new.scss

If you go to the template in a browser now, you should see a basic form

Commit the changes

git add -A

git commit -m "Add CSS to the Posts new.html.erb template"

Finally, we want to make sure that all fields are filled correctly. Inside the Post model we’re going to define some validations. Add the following code to the Post model:

models/post.rb

Commit the changes.

git add -A

git commit -m "Add validations to the Post model"

Cover these validations with specs. Go to the Post model’s spec file:

spec/models/post_spec.rb

Then add:

spec/models/post_spec.rb

Commit the changes.

git add -A

git commit -m "Add specs for the Post model's validations"

Merge the specific_branches branch with the master

git checkout -b master

git merge specific_branches

git branch -D specific_branches

Instant Messaging

Users are able to publish posts and read other users’ posts, but they have no ability to communicate with each other. We could create a simple mail box system, which would be much easier and faster to develop. But that is a very old way to communicate with someone. Real time communication is much more exciting to develop and comfortable to use.

Fortunately, Rails has Action Cables which makes real time features’ implementation relatively easy. The core concept behind the Action Cables is that it uses a WebSockets Protocol instead of HTTP. And the core concept of WebSockets is that it establishes a client-server connection and keeps it open. This means that no page reloads are required to send and receive additional data.

Private conversation

The goal of this section is to create a working feature which would allow to have a private conversation between two users.

Switch to a new branch

git checkout -B private_conversation

Namespacing models

Start by defining necessary models. We’ll need two different models for now, one for private conversations and another for private messages. We could name them PrivateConversation and PrivateMessage , but you can quickly encounter a little problem. While everything would work fine, imagine how the models directory would start to look like after we create more and more models with similar name prefixes. The directory would become hardly manageable in no time.

To avoid chaotic structure inside directories, we can use a namespacing technique.

Let’s see how it would look like. An ordinary model for private conversation would be called PrivateConversation and its file would be called private_conversation.rb , and stored inside the models directory

models/private_conversation.rb

Meanwhile, the namespaced version would be called Private::Conversation . The file would be called conversation.rb and located inside the private directory

models/private/conversation.rb

Can you see how it might be useful? All files with the private prefix would be stored inside the private directory, instead of accumulating inside the main models directory and making it hardly to read.

As usually, Rails makes the development process enjoyable. We’re able to create namespaced models by specifying a directory which we want to put a model in.

To create the namespaced Private::Conversation model run the following command:

rails g model private/conversation

Also generate the Private::Message model:

rails g model private/message

If you look at the models directory, you will see a private.rb file. This is required to add prefix to database tables’ names, so models could be rec