Building Financial Stock Scanner with Ruby on Rails and R. Part 17. Navigation.

Let’s create a menu for the site navigation.

For the menu and the general site appearance we will use the  Bootstrap, which is a popular web front end framework.

There are few CSS preprocessors available to use with the Bootstrap. My original choice was Less. It is native for Bootstrap, therefore, supposedly, to be faster then others. Also I used to use Bootstrap Magic to generate themes. You can save your theme in the Less format, which I could just drag-n-drop deploy into my Rails app. To integrate Bootstrap and Less into Rails I was using less-rails-bootstrap gem.

However at the time of writing this post I am running OS X El Capitan public beta 3 and with that I have been having difficulty with installation of  the therubyracer gem – which is required for the integration with the Google’s V8 JavaScript engine, which, in turn, is a dependency for the less-rails-bootstrap. Therefore I had to change the approach and chose SASS as a CSS preprocessor and therefore bootstrap-sass gem.

We will also use font-awesome for some icons.

First install ‘bootstrap-sass’ and ‘font-awesome-rails’ gems per the Readme instructions.

Move we need to rename app/assets/stylesheets/application.css to application.scss and edit it to change the syntax from CSS to SASS as the following:


@import "select2";
@import "select2-bootstrap";
@import "bootstrap-sprockets";
@import "bootstrap";
@import "font-awesome";

Edit app/assets/javascripts/application.js to include ‘require bootstrap-sprockets’ line:


//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require select2
//= require bootstrap-sprockets
//= require_tree .

Now we are ready to create a menu.

We want it to be a navigation bar at the top of the page with a text logo of the site (let’s call it DancingCandleSticks) at the left side and few menu items at the right side

For the menu items we want

  • A home button – we use it later for the market summary
  • ‘Exchanges’ drop-down menu with a list of exchanges and an ability to add a new one
  • ‘Lists’ drop-down menu with a list of lists and an ability to add a new one

Therefore we need to make exchanges and lists lists available to the application view (that’s where the code for the navigation bar will go).

For that we need to update app/assets/controllers/application_controller to add methods for exchanges and lists as the following:


class ApplicationController < ActionController::Base

 protect_from_forgery with: :exception
 before_filter :lists
 before_filter :exchanges

private
 def lists
 @lists = List.all
 end
 def exchanges
 @exchanges = Exchange.all
 end
end

Edit app/views/layouts/application.html.erb:

<!DOCTYPE html>
<html>
  <head>
    <title>Stock Scanner</title>
    <%= include_gon %>
    <%= stylesheet_link_tag    "application", :media => "all" %>
    <%= javascript_include_tag "application" %>
    <%= csrf_meta_tags %>
  </head>
  <body>
    <div class="row">
      <div class="col-md-1"></div>
      <div class="col-md-10">
          <div class="container">
            <div class="navbar-outer">
              <div class="navbar navbar-default navbar-fixed-top">
                <div class="navbar-inner">
                   <div class="container"> <a href="http://localhost:3000/"
                      class="navbar-brand">Dancing<b>Candle</b>Sticks</a>
                    <ul class="nav navbar-nav navbar-right">
                      <li><%= link_to root_path do%>
                        <i class="fa fa-home"></i> Home
                      <% end %>
                      </li>
                      <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-globe"></i> Markets <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                          <% @exchanges.each do |ee| %>
                          <li><%= link_to exchange_path(ee.name) do%>
                              <i class="fa fa-th-large"></i> <%= ee.name %>
                            <% end %>
                          </li>
                          <% end %>
                          <li class="divider"></li>
                          <li><%= link_to 'New Market', new_exchange_path %></li>
                        </ul>
                      </li>
                      <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-list"></i> Lists <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                          <% @lists.each do |ll| %>
                          <li><%= link_to list_path(ll.name) do%>
                            <i class="fa fa-th-large"></i> <%= ll.name %>
                            <% end %>
                          </li>
                          <% end %>
                          <li class="divider"></li>
                          <li><%= link_to 'New List', new_list_path %></li>
                        </ul>
                      </li>
                    </ul>
                  </div> <!-- container -->
                  </div> <!-- nabar-inner -->
                </div> <!-- navbar -->
              </div> <!-- navbar-outer -->
              <%= yield %>
            </div> <!-- container -->
          </div> <!-- col-md-10 -->
      <div class="col-md-1"></div>
    </div> <!-- row -->
  </body>
</html>

This is pretty straightforward – we take a list of exchanges and a list of lists. We iterate through them and call ‘link_to model_path(instance.name)’, which renders right html code with the links to exchanges/lists views.

