Building Ruby on Rails from Scratch
Day 1: From Nothing to Something
- Day 1: From Nothing to Something
- Day 2: More Things about Relations, Tests, etc.
- Day 3: My Thoughts on Ruby
I have changed my job a while ago, so I will take some time to pick up the architecture and services of the new organization. However, in order to practice my English and take notes, I will still try to post as often as possible once a week, but the content will be different from the past, from an introduction to software architecture to my Ruby learning experience.
Because the programming language used in my new job is Ruby, and I have no experience with Ruby before. Therefore, this series will focus on the perspective of someone who already knows multiple languages and how to quickly get started with Ruby on Rails, not to write perfect Ruby, but to be able to quickly build a workable application. By the way, it’s a bit like how I learned English, there’s no structure at all, it just works and can be communicated anyway.
Installation
My work machine is a MacPro M1, so all operations will be Mac-based. M1 is also a very important keyword, and I stepped on several traps on M1, which will be described in a later article.
Because I need to install many versions of Ruby, I use a similar solution to nvm
, rvm
.
To install rvm on a Mac, we first have to install gnupg.
$ brew install gnupg
Then just follow the steps on the official website.
$ gpg — recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
$ curl -sSL https://get.rvm.io | bash
$ rvm install 2.7
$ rvm 2.7 — default
Finally, just install Rails. I know there are newer versions of Rails already available, but the resources I have on hand are based on 5.x
, so I chose to install 5.2.8
.
$ gem install rails -v 5.2.8
Start building a Rails application
First, let me describe my goal. I want to make a simple web store with two objects, user and store, where the relationship between these two objects is a one-to-one mapping. Also, using MongoDB instead of the built-in SQLite.
Building a Rails application can be done easily with scaffold
, which provides a fully resource-based, standard RESTful API.
Start by creating a project.
$ rails new hello_rails
$ cd hello_rails
$ bin/rails server
At this point, the application has been created and is working on the 3000 port. Connect directly to http://localhost:3000
and you will see the welcome page.
Next, we create the first model, starting with the user’s first and last name.
$ bin/rails generate scaffold User first_name last_name
$ bin/rails db:migrate
The user model has been created so far, and if you connect directly to http://localhost:3000/users
you will find out not only the API but also the basic UI has been generated.
We haven’t even written a single line of code yet!
Create new routes
Since we already have the basic API, I’d like to generate a few new API routes. Let’s take non-resource-based routes and resource-based routes as an example each.
First, a non-resource-based route. I want to create a user count function, so I need /users/count
. I’ll attach the specific process directly at Github commit.
Generally speaking, what we need to do is to modify config/routes.rb
and create a count of the corresponding controller and view.
Further is the resource-based routing, the concept is similar, also attached commit link.
The only difference between the two is in config/routes.rb
, where non-resource-based routes are built on top of collection
, while resource-based routes are built on member
.
Create new fields
Now we have two fields in the user model, first_name and last_name, and we will add more fields, such as gender and age, and we will add some constraints to each field.
For example, first_name and last_name must be present and cannot be blank. And gender must be one of male, female or others. Finally, age must be a natural number.
Let’s start by adding the gender field, i.e. Github commit.
The most important change is in app/models/user.rb
. In addition to the new fields, we also add the constraints of the existing fields. Of course, remember to run migration after modifying the model.
$ bin/rails db:migrate
As you can see, adding an age is as simple as Github commit.
From SQLite to MongoDB
We have implemented a user model in SQLite, but SQLite is a local database and cannot be used for distributed systems. Therefore, we will adopt a standalone database and use MongoDB, which is more flexible in terms of schema, for the subsequent development.
The whole migration process follows the official documentation.
I also attach the full Github commit.
First rewrite the model by removing ApplicationRecord
and adding Mongoid with its fields. Next, we completely remove all the places where active_record
and active_storage
are used in the source code (which is not much).
Afterword
There should be a few more articles to come on expanding this application, which is my first step into Rails.
I’ve experienced many different backend languages before, including Python, Golang, and Node, but none of them are as friendly as Ruby on Rails.
From this article, it’s easy to see that building a “standard” RESTful API with Rails is very simple, and if you don’t have any special needs, you don’t even have to write a single line of code.
Why emphasize standard RESTful APIs?
In fact, the APIs I have written in the past are not so RESTful, such as ${resource}/${id}
, and then provide GET, POST and DELETE operations, such strict routing if not generated by the framework itself, I believe few people will fully follow.
But Rails provides a simple scaffold
to make things easy, and even unit tests are built in, as described in a later article.
Although I took a few days to get familiar with Ruby and Rails, it still takes a bit of time to digest and organize, so let me take a few weeks to finish my thoughts on Ruby. But I have to say, Rails is amazing to me. I can see brilliant ideas everywhere, and there are a lot of design patterns in it. Although it was a bit painful to learn a new language and framework, I still got some insights out of it.