As part of my journey to becoming a software developer I learned the Ruby on Rails framework, a popular web application framework which makes use of the principle of convention over configuration to make it easier for developers to quickly develop useful web applications.
Ruby on Rails, or Rails, utilizes the model-view-controller (MVC) architecture where controllers handle data flow between views (the representations of data which the user of the application sees and can interact with) and models (which manage the logic and data of the application).
An important feature of Rails is its built-in use of the Active Record Object Relational Mapping System to map database tables (which store the actual information which the program uses) to models (which in Rails are certain Ruby classes which are defined as the models of the application).
Through the use of the Active Record system developers do not need to focus on putting excessive effort into configuration (since the configuration of this aspect of the application is already set up in any Rails application) and can also utilize certain standard, pre-defined conventions to quickly manipulate data for their Rails applications.
Active Record has naming conventions for classes and their corresponding database tables, where table names are pluralized versions of their corresponding model/class names. Active Record also comes with many standard, pre-defined methods which can be utilized on the application's models/classes and instances of those classes.
In Rails, in order to set up database tables for the models of an application, a developer first has to run migrations, which create database tables for the models. In the migration files which a developer creates in order to set up their database, the structure of the database tables (their columns, the data types of these columns, and the relationships of each table to other tables in the application) are defined, and then the developer runs these migration files and the database is created.
Developers can also very easily set up RESTful routes (index, new, create, show, edit, update, and destroy) in Rails by defining the necessary actions (defined as methods belonging to controller classes) in their controller files, then creating corresponding view pages, and also defining the necessary routes. By doing this, developers can provide users of their application access to view pages (accessible to users through specific URL paths) which display certain standard information about the data used in the application: a list of all existing objects, a form to create new objects and then have the newly created object persist in the database, a display of the details of one specific object, a page to edit the details of an object and then have it updated in the database, and the ability to delete an object from the application.
In learning to use the Ruby on Rails framework my goal was to build a working Ruby on Rails application which allowed users to manage a collection of news articles, and make notes about news articles which other users of the application could view.
The final application was a kind of Wikipedia-like application where anyone could create an account (sign up), login into their account and create, read, update, or delete news articles of interest to them, in addition to being able to create, read, update, or delete notes about the news articles (or anything else). Whereas in Wikipedia any user can create an account and add new encyclopedia articles or modify existing ones, as well as discuss the aspects (such as quality, style, tone, problems, etc.) of each online encyclopedia article on the talk pages for articles, in my application users could add news/journalism articles, modify existing ones, and create viewable notes about the news articles or other issues of interest.
The project needed to meet certain requirements in terms of its functionality and design, which are listed below:
The application needed to have models which had at least one
has_many
, at least onebelongs_to
, and at least twohas_many :through
relationships.It needed to include a
has_many :through
association where the join table was a user-submittable attribute.Models needed to have reasonable validations for simple attributes.
There needed to be at least one class level ActiveRecord scope method that was chainable
The application needed to be able to provide standard user authentication including signup, login, logout and passwords.
The application needed to allow login from some third party service, such as GitHub or a social media account such as Facebook, Twitter, etc.
The application needed to make use of a nested resource with the appropriate RESTful URLs, which included a nested new route and either a nested index route or a nested show route.
The forms which were used to enter new information or modify existing information needed to display validation errors on the page.
The application needed to be a DRY (Do-not-Repeat-Yourself) Rails application, where there was not excess repetition of similar or identical lines of code throughout the application.
It was also required that I not use scaffolding (Rails has certain commands a developer can enter which allow them to auto-generate a lot of the working parts of an application from the get go), in order to learn more by building out each part of the application piece by piece on my own rather than having much of it being automatically generated from the start.
Here is how I met these requirements for my application:
- For the first requirement, I set up a
has_many
andbelongs_to
relationship between a region model and a country model, where a region had many countries and a country belonged to a particular region of the world. I used countries and regions for the part of the application where the user could access news articles grouped by country, view countries by region, and add countries to regions.
For the first requirement I also set up two has_many :through
relationships:
A writer has_many :sources, through: :articles
.
A source has_many :writers, through: :articles
.
An article belongs_to :writer
and belongs_to :source
Writer referred to the author or authors of the news article, while source referred to the media company or newspaper responsible for publishing the news article.
A category has_many :subjects, through: :notes
A subject has_many :categories, through: :notes
A note belongs_to :category
and belongs_to :subject
Category referred to the type of note (i.e. what the note could be classified as - such as a critical note, a note expressing an opinion, etc.) while subject referred to what specific topic the note was actually about (such as finance, business, economics, art, politics, movies, etc.).
In my application the join table between writers and sources was the articles table. A user was able to create, read, update, and delete articles, so article was a user-submittable attribute acting as the join table between writers and sources. Similarly the notes table was the join table between subjects and categories and was also user-submittable.
I included validations on the article model ensuring that each article had a title, URL address, and description and also ensuring that each URL address given for each article was unique, before articles could be saved to the database. I also included validations on the names of countries, regions, and writers, and included validations on notes to ensure that they contained information before they could be saved
class Article < ApplicationRecord
belongs_to :writer
belongs_to :source
belongs_to :country
validates :title, :url, :description, presence: true
validates :url, uniqueness: true
end
- I included class level ActiveRecord scope methods on the Writer model/class and on the Source model/class which enabled writers and sources to be ordered by name, respectively.
class Writer < ApplicationRecord
has_many :articles
has_many :sources, through: :articles
validates :name, presence: true
scope :ordered_by_name, -> { order(:name) }
end
I set up standard user authentication allowing signup, login, logout and utilizing passwords. Initially I did this on my own, and got it working perfectly fine following the standard Rails approach to this using user and session controllers, but then later I redid this part using the devise gem instead, which also made this part a bit easier.
For the external third-party or social media login, I used the devise gem, the omniauth gem, the omniauth-github gem, and the omniauth-rails_csrf_protection gem in my Gemfile and set up the necessary models, controllers, and routes in order to allow a user to login to the application using their GitHub account.
gem 'devise'
gem 'omniauth'
gem 'omniauth-github'
gem 'omniauth-rails_csrf_protection'
I set up a nested resource using countries and regions where there was a nested new route for making new countries by region, and a nested index route where one could see all the countries within the same region as a selected country, with the appropriate RESTful URLs.
The forms for creating or editing articles, notes, countries, or regions did display validation errors on their respective view pages if the user failed to enter in the required information correctly.
The application made use of partials and helper methods to keep the repetition of similar or identical code throughout the application to a minimum.
I also used https://newsapi.org/ as a source of real news articles based around a particular subject (I chose news articles about business and the economy) to seed my database with, so I could see the effect of changes to the application with the use of real data as I was building it out. I utilized the rest-client gem to easily import news articles from the newsapi.org website into the seed file for my database, in order to create instances of articles, writers, and sources.
I was able to build out a working Ruby on Rails application which met the technical and design requirements described above, and which provided a basic service to users (storing and managing news information/content in the form of news articles) and in the process I learned a great deal about Rails and also about web development more generally.
Top comments (0)