Similarly “link_to ‘New whatever’ new_model_path” leads to a form to create a new instance of exchange or list.

Open your browser and go to http://localhost:3000/lists/test and you should now see our navigation bar at the top of the page. Like this:
Screen Shot 2015-08-13 at 8.25.23 PM

Let’s change colours to green letters on dark blue background.
Edit application.scss file and add the following line before first ‘import’ statement:


@import 'custom/variables.scss';

Now we need to create a custom/variables.scss file:

// Grays
$gray-darker: #222222;
$gray-dark: #333333;
$gray: #555555;
$gray-light: #999999;
$gray-lighter: #eeeeee;
// Brand Colors
$brand-primary: #9cff00;
$brand-success: #5cb85c;
$brand-warning: #f0ad4e;
$brand-danger: #d9534f;
$brand-info: #5bc0de;
// Scaffolding
$body-bg: #B6C0D2;
$text-color: $gray-dark;
$link-color: $brand-primary;
$link-hover-color: darken($link-color, 15%);
$padding-base-vertical: 6px;
$padding-base-horizontal: 12px;
$padding-large-vertical: 10px;
$padding-large-horizontal: 16px;
$padding-small-vertical: 5px;
$padding-small-horizontal: 10px;
$border-radius-base: 4px;
$border-radius-large: 6px;
$border-radius-small: 3px;
$line-height-large: 1.33;
$line-height-small: 1.5;
$component-active-bg: $brand-primary;
// Typography
$font-size-base: 14px;
$font-size-large: ceil($font-size-base * 1.25);
$font-size-small: ceil($font-size-base * 0.85);
$font-size-h1: floor(($font-size-base * 2.6));
$font-size-h2: floor(($font-size-base * 2.15));
$font-size-h3: floor(($font-size-base * 1.7));
$font-size-h4: floor(($font-size-base * 1.25));
$font-size-h5: $font-size-base;
$font-size-h6: floor(($font-size-base * 0.85));
$line-height-base: 1.428571429;
$line-height-computed: floor($font-size-base * $line-height-base);
$headings-font-weight: 500;
$headings-line-height: 1.1;
$headings-color: inherit;
// Buttons
$btn-font-weight: normal;
$btn-default-color: #333;
$btn-default-bg: #fff;
$btn-default-border: #ccc;
$btn-primary-color: #fff;
$btn-primary-bg: $brand-primary;
$btn-primary-border: darken($btn-primary-bg, 5%);
$btn-success-color: #fff;
$btn-success-bg: $brand-success;
$btn-success-border: darken($btn-success-bg, 5%);
$btn-warning-color: #fff;
$btn-warning-bg: $brand-warning;
$btn-warning-border: darken($btn-warning-bg, 5%);
$btn-danger-color: #fff;
$btn-danger-bg: $brand-danger;
$btn-danger-border: darken($btn-danger-bg, 5%);
$btn-info-color: #fff;
$btn-info-bg: $brand-info;
$btn-info-border: darken($btn-info-bg, 5%);
$btn-link-disabled-color: $gray-light;
// Dropdowns
$dropdown-bg: #313c53;
$dropdown-border: #1c263c;
$dropdown-divider-bg: #e5e5e5;
$dropdown-link-active-color: #fff;
$dropdown-link-active-bg: $component-active-bg;
$dropdown-link-color: $brand-primary;
$dropdown-link-hover-color: #1c263c;
$dropdown-link-hover-bg: $dropdown-link-active-bg;
$dropdown-link-disabled-color: $gray-light;
$dropdown-header-color: $gray-light;
$dropdown-caret-color: #000;
// Forms
$input-bg: #fff;
$input-bg-disabled: $gray-lighter;
$input-color: $gray;
$input-border: #ccc;
$input-border-radius: $border-radius-base;
$input-color-placeholder: $gray-light;
$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2);
$input-height-large: (floor($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2);
$input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2);
$legend-color: $gray-dark;
$legend-border-color: #e5e5e5;
$input-group-addon-bg: $gray-lighter;
$input-group-addon-border-color: $input-border;
// Table
$table-cell-padding: 8px;
$table-condensed-cell-padding: 5px;
$table-cell-padding: 8px;
$table-condensed-cell-padding: 5px;
$table-bg: transparent;
$table-bg-accent: #f9f9f9;
$table-bg-hover: #f5f5f5;
$table-bg-active: $table-bg-hover;
$table-border-color: #ddd;
// Navbar
$navbar-height: 40px;
$navbar-margin-bottom: $line-height-computed;
$navbar-default-color: $brand-primary;
$navbar-default-bg: #313c53;
$navbar-default-border: #1c263c;
$navbar-border-radius: $border-radius-base;
//$navbar-padding-horizontal: ;
$navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2);
$navbar-default-link-color: $brand-primary;
$navbar-default-link-hover-color: lighten($brand-primary, 10%);
$navbar-default-link-hover-bg: darken($navbar-default-bg, 5%);
$navbar-default-link-active-color: $brand-primary;
$navbar-default-link-active-bg: darken($navbar-default-bg, 10%);
$navbar-default-link-disabled-color: #ccc;
$navbar-default-link-disabled-bg: transparent;
$navbar-default-brand-color: $navbar-default-link-color;
$navbar-default-brand-hover-color: lighten($navbar-default-link-color, 15%);
$navbar-default-brand-hover-bg: transparent;
$navbar-default-toggle-hover-bg: #ddd;
$navbar-default-toggle-icon-bar-bg: #888;
$navbar-default-toggle-border-color: #ddd;
// Navs Tabs
$nav-link-padding: 10px 15px;
$nav-link-hover-bg: $gray-lighter;
$nav-disabled-link-color: $gray-light;
$nav-disabled-link-hover-color: $gray-light;
$nav-tabs-border-color: #ddd;
$nav-tabs-link-hover-border-color: $gray-lighter;
$nav-tabs-active-link-hover-bg: $body-bg;
$nav-tabs-active-link-hover-color: $gray;
$nav-tabs-active-link-hover-border-color: #ddd;
$nav-tabs-justified-link-border-color: #ddd;
$nav-tabs-justified-active-link-border-color: $body-bg;
$nav-pills-border-radius: $border-radius-base;
$nav-pills-active-link-hover-bg: $component-active-bg;
$nav-pills-active-link-hover-color: #fff;
// Jumbotron
$jumbotron-padding: 30px;
$jumbotron-color: inherit;
$jumbotron-bg: $gray-lighter;
$jumbotron-font-size: ceil(($font-size-base * 1.5));
// Pagination
$pagination-bg: #fff;
$pagination-border: #ddd;
$pagination-hover-bg: $gray-lighter;
$pagination-hover-color: $link-hover-color;
$pagination-hover-border: #ddd;
$pagination-active-bg: $brand-primary;
$pagination-active-color: #fff;
$pagination-active-border: $brand-primary;
$pagination-disabled-color: $gray-light;
$pagination-disabled-bg: #fff;
$pagination-disabled-border: #ddd;
$pager-bg: $pagination-bg;
$pager-border: $pagination-border;
$pager-border-radius: 15px;
$pager-hover-bg: $pagination-hover-bg;
$pager-active-bg: $pagination-active-bg;
$pager-active-color: $pagination-active-color;
$pager-bg: $pagination-bg;
$pager-disabled-color: $gray-light;
// Form States
$state-warning-text: #c09853;
$state-warning-bg: #fcf8e3;
$state-warning-border: darken($state-warning-bg, 3%);
$state-danger-text: #b94a48;
$state-danger-bg: #f2dede;
$state-danger-border: darken($state-danger-bg, 3%);
$state-success-text: #468847;
$state-success-bg: #dff0d8;
$state-success-border: darken($state-success-bg, 5%);
$state-info-text: #3a87ad;
$state-info-bg: #d9edf7;
$state-info-border: darken($state-info-bg, 7%);
// ToolTip
$tooltip-max-width: 200px;
$tooltip-color: #fff;
$tooltip-bg: #000;
$tooltip-arrow-width: 5px;
$tooltip-arrow-color: $tooltip-bg;
$tooltip-opacity: .9;
// Popover
$popover-bg: #fff;
$popover-max-width: 276px;
$popover-border-color: rgba(0,0,0,.2);
$popover-fallback-border-color: #ccc;
$popover-title-bg: darken($popover-bg, 3%);
$popover-arrow-width: 10px;
$popover-arrow-color: #fff;
$popover-arrow-outer-width: ($popover-arrow-width + 1);
$popover-arrow-outer-color: rgba(0,0,0,.25);
$popover-arrow-outer-fallback-color: #999;
// List Group
$list-group-bg: #fff;
$list-group-border: #ddd;
$list-group-border-radius: $border-radius-base;
$list-group-hover-bg: #f5f5f5;
$list-group-active-color: #fff;
$list-group-active-bg: $component-active-bg;
$list-group-active-border: $list-group-active-bg;
$list-group-active-text-color: lighten($list-group-active-bg, 40%);
$list-group-link-color: #555;
$list-group-link-heading-color: #333;

