Learning the ways with Rails

Franklin Bado
8 min readSep 3, 2020

--

Creating an application on rails can be simple as doing rails new application-name and after some auto commands and installing some packages on the terminal, you can check-in to your application and run rails server, and if you have the correct gems you should be able to see your application in the browser by using http://localhost:3000/

but we still need to build all the logic and super magic that we want for the application.

I am currently attending Flatiron Bootcamp, and I always being told to follow conventions, so in this blog I will walk you through my own personal approach when building the components for my applications. I am sure there are many others efficient and better built overall, but as of right now I am learning my ways with rails. These are the conventions I follow.

1- Creating migrations

~/.../tutoring_session/tutoring_session // 🧟😎👉 > rails g migration CreateStudents
Running via Spring preloader in process 10471
invoke active_record
create db/migrate/20200902031510_create_students.rb

By running rails g (rails generate) migration, rails provides me with a new migration file, and in there I can write the attributes for the table just initiated.

db/migrate/

My next step is to run rails db:migrate, this will create the table, as well as a new friend Schema for the database

~/.../tutoring_session/tutoring_session // 🧟😎👉 > rails db:migrate
== 20200902031510 CreateStudents: migrating ===================================
-- create_table(:students)
-> 0.0045s
== 20200902031510 CreateStudents: migrated (0.0046s) ==========================

2- Creating models

I open the folder named models, and create a file in there as student.rb, which I will use as the student model to write my associations.

3- Creating controllers

Let’s expand on the idea of rails g to helps us create the controller

~/.../tutoring_session/tutoring_session // 🧟😎👉 > rails g controller students --no-test-framework
Running via Spring preloader in process 10869
create app/controllers/students_controller.rb
invoke erb
create app/views/students
invoke helper
create app/helpers/students_helper.rb
invoke assets
invoke scss
create app/assets/stylesheets/students.scss

This time rails provide us with not just the controller but also a folder called students in the view folder, 🙉 already more efficient.

4- Creating routes

When creating routes, rails is expecting to have the HHTP verb, the url, the controller and method pointing-to, and it is a good idea to add a path to make things easier on the eye. But what is all this?
A great guide on how to write routes is RESTULAR

Indeed rails has your back, we can use a command called rails g resource that will generate steps 1 through 4 at once 😎.
I personally think this is great news, since I can avoid human errors when writing the structure for the application.

~/.../tutoring_session/tutoring_session // 🧟😎👉 > rails g resource Coach name experience_years:integer --no-test-framework
Running via Spring preloader in process 11375
invoke active_record
create db/migrate/20200902033106_create_coaches.rb
create app/models/coach.rb
invoke controller
create app/controllers/coaches_controller.rb
invoke erb
create app/views/coaches
invoke helper
create app/helpers/coaches_helper.rb
invoke assets
invoke scss
create app/assets/stylesheets/coaches.scss
invoke resource_route
route resources :coaches

Now I will generate the joiner table with the belongs_to syntax

~/.../tutoring_session/tutoring_session // 🧟😎👉 > rails g resource Tutoring_time appointment_time:datetime student:belongs_to coach:belongs_to
Running via Spring preloader in process 11458
invoke active_record
create db/migrate/20200902033503_create_tutoring_times.rb
create app/models/tutoring_time.rb
invoke test_unit
create test/models/tutoring_time_test.rb
create test/fixtures/tutoring_times.yml
invoke controller
create app/controllers/tutoring_times_controller.rb
invoke erb
create app/views/tutoring_times
invoke test_unit
create test/controllers/tutoring_times_controller_test.rb
invoke helper
create app/helpers/tutoring_times_helper.rb
invoke test_unit
invoke assets
invoke scss
create app/assets/stylesheets/tutoring_times.scss
invoke resource_route
route resources :tutoring_times

One thing to know is that when running these commands, we can avoid creating test_units by adding — no-test-framework to the end of each rails g command.

If I look at the schema now, I will see that the set-up for this application is done and I can move on to models to plan out my associations

table coaches, and tutoring_times were initialized with rails g resource, table students was initialized with rail g migration

Model associations

class Coach < ApplicationRecord
has_many :tutoring_times
has_many :students, through: :tutoring_times
end
class Student< ApplicationRecord
has_many :tutoring_times
has_many :coaches, through: :tutoring_times
end
class TutoringTime < ApplicationRecord
belongs_to :student
belongs_to :coach
end

Note: I only had to write the associations for the first two models, for TutoringTime the associations were created with rails g resource.

At this point, I will open my rails console and just test my associations by creating instances of each model, — As an alternative I can test the data by seeding it, but for this application I chose to do all in the rails console.

