Rails 4.1: deploying to Heroku

download the toolbelt kit:

$ wget -O- https://toolbelt.heroku.com/install-ubuntu.sh | sh

login:

$ heroku login

create your app:

$ heroku apps:create [your app name]

or, if you already have an app in Heroku:

heroku git:remote -a ticketee-demo

Add the rails_12factor gem, to take care of the standard points a web app should cover. In your gemfile:

gem “rails_12factor”, group: :production

Also, in the same gemfile, to make sure you are using the same Ruby version as in development, add the following line (right under the “source” first line):

source 'https://rubygems.org'
ruby "2.2.1"

Also, to specify a better production server (than the redbrick default), add the following line at the bottom of your gemfile:

gem "puma", group: :production

If you are going to use the Puma server in production, in Heroku, you will also need to add a Procfile in your root folder, with the following content:

web: bundle exec puma -t 5:5 -p ${PORT:-3000}
  -e ${RACK_ENV:-development}

Then, just rebuild your gemfile.lock by running:

bundle install --without=production

And finally, to push to Heroku, checking your code, and push it:

$ git add .
$ git commit -am "setup heroku ruby version and server"
$ git push heroku master

Your code is now there, but you will also need to get the database ready and run all the needed migrations:

heroku run rake db:migrate

If you have any seed files for your db, this is also a good time to run it:

heroku run rake db:seed

If anything goes wrong, you can always see what is happening in the logs:

heroku logs

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

bootstapping an angular.js app on top of rails

Complete example here

– The following will skip jquery and turbolinks:

rails new [your-app-name-here] –skip-javascript

– But now you need to manually create the following file: app/assets/javascripts/application.js

– And include the following in the file contents:

//= require_tree .

– put all your angular app files inside app/assets/javascripts, and include the following in your app/views/layouts/application.html.erb template:

<%= javascript_include_tag ‘application’ %>

– Your app/views/layouts/application.html.erb will also be the home page of your single page app, so make sure you put all your initial HTML code in there, and get rid of the default  <%= yield %> in there.

– Add the following to your application controller:

def angular

render ‘layouts/application’

end

– and in your config/ routes.rb file, add a route to it:

root to: ‘application#angular’

– install bower, if you haven’t done so already:

npm install -g bower

– initialize it inside your rails project:

bower init

– create a .bowerrc file, with the following, to tell bower where you are storing your js dependencies:

{ “directory“:“vendor/assets/bower_components” }

– install the dependencies you need, and tell bower to save them in your config file:

bower install angular angular-ui-router bootstrap –save

– now that the libraries you need are installed, modify your app/assets/javascripts/application.js file to call them at page load time:

//= require angular

//= require angular-ui-router

//= require_tree .

– to add the bootstrap CSS, add the following line to the comments section on app/assets/stylesheets/application.css

*= require bootstrap/dist/css/bootstrap

*= require_tree .

Important: that goes inside the header /* comments */

At this point, you have all your js / css dependencies managed by bower, and minified and pulled in the right places by the rails app.

– place your template files inside public/templates, and modify your config file as follows:

.config([

'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {

$stateProvider
.state(‘home’, {
url: ‘/home’,
templateUrl: ‘templates/_home.html’,
controller: ‘MainCtrl’
}).state(‘posts’, {
url: ‘/posts/{id}’,
templateUrl: ‘templates/_posts.html’,
controller: ‘PostsCtrl’
});

$urlRouterProvider.otherwise(‘home’);
}])

– move your JS to folders inside app/assets/javascripts (create mainCtrl.js inside the home folder, and postsCtrl.js inside the posts folder), so the only thing left inside app.js is the configuration and routes.

– generate your models:

rails generate model Post title:string link:string upvotes:integer

rails generate model Comment body:string upvotes:integer post:references

rake db:migrate

– declare your associations inside your models:

class Post < ActiveRecord::Base

  has_many :comments

end

– if you have children models from one of your models, and you want the children to be returned as part of your JSON, put the following method inside your model as well:

def as_json(options = {})

     super(options.merge(include: :comments))

end

 – setup your base routes:

root to: ‘application#angular’

resources :posts, only: [:create, :index, :show] do

   resources :comments, only: [:show, :create] do

      member do put ‘/upvote’ => ‘comments#upvote’

   end

end

member do put ‘/upvote’ => ‘posts#upvote’ end end

Nesting resources will create urls like this: posts/1/comment/3

By specifying member do, you are also simplifying some of the resources out of the deep nesting

 – using “rake routes” at this point will tell you what kind of routes you have defined

– now is time to create your controllers, make sure you skip the templates and assets creation when doing so:

rails generate controller Posts –skip-assets –skip-template-engine

rails generate controller Comments –skip-assets –skip-template-engine

 – add the following line to your application controller, so it responds back in json format:

respond_to :json

– in the newer versions of rails, you will need to add this gem to your Gemfile to get that respond_to functionality:

gem ‘responders’, ‘~> 2.0’

 – tell your controllers which parameters are allowed, and also create the basic controller methods:

def index
respond_with Post.all
end

def create
respond_with Post.create(post_params)
end

def show
respond_with Post.find(params[:id])
end

def upvote
post = Post.find(params[:id])
post.increment!(:upvotes)

respond_with post
end

private
def post_params
params.require(:post).permit(:link, :title)
end
end

– back to your front end: setup your factory to be able to retrieve all of your records by means of hitting your index method (see how $http is injected, and how the getAll function is implemented as a promise)

angular.module(‘flapperNews’)

.factory(‘posts’, [‘$http’, function(){

  var o = {

    posts: []

  };

  o.getAll = function() {

    return $http.get(‘/posts.json’).success(function(data){

      angular.copy(data, o.posts);

    });

  };

  return o;

}]);