Here I took Bootstrap Magic theme export in the Less format and converted it into SASS – by changing @ to $ signs and adjusting darken/lighten statements according to the SASS format.

Save the file and reload the http://localhost:300/lists/test page. It now will look like this:

Screen Shot 2015-08-13 at 9.21.59 PM

There are few problems here. The navigation bar blocks part of the content. Also, suppose,  we’d like black borders and white background for the table. At last we want width of the navigation bar to be fixed (as our pictures are fixed by size) and be indent with the left border of the main table.

For that we add the following code to the application.scss:


body {
padding-top: 60px;
background: #B6C0D2 !important;
// margin: 0 auto;
// max-width: 940px;
}
@media screen and (max-width: 768px) {
body { padding-top: 0px; }
}
.container {
max-width: 850px;
}
.col-md-10 > div
{
background-color:#ffffff;
border: 1px solid #1c263c;
border-radius:3px;
}

Reload the page. Now it looks better:

Screen Shot 2015-08-13 at 9.28.57 PM

So using this menu we can now create lists, but we can’t edit or delete them.

For that we cold just construct another level of drop-down sub-menus, but that would make it somewhat painful to use.

Instead we can combine edit/delete function with a list title. Here is how.

First, add pate_title method to the ApplicationHelper (app/helpers/application_helper.rb):

