Rails’ ‘magic’ shouldn’t be intimidating. Here’s a quick setup tutorial on how to get started on using Rails in accordance to Digiteer’s Code Guidelines.
Create new Rails app with PostgreSQL
rails new 'app-name' -d postgresql
For Tailwindcss (Skip this if you will not use it)
bundle add tailwindcss-rails
rails tailwindcss:install
For slim-rails, a templating language
gem slim-rails # add this to Gemfile
bundle install
In config/application.rb
file, add the following lines:
# config/application.rb
module AppName
class Application < Rails::Application
# Rest of the code...
# Configure your timezone here
config.time_zone = "Asia/Singapore" # Configure this to your local timezone
config.active_record.default_timezone = :local # Add this
# Set Slim as the default template engine
config.generators do |g| # Add this
g.template_engine :slim # Add this
end # Add this
end
end
For Authentication using heartcombo/devise
bundle add devise
rails generate devise:install
You will be prompted to create your root path, “Ensure you have defined root_url to something in your config/routes.rb
.”
Create this file in app/views/layouts/_alerts.html.slim
. This is a styled banner notifications in your application.
- flash.each do |type, message|
div class="fixed top-4 right-4 z-50 max-w-sm p-4 rounded-lg shadow-lg border animate-fade-in-down #{type == 'notice' ? 'bg-green-100 border-green-200' : 'bg-red-100 border-red-200'}" id="flash-#{type}" data-flash-type="#{type}"
div class="flex items-center"
- if type == 'notice'
svg.w-5.h-5.text-green-700 xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"
- else
svg.w-5.h-5.text-red-700 xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"
div class="ml-3 text-sm font-normal #{type == 'notice' ? 'text-green-700' : 'text-red-700'}"
= message
button type="button" class="ml-1 text-sm text-gray-500 hover:text-gray-700 focus:outline-none" aria-label="Close" data-dismiss="#flash-#{type}"
svg.w-5.h-5 xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"
/ JavaScript for auto-dismiss and close button
javascript:
document.addEventListener('click', function(event) {
setTimeout(function() {
const flash = document.querySelector('[data-flash-type="notice"], [data-flash-type="alert"]');
if (flash) {
flash.remove();
}
}, 5000);
const button = event.target.closest('[data-dismiss]');
if (button) {
const flashMessage = document.getElementById(button.getAttribute('data-dismiss').substring(1));
if (flashMessage) {
flashMessage.remove();
}
}
});
Then call this alert component in your app/views/layouts/application.html.slim
file. Preferrably at the header part of your application.
body.flex.flex-col.justify-center.items-center.bg-gray-100.text-gray-900.space-y-1.pb-8
header.w-full.m-0
= render "components/alerts" # Add this part
= yield
A quick background information: Digiteer follows the separation of concerns principle rigorously.
Throughout projects, Digiteer structure their applications to mainly two parts; Admin and Site. They assign the administrators to the ‘users’ model and the site visitors to the ‘customers’ model .
User is to admin as Customer is to site.
To get started, you have to think of your application having two roles. One is to serve the site visitors ‘customers’ through the site controllers and views and the other is to serve the administrators ‘users’ through the admin controllers and views.
Following the devise authentication setup, you will have to create these two devise models.
rails g devise user
rails g devise customer
rails db:migrate
Then create devise controllers for these two. Now remember, the user is to the admin and the customer is to the site. This is because the common users of the web applications that Digiteer builds for are the administrators or the client companies. And the customers of the clients are only capable to navigate in the Site route, hence, user is to admin as customer is to site.
Now, create the following controllers:
rails g devise:controllers admin # For the user
rails g devise:controllers site # For the customers
Then, paste this into your config/routes.rb
file:
Rails.application.routes.draw do
get "pages/home"
# Routes for the admin users
devise_for :users,
path: "admin",
controllers: {
sessions: "admin/sessions",
registrations: "admin/registrations",
passwords: "admin/passwords"
},
path_names: {
sign_in: "/login",
password: "/forgot",
confirmation: "/confirm",
unlock: "/unblock",
registration: "/register",
sign_up: "/new",
sign_out: "/logout",
password_expired: "/password-expired"
}
namespace :admin do
resources :post
end
# Routes for the site customers
devise_for :customers,
path: "/",
controllers: {
sessions: "site/sessions",
registrations: "site/registrations",
passwords: "site/passwords"
},
path_names: {
sign_in: "/login",
password: "/forgot",
confirmation: "/confirm",
unlock: "/unblock",
registration: "/register",
sign_up: "/new",
sign_out: "/logout",
password_expired: "/password-expired"
}
scope module: :site, path: "/" do
resources :posts, controller: "/site/posts", as: :posts
root "posts#index", as: :root
end
get "up" => "rails/health#show", as: :rails_health_check
end
Basically, we assign the models (user, customer) to the following routes (admin, site).
Notice that I assigned the root route to site. Also, the default resource route of site is the posts resource. If you’re not doing this, then you can switch back to this:
# Routes for the site customers
devise_for :customers,
path: "/",
controllers: {
sessions: "site/sessions",
registrations: "site/registrations",
passwords: "site/passwords"
},
path_names: {
sign_in: "/login",
password: "/forgot",
confirmation: "/confirm",
unlock: "/unblock",
registration: "/register",
sign_up: "/new",
sign_out: "/logout",
password_expired: "/password-expired"
}
scope module: :site, path: "/" do
resources :posts, controller: "/site/posts", as: :posts
root "posts#index", as: :root
end
# Defines the root path route ("/") in config/routes.rb
root "pages#home"
If you want to separate the site scoping into your config/routes.rb
for maintainability, you can do so by separating the site scopes for both of the routes into separate files. First, copy the contents of your config/routes.rb
file. Then under config directory, add a routes directory. Inside it, create two files named, “admin.rb” and “site.rb”. Then, paste the site scopes accordingly.
# config/routes/admin.rb
devise_for :users,
path: "admin",
controllers: {
sessions: "admin/sessions",
registrations: "admin/registrations",
passwords: "admin/passwords"
},
path_names: {
sign_in: "/login",
password: "/forgot",
confirmation: "/confirm",
unlock: "/unblock",
registration: "/register",
sign_up: "/new",
sign_out: "/logout",
password_expired: "/password-expired"
}
namespace :admin do
resources :post
end
# config/routes/site.rb
devise_for :customers,
path: "/",
controllers: {
sessions: "site/sessions",
registrations: "site/registrations",
passwords: "site/passwords"
},
path_names: {
sign_in: "/login",
password: "/forgot",
confirmation: "/confirm",
unlock: "/unblock",
registration: "/register",
sign_up: "/new",
sign_out: "/logout",
password_expired: "/password-expired"
}
scope module: :site, path: "/" do
resources :posts, controller: "/site/posts", as: :posts
root "posts#index", as: :root
end
Then call both of the group routes in your config/routes.rb
like so:
Rails.application.routes.draw do
resources :posts
get "pages/home"
# Routes for the admin users
draw :admin
# Routes for the site customers
draw :site
# Root route is removed since it is handled by the 'site' module
get "up" => "rails/health#show", as: :rails_health_check
end
For authentication styling customization, you can access the Devise files by copying the Devise views to your app by running:
rails g devise:views # If you want to proceed with separate styling, proceed to the next step below.
This will generate a devise folder under your app/views
directory. You can now customize your authentication workflow.
If you want full and separate control of the styling for each of the following routes, you can do so by passing the group route name:
rails g devise:views admin
rails g devise:views site
Also, if you want to modify the authentication workflow, you can do so by generating the devise controllers like this:
rails g devise:controllers admin
rails g devise:controllers site
This will create an admin and site directory under app/controllers and inside them are the devise-specific controllers. Now, as you progress with your application, things might get a little cluttered. If you want to make your folder structure clean and tidy, you can make the devise-specific controllers to be placed within another folder. In this case you can follow this tutorial here.
You can now direct to these controllers your existing scaffolds or if you don’t have one yet, you can generate them and point it to these routes accordingly.
Then, you can now also start your application.
bin/dev # If using Tailwindcss
rails s # If using standalone Rails
You should consider pushing your new changes to GitHub.
That's all! I hope you learn a thing or two. If you have any opinions, feel free to talk to me at the comment section! Happy Valentine's Day everyone!