Categories
bootstrap Rails

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 }
}
Categories
Rails

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") %>
Categories
Rails

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?" }
%>
Categories
Rails

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