How to Architect Rails Project - Part 2
Tags: rails, tutorial, ruby, architecture
Published: 2019-12-08 21:15:59


![Modules](https://res.cloudinary.com/practicaldev/image/fetch/s--vfnMpkSb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1060/1%2ArW0W8dEaRtJzuXRz3RCszA.jpeg)
In the previous article, we've done setting up our Surface module
[How to Architect Rails Project - Part 1](https://dev.to/renzodiaz/how-to-architect-rails-project-part-1-34bp)

In this article, we'll cover the following features:

- Module Admin
- Layouts
- Front-End Scaffolding

So, let's kick off...

For our Admin module, we want to get these routes

```ruby
'domain.com/admins' # Overview, basic reports, charts
'domain.com/admins/blog' # Articles CRUD
'domain.com/admins/work' # Work CRUD
```


Let's add them to our routes.

```ruby
# config/routes.rb
Rails.application.routes.draw do
  ...
  namespace :admins do
    resources :blogs
    resources :works
  end
end
```

> Why **admins** and not **admin** in singular? Because this module was thought to have multiple user types, users that only can publish articles and won't have permissions to publish works.

Moving on, we need to create the views for our Admin module according to our routes.

Create these files inside `app/views` folder

```
app/views
  ├── admins/blogs
  │   └── index.html.erb
  │   └── show.html.erb
  ├── admins/works
  │   └── index.html.erb
  │   └── show.html.erb
  └── layouts 
      └── admin.html.erb
```

Let's add our markup to `admin.html.erb` layout, for now, let's copy the content from `application.html.erb`. Our layout would look like this...
```erb
<!DOCTYPE html>
<html>
  <head>
    <title>Portfolio</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= render "partials/header" %>
    <%= yield %>
  </body>
</html>
```

Cool right? We have our Admin layout. 

Hold on your horses... We are still using the assets from our **Surface** module (application), we want to isolate **Admin/Surface** and make them to not depend on each other, so, let's isolate our Admin assets out of the Surface module.

Create these files under `app/assets/stylesheets`...

```
app/assets/stylesheets
  ├── admin/base
  │   └── _base.scss
  │   └── _variables.scss
  ├── admin/components
  │   └── _header.scss
  │   └── _sidebar.scss
  └── admin.scss
```

So our `admin.scss` would look like this...

```scss
// Base theming
@import "admin/base/variables";
@import "admin/base/base";

// Components
@import "admin/components/header";
@import "admin/components/sidebar";
```

Next, add this line to our `base_controller.rb` under `controller/admins` folder

```ruby
class Admins::BaseController < ApplicationController
  layout 'admin'
end

```

We need also to tell rails that we want to use `admin.css|admin.js` in our initializer, so add the following line to `config/initializers/assets.rb`

```ruby
Rails.application.config.assets.precompile += %w( admin.js admin.css )
```

Now let's change our `admin.html.erb` layout to use `admin.css` and remove the current header stylesheet which comes from the `Surface` module.

```erb
<!DOCTYPE html>
<html>
  <head>
    <title>Portfolio</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag 'admin', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
```

Well done! now if we run `rails s` and navigate to `http://localhost:3000/admins/blogs` we should see a blank page. If we inspect elements in the browser we'll see that our styles are coming from **admin** and not **application** anymore.

Alright, Renzo, this is cool but I don't see any component and styles on the page yet. Besides the javascript still comes from the Surface module.

Before deep into the components and styles, let's add an Admin module for our javascript files, this is pretty much the same as what we did with styles.

So, let's create an `admin.js` file under `app/javascript/packs` folder and add the following lines...

```javascript
// app/javascript/packs/admin.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
```

Change `<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>` to `<%= javascript_pack_tag 'admin', 'data-turbolinks-track': 'reload' %>` in our `admin.html.erb` layout

```erb
<!DOCTYPE html>
<html>
  <head>
    <title>Portfolio</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag 'admin', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'admin', 'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
```

Let's restart our server and see what we got, we should see the same blank page if we navigate to `http://localhost:3000/admins/blogs`. If we inspect the page we'll see that now our javascript comes from admin.


Now we have our js/css isolated from the Surface module, let's add Bulma to our project and start building our components.

Add [Bulma gem](https://github.com/joshuajansen/bulma-rails) to our `Gemfile` at the bottom

```
gem "bulma-rails", "~> 0.8.0"
```

Then run `bundle install` in the terminal.


> In this article we are going to use Bulma in both modules `Surface/Admin`, just to not make this article too long. Remember that we can also isolate to use Bulma for the Admin and any other framework for our Surface.

Now let's build our layout markup correctly for the Admin module.


```erb
<!Doctype html>
<html>
...
  <body>
    <%= render "patials/admin/header" %>
    <div class="columns is-fullheight">
      <div class="column is-2 is-sidebar-menu is-hidden-mobile">
        <%= render "partials/admin/sidebar" %>
      </div>
      <div class="column is-main-content">
        <%= yield %>
      </div>
    </div>
  </body>
</html>
```

Create a `admin` folder under `app/views/partials` add these files...

`app/views/partials/admin/_header.html.erb`
```erb
<nav class="navbar">
  <div class="navbar-brand">
    <a class="navbar-item" href="#">
      Logo
    </a> 
    <div class="navbar-burger burger" data-target="navMenubd-example">
      <span></span>
      <span></span>
      <span></span>
    </div>
  </div>

  <div id="navMenubd-example" class="navbar-menu">
    <div class="navbar-start">
    </div>

    <div class="navbar-end">
      <div class="navbar-item has-dropdown is-hoverable">
        <a class="navbar-link">
          <figure class="avatar image is-32x32">
            <img class="is-rounded" src="https://bulma.io/images/placeholders/32x32.png" alt=""/>
          </figure>
          username
        </a>

        <div class="navbar-dropdown is-right">
          <a class="navbar-item">
            Logout
          </a>
        </div>
      </div>
    </div>
  </div>
</nav>
```

`app/views/partials/admin/_sidebar.html.erb`
```erb
<aside class="menu">
  <p class="menu-label">
    General
  </p>
  <ul class="menu-list">
    <li><%= link_to 'Overview', admins_blogs_path %></li>
  </ul>
  <p class="menu-label">
    Administration
  </p>
  <ul class="menu-list">
    <li>
      <%= link_to 'My Work', admins_works_path %>
    </li>
    <li><%= link_to 'My Work', admins_blogs_path %></li>
    <li><a>Users</a></li>
  </ul>
</aside>
```

Add some styles

`app/assets/stylesheets/admin/base/_variables.scss`
```scss
$body-size: 16px !default;

// Responsiveness
// 960, 1152, and 1344 have been chosen because they are divisible by both 12 and 16
$tablet: 769px !default;
// 960px container + 40px;
$desktop: 1000px !default;
// 1152px container + 40;
$widescreen: 1192px !default;
// 1344px container + 40;
$fullhd: 1384px !default;

$white: #fff !default;
$orange: hsl(17, 83%, 57%);
$primary: $orange !default;
$primary-invert: $white !default;

// Link colors
$link: $primary !default;
$link-invert: $white !default;
$navbar-height: 3.25rem;
```

`app/assets/stylesheets/admin/base/_base.scss`

```scss
body {
	overflow-x: hidden;
}
.columns {
	&.is-fullheight {
		min-height: calc(100vh - ( #{$navbar-height} - .75rem ) );
		max-height: calc(100vh - ( #{$navbar-height} - .75rem ) );
		height: calc(100vh - ( #{$navbar-height} - .75rem ) );
		display: flex;
		flex-direction: row;
		justify-content: stretch;

		.column {
			overflow-y: auto;
		}
	}
}    
.is-main-content {
	background: $grey-lighter;
	padding: 1rem  2.5rem 1rem 2rem;
}

```


`app/assets/stylesheets/admin/components/_sidebar.scss`

```scss
.is-sidebar-menu {
	padding: 2.5rem;
	background: $grey-dark;
	li {
		a {
			color: $white;
		}
	}
}

```


`app/assets/stylesheets/admin/components/_header.scss`

```scss
.navbar {
	.navbar-item {
		.avatar {
			margin-right: .65rem;
			.is-rounded {
				max-height: 100%;
			}
		}
	}
}

```

Alright, if we restart our browser at this point and navigate to `http://localhost:3000/admins/blogs` our dashboard should look like this.

![Portfolio | Admin](https://miro.medium.com/max/1919/1*lP3nTaT-q9Wz1rHfPFcd3g.png)


If you got any error through this setup, feel free to drop me a line dev.renzo.diaz@gmail.com I’ll try to answer all your doubts asap.

In part 1 of this article, I mentioned this part 2 will cover CRUDS and Authentication, and React as well.

We'll, we will cover those points in part3, I don't want to make this article too long and don't mix with Rails back-end stuff.


[<img src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg" alt="Buy me a coffee" style="display: inline !important;">](https://www.buymeacoffee.com/renzodiaz)