– now, if you want your view to refresh with the server data every time the UI calls “home”, you need to set your stateProvider with the “resolve” property:

$stateProvider

    .state(‘home’, {

      url: ‘/home’,

      templateUrl: ‘templates/_home.html’,

      controller: ‘MainCtrl’,

      resolve: {

            postPromise: [‘posts’, function(posts){

               return posts.getAll();

           }]

      }

    })

 – to be able to create post, add the following to your post service:

o.create = function(post) { return $http.post(‘/posts.json’, post).success(function(data){ o.posts.push(data); }); };

– and, in your main controller:

$scope.addPost = function(){ if(!$scope.title || $scope.title === ) { return; } posts.create({ title: $scope.title, link: $scope.link, }); $scope.title = ; $scope.link = ; };

– by default, rails has protection against fake posts, so in order to save the data you are posting, you will need to add the following gem (otherwise you will be getting “422 unprocessable entry” error messages

gem ‘angular_rails_csrf’

– to add user authentication via Device, you need to install the gem first:

gem ‘devise’, ‘~> 3.4.0’

– after you bundle install, initialize it, and create the user’s model:

rails generate devise:install

rails generate devise User

– if you need to, you can add more fields to the default devise model, which only contain email and password by default. We will also make the username unique:

rails generate migration AddUsernameToUser username:string:uniq

– in order to integrate devise with the front end, you can install the following js package helper via bower:

bower install angular-devise –save

– in application.js, require the newly installed package:

//= require angular-devise

– and inject the module in the main app:

angular.module(‘flapperNews’, [‘ui.router’, ‘Devise’]).

# note: when you try your registration / login forms, if there is a devise error, it may manifest in the front end as a 422 error message, you need to handle the errors as they come from the server

– to secure your posts savings (or any other controller actions for that matter) you can now use the following:

class PostsController < ApplicationController before_filter :authenticate_user!, only: [:create, :upvote]

– if you want to associate two of the models you are working with (in this case posts and users), run the following command, that will create the db migration necessary to do the work:

rails g migration AddUserRefToPosts user:references

rails g migration AddUserRefToComments user:references

rake db:migrate

– if you do so, you need to enhance the models to reflect that association:

class Comment < ActiveRecord::Base

  belongs_to :user

– and your update and create methods also need to include the relationship, so rails knows at save time what users are assigned what records:

def create

respond_with Post.create(post_params.merge(user_id: current_user.id))

end

Angularjs with Ruby On Rails app from scratch

# Assuming node is already installed, install the stuff needed to bootstrap the app:

npm install -g yo

npm install -g generator-angular

# create your app (the name of the folder will be the name of your app):

mkdir myapp; cd myapp;

yo angular

# in the series of questions, pick bootstrap (to make it easier on you)

# get git going:

git init; git add .

git commit -am “initial commit”

# (optional): open your Gruntfile.js file, and edit out the following line:

tasks: [‘newer:jshint:all’], and also tasks: [‘newer:jshint:test’, ‘karma’]

to:

tasks: [] # to avoid running jshint for each edit, and the test suite with any new test / changes

# bowel.json will contain all app dependencies, if you need anything more, you add it there

# package.json will contain all the development dependencies

# app/index.html has your single page app bootstrapped HTML page

# to make sure we are up and running, run:

grunt serve # you shall have a basic page running at this point

rails: handling errors for routes / controllers not found

Create a controller that will handle errors:

class ErrorsController < ApplicationController

  def routing

    render_404

  end

  def render_404

    render :json => {:status => “error”, :response_code => 404, :message => “no route found that match your request, please check your parameters and try again”, :results => {} }.to_json

  end

end

And then, in your router, include the following line (at the end, after all the resources are listed, so you can catch stuff in there):

  resources :some_resource_here

  # catch any unmatch paths:

  match ‘*a’, :to => ‘errors#routing’

another option

you can redirect to another page inside your controller, like this:

before_action :set_project, only: [:show, :edit, :update, :destroy]
... 

def set_project
   @project = Project.find(params[:id])
   rescue ActiveRecord::RecordNotFound flash[:alert] = "The  project you were looking for could not be found."
   redirect_to projects_path
end


Rails: barebones site template from the ground up, version II, using rails 4

Assuming you have rails 4 already running in your system. And git / heroku also ready to go.

$ rails new your-project

Add a bunch of static pages, remove the default page:
remove public/index.html (if there)
$ rails generate controller StaticPages home help about contact_us –no-test-framework
Inside config/routes.rb”
– Comment out the static pages you don’t need, or add the right routes to the ones you want:
# get “contact_us” => “static_pages#contact_us” (example)
– Add a default page as home:
root :to => “static_pages#home”

Add devise, and their views:
Modify your gem file to contain the following lines:
gem ‘devise’
gem ‘sqlite3’, group: [:development, :test]
gem ‘pg’, group: :production

$ bundle install –without production
$ rails generate devise:install # Follow the instructions printed

$ rails generate devise user
$ rake db:migrate
To be able to modify the devise views as any other template:
$ rails generate devise:views

Add source control, push to Heroku
$ git init
$ git add .
$ git commit -am “Initial commit”
$ heroku login # if you haven’t
$ heroku create [name of your app]
$ git push heroku master
$ heroku run rake db:migrate

Static assets

If images and such are not showing on heroku production, you may have to pre compile them locally, using the following command:

RAILS_ENV=production bundle exec rake assets:precompile

This will generate the static assets inside the public directory. Ideally you should use a CDN to serve your images, but this should be ok for small projects.

Extra gems you may need for Rails 4.0

gem ‘rails_12factor’, group: :production

That will take care of any errors related to assest