Modular Rails
Have you ever imagine building web application as huge and complex as Facebook or Twitter? The first thing we can think of is that no one can develop and maintain such a giant web on a single monolithic project? One will be simply drown by the flood of codes adding each day. One can ...
Have you ever imagine building web application as huge and complex as Facebook or Twitter? The first thing we can think of is that no one can develop and maintain such a giant web on a single monolithic project? One will be simply drown by the flood of codes adding each day. One can intuitively think of the many "small" projects those giant website can consist of. This gives us the first notion of modularity.
What is Modularity in Programming?
We will define two concepts, one is monolithic and another is modular. Monolithic is formed of a single large block of stone, modular is having parts that can be connected or combined in different ways __ defined by dictionary. We can see a monolithic project is single huge project with mostly inseparable components. Where as a modular project is the one which we seperated into single interchangeable parts.
For example: a regular monolithic Rail Application is composed of:
app controller users_controller.rb sessions_controller.rb products_controller.rb checkouts_controller.rb model user.rb product.rb checkout.rb view user product checkout config ...
Here is a monolithic project, a regular project that we normally build. We are not worried much if the project is still small. But if project get bigger, adding new feature, writing test, and maintainance have become a pain in the a**. Now modularity comes to the rescue, and this is how it may look:
app controller model view engines base app controller users_controller.rb sessions_controller.rb model user.rb view user config ... product app controller products_controller.rb model product.rb view product config ... checkout app controller checkouts_controller.rb model checkout.rb view checkout config ... config ...
As we see files structure above, we have modularize product and checkout and leave the basic funtionality to core.
Why modularize?
Suppose We are a freelancer who often work on many projects with the common funtionalities such as user registration, user login. If we can make this functionality reuseable, how much time can we save, say, for 100 projects. So one advantage is reuseablity.
Another one is encapsulation of features into modules. In this way we don't need to care about the other features and the conflict between those feature as long as we know about common interface.
One more advantage is that the project is easier to be tested. Writing test can be baffling if functionality is mixed up.
How to modularize?
We will create a small example project to demonstrate this. Suppose you are planning to build a big project (you can envision), and one of the features you want to create is user registration.
Let's create a new app.
rails new demo
Let's create a user registration module in the project.
rails plugin new user_registration --mountable
We see a new folder user_registration is created. and inside this folder is a regular Rails application. Let's create a new folder engines and move user_registration folder inside this.
mkdir engines && mv user_registration ./engines
We have to edit user_registration.gemspec file in order to bundle successfully.
$:.push File.expand_path("../lib", __FILE__) # Maintain your gem's version: require "todo/version" # Describe your gem and declare its dependencies: Gem::Specification.new do |s| s.name = "todo" s.version = Todo::VERSION s.authors = ["kouchchivorn"] s.email = ["kouch.chivorn@gmail.com"] s.homepage = "https://www.chivorn.com/" s.summary = "User registration" s.description = "Modularize user registration functionality" s.license = "MIT" ...
Let's connect the user registration module to the main app by adding gem to Gemfile
gem 'user_registration', path: 'engines/user_registration'
Then we can bundle to make it work.
bundle install
and we see this line show up in console.
Using user_registration 0.1.0 from source at `engines/user_registration`
Now we have to add route so that we can access user_registration through the web browser.
Rails.application.routes.draw do mount UserRegistration::Engine => "/", as: 'signup' end
Let's create a scaffold user to register user in engines/user_registration
rails g scaffold user name:string email:string password:string
Now add a root path to the user_registration module.
UserRegistration::Engine.routes.draw do resources :users root 'users#index' end
It is time to test whether our app is working or not. Start the server in the root folder.
rails s
Oh no! one more thing to do is run database migration and before we can to that we have to edit this file engines/user_registration/lib/user_registration/engine.rb
module Todo class Engine < ::Rails::Engine isolate_namespace UserRegistration initializer :append_migrations do |app| unless app.root.to_s.match(root.to_s) config.paths["db/migrate"].expanded.each do |p| app.config.paths["db/migrate"] << p end end end end end
Now run database migration
rails db:migrate
OK. start the server again.
rails s
Now open the web browser and enter the url
localhost:3000
and try to create a user. It works!!!
I hope you enjoy the journey with me and find it useful.