PigRider Blogs

Sign In
HTML CSS Javascript Ruby on Rails C++ Java Python SQL Git Linux Others All

Use an engine to share layout among multiple applications in Rails 3.2

[Author: Dingyu]   [Thu, 2016-08-11, 10:29]   [4021 views]

Ruby on Rails

This blog shows how I build an Rails engine to share layout among multiple applications step by step. The way I build the shared layout engine in this blog may be not the best. However, I only want to give some clue to those who are still struggling in building this kind of engine. I have created GitHub repositories for pigrider.com website, please click here to see more details. 1. The name of my engine is "PigriderLayout". Run the command rails plugin new PigriderLayout --mountable -d mysql to create a new engine project. Here I use -d mysql because I use MySQL as database. You may change it to fit your own database, and don't forget to add an according database gem into Gemfile. I put gem 'mysql2', '0.3.11' into my Gemfile. 2. Generate a controller for all shared layout contents. Run this command rails generate controller main globalLayout aboutUs contactUs. Here I have three actions in this main controller. Then, edit the file config/routes.rb, let it looks like:
PigriderLayout::Engine.routes.draw do
  match "AboutUs"=>"main#aboutUs", :as=>:aboutUs
  match "ContactUs"=>"main#contactUs", :as=>:contactUs 
end
3. In the directory app/views/pigrider_layout/main, you should see three view files now. They are globalLayout.html.erb, aboutUs.html.erb, and contactUs.html.erb. You may write anything you want in aboutUs.html.erb and contactUs.html.erb. In the file globalLayout.html.erb, I write:
<!DOCTYPE html>
<html>
<head>
  <%= render 'pigrider_layout/main/appHeadSetting' %>
  <%= stylesheet_link_tag "pigrider_layout/layout", :media=>"all" %>
  <%= javascript_include_tag "pigrider_layout/dropList" %>
  <%= yield(:webpageCSS) %>
  <%= yield(:webpageHeadJavascript) %>
  <%= yield(:webpageMetaTag) %>
  <%= csrf_meta_tags %>
  <!--[if lt IE 9]>
    <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->
</head>


<body>
  <header>
    <img src="/assets/pigrider_layout/pigRiderLogo.png" id="pigRiderLogo" class="FloatLeft" alt="PigRider">

    <%= render 'pigrider_layout/main/headerApp' %>

    <nav id="headerNav">
      <ul>
        <li><%= link_to "Home", main_app.root_path %></li>
        <li><a href="#" onmouseover="openDropList('allPigRiderProducts')" onmouseout="setDropListCloseTimer()">Products</a>
          <div id="allPigRiderProducts" onmouseover="removeDropListCloseTimer()" onmouseout="setDropListCloseTimer()">
            <a href="http://account.pigrider.com/">PigRider Account</a>
            <a href="http://blogs.pigrider.com/">PigRider Blogs</a>
            <a href="http://tools.pigrider.com/">PigRider Tools</a>
          </div>
        </li>
        <li><%= link_to "About Us", pigrider_layout.aboutUs_path %></li>
        <li><%= link_to "Contact Us", pigrider_layout.contactUs_path %></li>
      </ul>
    </nav>
  </header>

  <div class="MainBody">
    <%= yield %>
  </div>

  <footer>
    <nav id="footerNav">
      <%= link_to "About Us", pigrider_layout.aboutUs_path %>
      <%= link_to "Contact Us", pigrider_layout.contactUs_path %>
    </nav>
  </footer>

  <%= yield(:webpageTailJavascript) %>
</body>
</html>
4. Above code uses two render files 'pigrider_layout/main/appHeadSetting' and 'pigrider_layout/main/headerApp'. To easily override application specific contents into the engine is the purpose of using them. Create two files _appHeadSetting.html.erb and _headerApp.html.erb in the directory app/views/pigrider_layout/main In the file _appHeadSetting.html.erb, I write:
<title>PigRider Layout | <%= yield(:webpageTitle) %></title>

<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="application-name" content="pigRider.com" />
<meta name="author" content="pigRider" />
In the file _headerApp.html.erb, I write:
<div class="FloatLeft">
  <h1 style="margin:5px 5px 5px 10px; font-style:italic;"><span style="color:yellow">Pig</span><span style="color:blue">Rider</span> <span style="color:red">Layout</span></h1>
</div>
5. Delete the engine layout file app/views/pigrider_layout/application.html.erb to force the engine not to use it. 6. Edit the file app/controllers/pigrider_layout/application_controller.rb, delete all original code in it, and add these lines:
class PigriderLayout::ApplicationController < ApplicationController
end
This change is important. It let the engine use the layout that the application uses. 7. It's the time to deal with assets now. First, add a picture pigRiderLogo.png into the directory app/assets/images/pigrider_layout. Then, I delete all files in the directory app/assets/javascripts/pigrider_layout, create a file dropList.js there, and write below code in this file:
var gnDropListCloseTimeout=500;
var goDropListCloseTimer=null;
var goDropListEntry=null;


