rails 4.1: styling an app with bootstrap and font-awesome

Install the bootstrap gem:

gem "bootstrap-sass", "~> 3.3"
gem "font-awesome-rails", "~> 4.3"

Rename app/assets/stylesheets/application.css to application.css.scss, so you an add @import statements to it

Replace the entire content of the file with:

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

Once you do this, you will need to also import the particular stylesheets your app is compose of, example (importing projects.scss:

@import "projects";

At this point you have the bootstrap css already applied to your pages. It is recommended you apply at least this div wrapper to your app/views/layouts/application.html.erb file:

<body>

  <div class="container">
  <% flash.each do |key, message| %>
    <div><%= message %></div>
  <% end %>

  <%= yield %>
</div>

</body>

An example of a styled link with bootstrap:

<%= link_to “New Project”, new_project_path, class: “new” %>

And the CSS (inside application.css.scss):

a.new {
  @extend .btn;
  @extend .btn-success;

  &:before {
    font-family: "FontAwesome";
    @extend .fa-plus;
    padding-right: 0.5em;
  }
}

This is an example on how to create a decent looking action buttons:

<ul class="actions">
  <li><%= link_to "Edit Project", edit_project_path(@project),
    class: "edit" %></li>

  <li><%= link_to "Delete Project", project_path(@project),
     method: :delete,
     data: { confirm: "Are you sure you want to delete this project?" },
     class: "delete" %></li>
</ul>

And the associated css:

a.edit {
  @extend .btn;
  @extend .btn-primary;

  &:before {
    font-family: "FontAwesome";
    @extend .fa-pencil;
    padding-right: 0.5em;
  }
}

a.delete {
  @extend .btn;
  @extend .btn-danger;

  &:before {
    font-family: "FontAwesome";
    @extend .fa-trash;
    padding-right: 0.5em;
  }
}

This is an example of how to style your alert messages (in application.html.erb):

<% flash.each do |key, message| %>
  <div class="alert alert-<%= key %>">
    <%= message %>
  </div>
<% end %>

And the CSS associated with it:

.alert-notice {
  @extend .alert-success;
}

.alert-alert {
  @extend .alert-danger;
}

Example of how to add a top nav bar:

<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
  <div class="container">
    <div class="navbar-header">
      <%= link_to "Ticketee", root_path, class: "navbar-brand" %>
      <button type="button" class="navbar-toggle collapsed"
        data-toggle="collapse" data-target="#collapse">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
    </div>

    <div class="collapse navbar-collapse" id="collapse">
      <ul class="nav navbar-nav">
        <li class="<%= "active" if current_page?("/") %>">
          <%= link_to "Home", root_path %>
        </li>
      </ul>
    </div>
  </div>
</nav>

And the CSS associated with it:

body {
  padding-top: 70px;
}

If you end up using the nav bar code above, you are going to need a bit of javascript to make the menu work on mobile platforms (otherwise the menu won’t open). Make sure to include this line in your app/assets/javascripts/application.js file:

//= require bootstrap-sprockets

Making your app responsive

Include the following inside your application.html.erb <head> tags:

<meta name="viewport" content="width=device-width, initial-scale=1">

Also, create a dedicated stylesheet file to include your responsive styles (assets/stylesheets/responsive.scss), and import it:

@import "responsive";

Inside there, you can place all your scss related to media queries:

@media(max-width: 500px) {
  p { color: red }
}

rails 4.1 helpers

helpers are methods inside your app/helpers folder, and they are available to your views usage. Every controller gets one (if the controller gets created via a generator).

Example of an application helper:

module ApplicationHelper
  def title(*parts)
    unless parts.empty?
      content_for :title do
        (parts << "Ticketee").join(" - ")
      end
    end
  end
end

The * means any argument passed will be available as an array

This particular helper can be called via this example code (in your application.html.erb template):

<title>
  <% if content_for?(:title) %>
    <%= yield(:title) %>
  <% else %>
    Ticketee
  <% end %>
</title>

And, the particular pages or sub-templates that call this:

<% title(@project.name, "Projects") %>

rails 4.1: creating a CRUD resource feature

Create the test first, inside your specs/feature/ folder:
spec/features/your_feature_name_spec.rb

Sample content for that file:

require "rails_helper"

RSpec.feature "Users can create a project" do
  scenario "with valid attributes" do
    visit "/"

    click_link "New Project"

    fill_in "Name", with: "Some Project name"
    fill_in "Description", with: "Some project description"
    click_button "Create"

    expect(page).to have_content "Project has been created."
  end
end

The test will fail when you run:

bundle exec rspec

If you want to run only one:

bundle exec rspec spec/features/creating_projects_spec.rb

You need to create the db in order for them to pass

bundle exec rake db:create

To make the test pass, you need to build a route from the “/” request

Inside config/routes.rb, add the following line:

root "projects#index"

Create your controller (use plurals! singular names are for models)

rails g controller projects

And, your models (by default, the columns will be string)

rails g model project name description
bundle exec rake db:migrate

Now, inside your src/app/controllers/projects_controller.rb, create your controller methods:

class ProjectsController < ApplicationController
  def index
  end

  def new
    @project = Project.new
  end

  def create
    @project = Project.new(project_params)
  
    if @project.save
      flash[:notice] = "Project has been created."
      redirect_to @project
    else
      flash.now[:alert] = "Project has not been created."
      render "new"
    end
  end
  def show
    @project = Project.find(params[:id])
  end

  def edit
     @project = Project.find(params[:id])
  end

  def update
     @project = Project.find(params[:id])
     if @project.update(project_params)
      flash[:notice] = "Project has been updated."
      redirect_to @project
     else
      flash.now[:alert] = "Project has not been updated."
      render "edit"
     end
  end

  def destroy
     @project = Project.find(params[:id])
     @project.destroy
     flash[:notice] = "Project has been deleted."
     redirect_to projects_path
   end

  private
  def project_params
    params.require(:project).permit(:name)
  end
end

Also, build your view templates:

/src/app/views/projects/index.html.erb

/src/app/views/projects/new.html.erb

/src/app/views/projects/show.html.erb

Example of the way you display model information inside any of the ERBs above:

<h1><%= @project.name %></h1>
<p><%= @project.description %></p>

Create the form, usually inside a partial, so you can reuse the code for Create and Edit views (inside app/views/projects/_form.html.erb):

<%= form_for(project) do |f| %>
  <% if project.errors.any? %>
   <div id="error_explanation">
     <h2><%= pluralize(project.errors.count, "error") %>
     prohibited this project from being saved:</h2>

     <ul>
       <% project.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
       <% end %>
     </ul>
   </div>
  <% end %>

  <p>
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </p>

  <p>
    <%= f.label :description %><br>
    <%= f.text_field :description %>
  </p>

  <%= f.submit %>
<% end %>

Inside your view, since you were looking for a link in this particular test, you can put the following content to make the test pass:

<%= link_to “New Project”, new_project_path %>

But now we need to define a route for that link (inside your config/routes.rb file:

resources :projects

By adding that resource, you are basically accessing seven potential actions on your page:

GET/projectsindex
POST/projectscreate
GET/projects/newnew
GET/projects/:idshow
PATCH/PUT/projects/:idupdate
DELETE/projects/:iddestroy
GET/projects/:id/editedit

If you want Flash messages to display in the page, you need to include this on your views/layout/application.hml.erb, or in the page where you need them:

<% flash.each do |key, message| %>
  <div><%= message %></div>
<% end %>

Basic validation

Add the following to your model:

class Project < ActiveRecord::Base
  validates :name, presence: true
end

Index page

The rspec (spec/features/viewing_projects_spec.rb) will look like this:

require "rails_helper"

RSpec.feature "Users can view projects" do
  scenario "with the project details" do
    project = FactoryGirl.create(:project, name: "Sublime Text 3")

    visit "/"
    click_link "Sublime Text 3"
    expect(page.current_url).to eq project_url(project)
  end
end

Before you run you new test, make sure you add the project factory, inside spec/factories/project_factory.rb:

FactoryGirl.define do
  factory :project do
    name "Example project"
  end
end

A typical index.html.erb view page will look like this:

<h1>Projects</h1>

<%= link_to “New Project”, new_project_path %>

<div id=“projects”>

  <% @projects.each do |project| %>

    <h2><%= link_to project.name, project %></h2>

    <p><%= project.description %></p>

  <% end %>

</div>

Updating section

Add the following test in spec/features/editing_projects_spec.rb

require "rails_helper"

RSpec.feature "Users can edit existing projects" do
  before do
    FactoryGirl.create(:project, name: "Sublime Text 3")

    visit "/"
    click_link "Sublime Text 3"
    click_link "Edit Project"
  end

  scenario "with valid attributes" do
    fill_in "Name", with: "Sublime Text 4 beta"
    click_button "Update Project"

    expect(page).to have_content "Project has been updated."
    expect(page).to have_content "Sublime Text 4 beta"
  end
  scenario "when providing invalid attributes" do
    fill_in "Name", with: ""
    click_button "Update Project"
    expect(page).to have_content "Project has not been updated."
  end
end

Then, add a link to the edit page on your show.html.erb page:
<%= link_to “Edit Project”, edit_project_path(@project) %>

And the actual template (app/views/projects/edit.html.erb):

<h1>Edit Project</h1>
<%= render "form", project: @project %>

Delete section

Create your test at: spec/features/deleting_projects_spec.rb

require "rails_helper"

RSpec.feature "Users can delete projects" do
  scenario "successfully" do
    FactoryGirl.create(:project, name: "Sublime Text 3")

    visit "/"
    click_link "Sublime Text 3"
    click_link "Delete Project"

    expect(page).to have_content "Project has been deleted."
    expect(page.current_url).to eq projects_url
    expect(page).to have_no_content "Sublime Text 3"
  end
end

In your show.html.erb template, you need to add the delete action:

<%= link_to "Delete Project",
    project_path(@project),
    method: :delete,
    data: { confirm: "Are you sure you want to delete this project?" }
%>

rails projects workflow

setup your rails vagrant env and add git, by following the instructions in this page

setup your IDE to be connected to your vagrant instance (inside Vagrantfile):

config.vm.synced_folder "./src", "/home/vagrant/your_project"

setup the initial rails app:

rails new myapp

setup the bitbucket repo

config and initialize your git repo:

git config --global user.name "your name"
git config --global user.email your@email.com
git init
git add .
git commit -am "initial commit"
git remote add origin https://youruser@bitbucket.org/youruser/yourrepo.git
git push origin master -u 

setup your testing env:

put the following gems in your Gemfile, in the development:, test: section:

gem "rspec-rails", "~> 3.2.1"

also, add capybara and factory girl:

group :test do
  gem "capybara", "~> 2.4"
  gem "factory_girl_rails", "~>4.5"
end

Update your gems with the additions by running:

bundle update

And install rspec afterwards:

rails g rspec:install

If you get an error message about uglifier being missing, you will have to install nodejs on your system, and run the command again afterwards (there is a js dependency on nodejs)

sudo apt-get install nodejs

make sure you commit your changes at this point:

git add .
git commit -am "setup tests"
git push

database configuration

Install postgresql in your vagrant box:

sudo apt-get update
sudo apt-get install postgresql postgresql-contrib
sudo apt-get install libpq-dev
sudo -i -u postgres psql ALTER USER postgres WITH PASSWORD 'somepassword'

Modify your Gemfile to include the pg gem:

gem ‘pg’

bundle install

Change your config/database.yml file from sqlite3 to postgresql:

default: &default

  adapter: postgresql

  pool: 5

  timeout: 5000

development:

  <<: *default

  database: ticketee_development

  username: vagrant

Also, make sure the test database gets a different name from the development one:

test:
< database: ticketee_test

username: vagrant

Get the vagrant user to be able to run the rails app:

sudo su - postgres
psql
CREATE ROLE vagrant superuser;
alter ROLE vagrant WITH LOGIN;

make sure you tests are running:

setup an rspec test (see the beginning of this post for details)

bundle exec rake db:create
bundle exec rspec