2.6.1 :007 > Student.first
Student Load (0.2ms) SELECT "students".* FROM "students" ORDER BY "students"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<Student id: 1, name: "Franklin", language: "ruby", age: 12>
2.6.1 :008 > Coach.last
Coach Load (0.3ms) SELECT "coaches".* FROM "coaches" ORDER BY "coaches"."id" DESC LIMIT ? [["LIMIT", 1]]
=> #<Coach id: 1, name: "Marlon", experience_years: 1, created_at: "2020-09-02 17:41:42", updated_at: "2020-09-02 17:41:42">
2.6.1 :009 > TutoringTime.last
TutoringTime Load (0.3ms) SELECT "tutoring_times".* FROM "tutoring_times" ORDER BY "tutoring_times"."id" DESC LIMIT ? [["LIMIT", 1]]
=> #<TutoringTime id: 1, appointment_time: nil, student_id: 1, coach_id: 1, created_at: "2020-09-02 17:43:23", updated_at: "2020-09-02 17:43:23">
2.6.1 :010 > Student.first.tutoring_times.first.coach.name
Student Load (0.3ms) SELECT "students".* FROM "students" ORDER BY "students"."id" ASC LIMIT ? [["LIMIT", 1]]
TutoringTime Load (0.6ms) SELECT "tutoring_times".* FROM "tutoring_times" WHERE "tutoring_times"."student_id" = ? ORDER BY "tutoring_times"."id" ASC LIMIT ? [["student_id", 1], ["LIMIT", 1]]
Coach Load (0.2ms) SELECT "coaches".* FROM "coaches" WHERE "coaches"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> "Marlon"
2.6.1 :011 >

Let’s move into the controllers and views and write all the magic we want to display in this application.

Target each method at the time, I first write the index method and test it to ensure things are working properly.

class StudentsController < ApplicationController
def index
@students = Student.all
end
end

Now when I visit http://localhost:3000/students, I see the following

This is telling me that I need a route for students, so lets add it on the routes.rb

get '/students', to: 'students#index', as: 'students'

If I reload the browser now I get the following

rails tells me that I don’t have a view for the index method

in app > views > students > index.html.erb

<h1> Welcome students</h1><ul>
<li><% @students.map do |student_info| %> Student Name: <%=student_info.name %> - Language: <%=student_info.language %></li><br>
<% end %>
</ul>

and now if I refresh my browser I will see the students I have (if I have created any)

Now that I have defined the index method, the next on the list is show, and then I move into new method followed by create, and then the edit and update, and lastly destroy.

class StudentsController < ApplicationController  def index
@students = Student.all
end
def show
@student = Student.find(params[:id])
end
def new
@student = Student.new
end
def create
@student = Student.create(student_params)
redirect_to student_path(@student)
end
def edit
@student = Student.find(params[:id])
end
def update
@student = Student.find(params[:id])
@student.update(student_params)
redirect_to student_path(@student)
end
private def student_params
params.require(:student).permit(:name, :age, :language)
end
end Here are my views index.html.erb<div class='content'>
<h1> List of current Students</h1>
<% @students.map do |student_info| %>
<p>Student Name: <strong><%=link_to student_info.name, student_path(student_info) %></strong>
- Language: <strong><%=student_info.language %></strong> </p>
<% end %>
<br><br><br>
<span class='button'><%= link_to "Create a New Student", new_student_path %></span>
</div>
show.html.erb<div class='content'>
<h1><%=@student.name %>'s page</h1>
<p>Language: <strong><%= @student.language %></strong></p>
<p>Age: <%= @student.age %></p>
<% if @student.tutoring_times.count > 0 %>
<h2>Upcoming appointments</h2>
<p><% @student.tutoring_times.map do |session| %>
<%= link_to session.id, tutoring_time_path(session.id) %>.
Coach: <strong><%= link_to session.coach.name, coach_path(session.coach)%></strong> -
Date and Time: <strong><%= session.appointment_time %></strong></p>
<% end %>
<% else %>
<p> Current Student has no appointments booked yet</p>
<% end %>
<br><br>
<span class='button'> <%=link_to "CREATE A TUTORING SESSION", new_tutoring_time_path %></span>
</div>
new.html.erb<div class='content'>
<h1> New Student Form</h1>
<%= form_for @student do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :language %>
<%= f.text_field :language %>
<%= f.label :age %>
<%= f.number_field :age %> <br><br>
<%=f.submit "New Student" %>
<% end %>
</div>
edit.html.erb<div class='content'>
<h1> Update Student Form</h1>
<%= form_for @student do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :language %>
<%= f.text_field :language %>
<%= f.label :age %>
<%= f.number_field :age %> <br><br>
<%=f.submit "Update Student" %>
<% end %>
</div>
Coaches Controller
TutoringTime Controller — Joiner
TutoringTime index view
TutoringTime show view
TutoringTime new view
TutoringTime edit view
Here is a quick demo on what the application can do at this point

Things I keep on mind when building the application

  • Based on the routes, I only need to create views for the the routes that use ‘get’, therefore I only did views for index, show, new, edit.
  • Follow convention, when defining a method, I can potentially render it to a specific view for instance I can render the index method to a view call 903tcR.html.erb, but this can get confusing when someone else is viewing my code.
  • Test as you go. I find it easier to fix bugs as I go along, than waiting until I have finished the application and then revisiting my first lines of code.

This tiny application has CRUD -Create_Read_Update_Destroy for the joiner model, and some logic implemented for the others such as creating instances, reading them and updating them.

There is so much more that we can build with rails. At this point I have only scratched the surface. But following conventions, and understanding the mere basics, it is essential for me to continue building.

The next things to tackle on my agenda will be to use validations, to implement more logic for the associations I have. Such as, making logins for students and coaches, and limiting the access at both ends. And of course, adding css and styling the application. But that, my friends, will be a topic for another blog.

--

--

Franklin Bado

Fellowship Student at Flatiron School, detailed-oriented learning code one step at the time. I enjoy learning how things work, and improving their functionality