// open hidden layer
function openDropList(sElementID) { 
  // cancel close timer
  removeDropListCloseTimer();

  // close old layer
  if (goDropListEntry) {
    goDropListEntry.style.visibility = 'hidden';
  }

  // get new layer and show it
  goDropListEntry=document.getElementById(sElementID);
  goDropListEntry.style.visibility = 'visible';
}


// close showed layer
function closeDropList() {
  if (goDropListEntry) {
    goDropListEntry.style.visibility = 'hidden';
  }
}


// go close timer
function setDropListCloseTimer() {
  goDropListCloseTimer=window.setTimeout(closeDropList, gnDropListCloseTimeout);
}


// cancel close timer
function removeDropListCloseTimer() {
  if (goDropListCloseTimer) {
    window.clearTimeout(goDropListCloseTimer);
    goDropListCloseTimer=null;
  }
}


// close layer when click-out
document.onclick = closeDropList; 
Last, I delete all files in the directory app/assets/stylesheets/pigrider_layout, create two files _common.css.scss and layout.css.scss there. In the file _common.css.scss, I write:
$pageWidth:1000px;
$pageMinHeight:500px;
$mainBodyLeftRightPadding:20px;
$mainBodyBorderWidth:1px;


@mixin LinkStyle($normalColor,$hoverColor,$visitedColor) {
  text-decoration:none;
  color:$normalColor;
  &:visited {
    color:$visitedColor;
  }
  &:hover {
    text-decoration:underline;
    color:$hoverColor;
  }
}


@mixin LinkStyleNoUnderline($normalColor,$hoverColor,$visitedColor) {
  text-decoration:none;
  color:$normalColor;
  &:visited {
    color:$visitedColor;
  }
  &:hover {
    text-decoration:none;
    color:$hoverColor;
  }
}


* {
  margin:0; 
  padding:0;
  clear:both;
}


.FloatLeft {
  clear:none;
  float:left; 
}


.FloatRight { 
  clear:none;
  float:right; 
}
In the file layout.css.scss, I write:
@import "common";


pre {
  padding:10px;
  white-space: pre-wrap;
  white-space: -moz-pre-wrap;
  white-space: -pre-wrap;
  white-space: -o-pre-wrap;
  word-wrap: break-word;
  overflow:auto;
}


body {
  width:$pageWidth;
  margin:0 auto;
  background-color:#e5f2ff;
  font-family:sans-serif,Arial,Helvetica;
  font-size:15px;
}


$headerHeight: 60px;
header {
  position:relative;
  background-color:#7dbeff;
  height:$headerHeight;
  width:100%;
  margin:0 auto;
  border-bottom:5px;
  border-bottom-style:solid;
  border-bottom-color:#ffc477;
}


.MainBody {
  position:relative;
  min-height:$pageMinHeight;
  padding:10px $mainBodyLeftRightPadding;
  background-color:#ffffff;
  border:$mainBodyBorderWidth;
  border-color:#b0b0b0;
  border-left-style:solid;
  border-right-style:solid;
  a {
    @include LinkStyle(#0000ff,#ff0000,#0000ff);
  }
}


$footerHeight: 30px;
footer {
  position:relative;
  background-color:#7dbeff;

  height:$footerHeight; 
  width:100%; 
  margin:0 auto;
  border-top:5px;
  border-top-style:solid;
  border-top-color:#ffc477;
}

 
// Header Specific
#pigRiderLogo {
  background-color:#ffffff;
  margin:0 0 0 1px;
  height:$headerHeight;
  width:$headerHeight * 1.5;
}


#headerNav {
  position:absolute;
  right:0;
  bottom:0;
  padding:5px 5px 2px 15px;
  background-color:#ffc477;
  -moz-border-radius:50px 0 0 0;
  -webkit-border-radius:50px 0 0 0;
  border-radius:50px 0 0 0;
  
  a {
    @include LinkStyle(#0000ff,#ff0000,#0000ff);
  }
  
  ul {
    li {
      @extend .FloatLeft;
      display:block;
      list-style:none;
      padding:2px 15px 0 15px;
    }
    
    div
    {
      visibility:hidden; 
      position:absolute;
      margin:3px 0 0 -5px;
      padding:4px 0;
      background:#fff0de;
      border:1px solid #5970B2;
      -moz-border-radius:10px;
      -webkit-border-radius:10px;
      border-radius:10px;
      z-index:999;
      
      a {
        display:block;
        padding:6px 12px;
      }
    }
  }
}


// Footer Specific
#footerNav{
  position:absolute;
  left:0;
  a {
    line-height:$footerHeight;
    margin:0 0 0 15px;
    @include LinkStyle(#0000ff,#ff0000,#0000ff);
  }
}


.ErrorReportContainer {
  margin:0 0 40px 0;
  p {
    font-weight:bold;
    font-size:20px;
  }
  ul {
    margin:0 0 0 30px;
    li {
      color:red;
    }
  }
}
Now, you have successfully built this shared layout engine. You may still don't know how to use it. Don't worry, I have written another blog to explain how to use this layout engine in an application. Be careful, you may need to modify the path in the first step of that blog to let your engine work!