module ApplicationHelper
  def image_exists?(image)
    img = "#{Rails.root}/app/assets/images/#{image}"
    File.exists?(img)
  end
  def page_title(name)
    pencil = "<i class='fa fa-pencil'></i>"
    trash = "<i class='fa fa-trash-o'></i>"
    html = ''
    html << "<h4 class='text-primary'>"
    html << "#{name} | "
    html << link_to(pencil.html_safe,  {:action => "edit"})
    html << " | "
    html << link_to(trash.html_safe, name, { :method => "delete", :confirm => 'Are you sure?'})
    html << "</h4>"
    html.html_safe
  end
end

This method accepts name of a page as a parameter and prints a single line of html code. It is an h4 tag with the name of a page and two font awesome icons – one for edit, one for delete.

Now we need to update app/views/lists/show.html.erb to include this new helper. Also we are going to control layout of the page with the Bootstrap grid – hence ‘col-md-…’ classes.

<div class="row" >
  <div class="col-md-3 col-heading">
    <%= page_title(@list.name) %>
   </div>
<div class="col-md-9">
</div>
</div>
<div class="row">
  <div class="col-md-12">
    <p>
</p>
    <table class="table-fixed" width="820">
    <% @companies.each do |company| %>
      <% if image_exists?("#{company.ticker}_mma.png") %>
        <tr>
          <th><%= company.ticker %></th>
          <th><%= company.name %></th>
          <th><%= company.industry %></th>
        </tr>
        <tr>
            <td colspan=3><%=image_tag("#{company.ticker}_mma.png") %><%=image_tag("#{company.ticker}_range.png") %></td>
        </tr>
      <% end %>
    <% end %>
    </table>
  </div>
</div>

Reload http://localhost:3000/lists/test and you will see name of the list with a pen and a trashcan icons next to it.

Screen Shot 2015-08-17 at 9.33.18 PM

Let’s add a background colour and a border to the title. Edit application.scss again and style for col-heading class:


.col-heading {
     background-color:#313c53;
     border: 1px solid #1c263c;
     border-radius:3px;
}

Reload the page:
Screen Shot 2015-08-16 at 11.05.51 PM

The last thing for today is to assign the root_path – it will be linked with the site logo and with the home button. In the future it will host financial indices charts. So for now let’s scaffold index views and controller only – no model as yet:


vint@belka:~/sscanner/app/views$ rails g erb:scaffold index
create app/views/indices
create app/views/indices/index.html.erb
create app/views/indices/edit.html.erb
create app/views/indices/show.html.erb
create app/views/indices/new.html.erb
create app/views/indices/_form.html.erb

vint@belka:~/sscanner/config$ rails g scaffold_controller index

Now we can update config/routes.rb with the route to the root:


Rails.application.routes.draw do

...
root :to => 'indices#index'
end

Update indices_controller and comment out ‘@indices = Index.all) in the index method:


class IndicesController < ApplicationController
before_action :set_index, only: [:show, :edit, :update, :destroy]

# GET /indices
# GET /indices.json
def index
# @indices = Index.all
end

Update index.html.erb to display a single blank line:



<p> &nbsp; </p>

Now you can navigate to http://localhost:3000/ and you will get a navigation bar and an empty content below.

To be continued...

Advertisements
Building Financial Stock Scanner with Ruby on Rails and R. Part 17. Navigation.

One thought on “Building Financial Stock Scanner with Ruby on Rails and R. Part 17. Navigation.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s