Modular Rails
Bài đăng này đã không được cập nhật trong 3 năm
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 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.
All rights reserved