Sunday, June 5, 2016

Devise Two-Step

DeviseTwoStep

Finally

Let me first say I am not a Devise expert by any stretch of the imagination. The project I am working on has a requirement to use two different models for authentication and I could not find a complete example of how to setup Devise to do this.

The documentation and several web sites and stack overflow articles I found gave hints on how to do it but I still had questions. So I thought I would write this post to show how I setup a test project to do it from end to end.

My goal is to have two models, user and admin that both are authenticated using Devise. Each model (I'll call it an account from here on out when referring to both) will have a set of pages that are only accessed when the account is logged in.

Additionally these protected pages are only accessible by one of the two accounts (depending on who is loved in). Finally, each account will have it's own (and separate) sign-in page and layout.

Much of the advice on the net recommended using a role based approach to solve this problem, but for many reasons that won't work for the app I will eventually apply this to. So a role based solution is off the table.

I started this investigation at the Devise website{***}. Unfortunately, I could not follow it enough to get a successful implementation. It felt like I was missing something.

The problem I kept running into was I would login as an Admin and when I logged out I would get redirected to the User's login page or if I logged in as the User I could then get to the Admin protected pages when it shouldn't. So something just wasn't clicking.

I next worked through this tutorial with the following deviations:

  • When I copied the devise views into the app I put them in their own directories with this command:
`rails generate devise:views User`
  • I did not do the section on Sending E-Mail and DelayedJob as they were not germane to the problem I was trying to solve

Once I had that going I created my Admin model and migrated my database.

rails generate devise Admin
rake db:migrate

Note: I did not add a name attribute to the Admin model like was done in the tutorial for the User model, as there was no need for it. I also left the registration support in so I could quickly add admin users, but obviously in a real app you wouldn't want to allow that.

I next created the Devise views for the Admin model (I had already copied the user ones when working through the tutorial):

rails generate devise:views Admin

This creates two directories under app/views, users and admins respectively. Under each directory will be sub directories for the different devise features that are supported.

Next I created sub directories in the controllers directory called users and admins and in those sub directories I created a controller for registration and session. This has to be done in order to customize registration and authentication for each of the models.

So for the User side I created a directory under controllers called ... wait for it ... users.

The registration controller (named registrations_controller.rb) looks like this:

class Users::RegistrationsController < Devise::RegistrationsController
  # disable default no_authentication action
  skip_before_action :require_no_authentication, only: [:new, :create, :cancel]
  
  protected

  def sign_up(resource_name, resource)
    # just overwrite the default one
    # to prevent auto sign in as the new sign up
  end
end

The session controller (named sessions_controller.rb) looks like this:

class Users::SessionsController < Devise::SessionsController
  # disable default no_authentication action
  skip_before_action :require_no_authentication, only: [:new, :create, :cancel]
end

Next is the registration controller for the Admin model (same name as above):

class Admins::RegistrationsController < Devise::RegistrationsController
  # disable default no_authentication action
  skip_before_action :require_no_authentication, only: [:new, :create, :cancel]
  
  protected

  def sign_up(resource_name, resource)
    # just overwrite the default one
    # to prevent auto sign in as the new sign up
  end
end

and the session controller for Admin (same name as above):

class Admins::SessionsController < Devise::SessionsController
  # disable default no_authentication action
  skip_before_action :require_no_authentication, only: [:new, :create, :cancel]
  # now we need admin to register new admin
  #prepend_before_action :authenticate_scope!, only: [:new, :create, :cancel]

  protected


  # def sign_up(resource_name, resoure)
  #   # just overwrite the default one
  #   # to prevent auto sign in as the new sign up
  # end
end

We are almost there I promise. Next are the changes to the routes.rb file:

Rails.application.routes.draw do
#  devise_for :admins
  devise_for :admins, module: 'admins', controllers: {sessions: 'admins/sessions', registrations:'admins/registrations'}
#  devise_for :users
  devise_for :users, module: 'users', controllers: {sessions: 'users/sessions', registrations:'users/registrations'}

  # These are the protected routes.
  # The pages controller is for the user model and the
  # admin_pages is for the admin model
  get '/secret', to: 'pages#secret', as: :secret
  get '/adminsecret', to: 'admin_pages#secret', as: :adminsecret
  get '/userhome', to: 'pages#index'
  get '/adminhome', to: 'admin_pages#index'

  # Define the root for when a user is authenticated
  authenticated :user do
    root 'pages#index', as: :authenticated_user_root
  end

  # define the root for when an admin is authenticated
  authenticated :admin do
    root 'admin_pages#index', as: :authenticated_admin_root
  end

  # default root (should never use this)
  root to: 'pages#index'
end

When you run the devise generator it will add a devise_for call in your routes. I have left those in (but commented out) to show what the default is so you can see how I adjusted the devise routes for the different models to point to their respective controllers.

Also you can see the "secret" pages and how they are defined as regular routes. I'll show them shortly. Finally I defined scoped routes for the "roots" for the different models.

Next is the application controller. This is where the "glue/magic" happens. I have included comments in the code so you can see what I was doing/thinking.

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?

  respond_to :html, :json

  # the layout should be specified by the resource (i.e. admin or user)
  layout :layout_by_resource

  protected

  # ensure name is allowed
  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) << :name  
    devise_parameter_sanitizer.for(:account_update) << :name
  end

  # if we are using a devise controller then we can check the
  # resource_name and return the appropriate layout, this can 
  # also be done at the controller level
  def layout_by_resource
    if devise_controller? && resource_name == :admin
      'adminlayout'
    elsif devise_controller? && resource_name == :user
      'userlayout'
    else
      'application'
    end
  end

  # Specify where to go after successful login again this is 
  # dictated by the resource that logged in
  def after_sign_in_path_for(resource)
    if devise_controller? && resource_name == :admin
      authenticated_admin_root_path
    elsif devise_controller? && resource_name == :user
      authenticated_user_root_path
    else
      root_path
    end
  end

  # Specify where to go after successful logout again this is 
  # dictated by the resource that logged in
  def after_sign_out_path_for(resource)
    if devise_controller? && resource_name == :admin
      new_admin_session_path
    elsif devise_controller? && resource_name == :user
      new_user_session_path
    else
      'application'
    end
  end

end

Next up is the protected content. For the user, he is directed to the pages index when properly authenticated. For the admin, she is directed to the admin_pages index when successfully authenticated. The controller for pages looks like this:

class PagesController < ApplicationController
  before_action :authenticate_user!

  layout 'userlayout'
end

By adding the before_action to the controller all routes will be forced to be authenticated.

The controller for the pages accessible to the admin look very similar:

class AdminPagesController < ApplicationController
  before_action :authenticate_admin!
  layout 'adminlayout'
end

Again all routes in this controller are protected by the before_action but this time the session must be a logged in admin.

With that said we are done. I modified the various views to indicate which one was being shown when you loaded it up. Check the completed code here here for what those views look like.

In summary, I guess I was just too dense to understand it but the key to all of this turned out to be how the application controller is configured.

Sunday, April 24, 2016

Editing Date in Rails MVC

Date_Select

I was working on a simple rails app to act as an administrative console for a larger project I am working on. The main application is a ReactJS based client so I haven't been doing much with Rails MVC, so my memory on using Rails' form helpers was a bit rusty.

I decided for the administrator's console, however I would use a standard Rails MVC design. Most everything came back quickly but one thing I had trouble with was how to edit dates using the Rails data_select tags. To get it to work end-to-end I had to piece several different pieces of information together.

So I thought I would write this short post to capture all that information in one place.

First, in the view for the form I configure the date_select like this:

<%= form_for goal, url:{action: 'goal_update'}, html: {class: 'form-horizontal'} do |f| %>
    <div class="form-group">
      <%= f.label(:start, 'Start:', class: 'col-sm-2 control-label') %>
      <div class="col-sm-10">
        <%= f.date_select(:start, class: 'form-control', disabled: disabled) %>
      </div>
    </div>
   <div class="form-group">
      <div class="col-sm-offset-2 col-sm-10">
        <%= f.submit('Save', class: 'btn btn-primary') %>
      </div>
    </div>
<% end %>

In this view code I have passed in a goal object and on line 1 I setup the form for it. Note: I am using Bootstrap here for most of the styling, with a little bit of my own CSS thrown in.

This goal has a start attribute which I setup the form element for on line 5.

The disabled attribute is passed into the view as well so I can use the same form code for when the administrator just wants to view the goal versus edit the goal.

Basically the view part of this is very simple and just requires following the syntax for the date_select tag.

It's a little more interesting we when get to the controller though.

Here is the relevant parts of goal_update from the controller. Note: goal_update is defined in my routes to point to a specific controller and action.

  def goal_update
    goal_id = params[:goal][:id].to_i
    @goal = Goal.where(id: goal_id).first
    ...

    @goal.start = date_from_params(params[:goal],'start')

    ... 
  end
  
  def date_from_params(hash, field_name)
    begin
      date_hash = %w(1 2 3).map { |e| hash["#{field_name}(#{e}i)"].to_i }
      return Date.civil(date_hash[0], date_hash[1], date_hash[2])
    rescue
      return nil
    end
  end

So you can see in line 2 all the information about the goal is passed in the params hash under the :goal key.

In line 2, I get the id of the goal to update and then load it up in line 3. Yes, I need more error checking here but thats not important for this post

Later in the method on line 6, I get the start date by calling a helper method I wrote to safely get the date from the params hash.

This helper method is shown starting at line 11.

Its important to note how the date values come in via the params object. The relevant portion of the goal hash will look like the following when it is submitted to the controller:

"start(1i)"=>"2016", "start(2i)"=>"4", "start(3i)"=>"27",

So the helper method takes the goal hash params[:goal] and the key for the date we want to construct 'start'. It uses the map function on line 13 to pull out the individual components and then on line 14 converts this to a Date which is returned. If anything goes wrong we catch any exceptions and return nil to indicate the date was not set.

And there you go.

One final note: It has been my intention to write a post once a week and as you can tell I haven't been able to keep that up over the last few months. I even thought I would just write a post once every two weeks, but that hasn't worked out very well either.

The problem is I need to balance project development with keeping this blog active. It's hard to do both. Right now project development is the higher priority, so for the foreseeable future I expect these posts to be a bit erratic. However, I am committed to keeping this blog active.

I do have some cool stuff I am doing in my currently active projects that I hope to write about in the near future.

So stay tuned.

Sunday, March 27, 2016

Avoiding Massive VCs

One of my biggest beefs with iOS development has been the lack of best practices particularly when dealing with what code belongs in the ViewController.

For my development, I like to design with these goals in mind:

  • Each class/method has one responsibility
  • Functions should not have side effects.
  • Well understood functionality should be abstracted away into their own classes.

When writing UIViewControllers, however, I find it tough to adhere to these principles. The reason, I think, is the fact that because the UIViewController essentially is the code for a view in your app it takes on many responsibilities, such as manipulating the view's widgets, holding state for the widgets, setting values on the widgets, and responding to actions taken on a widget.

I see examples of this type of design all over the Internet. This munging of the responsibilities leads to what has been commonly called the "Massive View Controller" design pattern.

There has been a lot written about how to avoid this pattern and why it is a bad thing, so this post may not be very interesting to those who are well versed in this anti-pattern.

For me, though, I have come to a point in my rewrite of my app, Pain Logger, that patterns are starting to emerge to mitigate this issue that I can use for future apps so I thought I would write these up as minimum for my own reference in the future.

If this has provides any value to you I would love to hear about it.

Avoiding Massive View Controllers

To avoid the Massive View Controller problem I have began using four patterns:

  • View Models
  • Unidirectional data flow
  • Services
  • Protocols Extensions

Note: For this discussion when I say "view" I mean the UIViewController as seen on a storyboard along with it's actual UIViewController class representation. My goal is to reduce the code in these view controllers as much as possible to meet the goals I strive for as outlined above.

View Models

The intent of a view model is to hold state for the view. That way when a particular value needs to be displayed on the view, the view asks it's view model for the data rather than storing the data in the view itself. This allows the view model to do two things:

  1. Hold the data in it's raw format
  2. Transform the data into exactly what the view needs to display.

A simple example would be a date. The view model can hold the raw NSDate object and when the corresponding view needs to display the date it can ask the view model for correctly formatted date (i.e. a string).

With this pattern, business logic (in this case formatting of a NSDate into a String) is moved out of the view code and into the view model.

I started my rewrite of Pain Logger thinking I would move all the data manipulation to a view model class for each view controller. In the end that's not exactly how it ended up, but for this discussion I'll keep it simple.

When a view controller first stands up it creates it's corresponding view model.

var viewModel = VC_ViewModel()

Then in the view controller I wire up the widget's action to the control. But the IBAction implementation only updates the view model and calls the method to refresh the UI.

Here is an example where I wired up a UISwitch:

    @IBAction func handleSwitchChanged() {
        viewModel.showGraph = showGraphSwitch.on
        refreshUI()
    }

The refreshUI() method just reads the values from the viewModel and updates the view's controls appropriately. In this case setting the showGraph attribute to true causes another widget in the view to be shown.

This pattern works well for moving the view's state out of the view controller class. But as I worked through this I ran into another pattern that makes it that much more powerful. This pattern is called 'Unidirectional Data Flow'

Unidirectional Data Flow

Unidirectional Data Flow comes from the 'Reactive' world. I first ran into this pattern with my web work using ReactJS. In this pattern a change is made to data via execution of 'actions'. These 'actions' update a central 'store' which then notifies it's listeners that the store was changed.

There are many implementations of this pattern. The one that seems to be getting a lot of traction is the ReSwift project.

One of the problems I ran into with using the 'View Model' pattern was initialization of the state. But when I incorporated that with UDF, things began to clean up a bit.

Now there is a 'global' store that represents the state of the system at any moment in time. The view models no longer hold state for the view, they are responsible for isolating the business logic for formatting the data from the store into the appropriate format for the view.

For example, in Pain Logger, when editing a pain area, a title must be displayed on the view. The view model exposes a computed attribute for the view's title like so:

    var title:String {
        let name = categoryState.categoryName
        if (name.length > 0) {
            return name
        }
        if let cat = category{
            if (cat.isNew()) {
                return "New Pain Area"
            }
        }
        return "Unknown"
    }

In this example categoryState represents the store, so now the view model doesn't hold state, only the store does. When the refreshUI() method is called on the view controller (after being told by the store that it changed) the view model defers to the "store" to get the value of the system state and then provides the view with the correctly transformed data value.

It should be noted that by adding UDF to the design most if not all of the attributes on the view model become computed values. View models now hold no state either. You can see that in the code above where category is used. category is actually another computed value that looks to the store to find the category that is being edited.

Another consequence that occurs by adding UDF is the view controllers now listen to the store for changes, and actions from the view are dispatched to the UDF system rather than calling the refreshUI method directly.

So for example the handleSwitchChanged() method from above becomes:

    @IBAction func handleSwitchChanged() {
        mainStore.dispatch(SetCategoryShowOnGraphAction(showOnGraph:showOnGraphSwitch.on))
    }

In this way most IBAction methods become one liners, very cool.

Services

The next pattern is a little more mainstream. Using this pattern I moved all async operations out to specialized "service" classes. Each "service" uses the singleton pattern (for all of those who don't like this pattern, I still find it useful for this case). Because of the introduction of UDF the service's don't hold state. They just update the stores.

I have services for local and remote data among other things. Since storing to a database should be done off the UI thread I call the correct service for what I want to do. It modifies the database appropriately and updates the store with the changed values after they have been successfully persisted.

Since for the most part these services work asynchronously I can offload the work that needs to be done, then the UDF system kicks in when the response is received.

Protocol Extensions

The final pattern, that I am just getting used to, is protocol extensions. To be honest I didn't fully understand the value of them until I did some more studying of the subject.

I thought that protocols were just like interfaces from Java, and that would be right, until you throw in extensions. That's when things change, A LOT!!

Call me dense or whatever, but it seems to me that only when you start using protocol extensions do you start to get LOTS of reuse.

So the key to this is to define a behavior you want a class to have say like the title property from above. Since the view models don't hold state either I can define a HasTitle protocol like so:

protocol HasTitle {
    var title:String {get}
}

extension HasTitle {
    var title:String {
        let name = categoryState.categoryName
        if (name.length > 0) {
            return name
        }
        if let cat = category{
            if (cat.isNew()) {
                return "New Pain Area"
            }
        }
        return "Unknown"
    }
    
    var category:PLCategory? {
        get {
            return categoryState.selectedCategory
        }
    }
    
    var categoryState:CategoryState {
        get {
            return mainStore.appState.categoryState
        }
    }
}

Now any class can conform to this protocol just by adding it to the class's definition. Because the extension is defined, as well, there is nothing for the class (in this case a view model) to define and it "just gets" the behavior.

This is extremely powerful and I am excited about adding this new found knowledge to my code base.

Conclusion

So there you have it. A brief overview of some of the patterns I have found useful in architecting an iOS app and to avoid the Massive View Controller problem.

Till next time.

Sunday, February 21, 2016

How I learned Capistrano . . . 3

Capistrano3

With my current web app my goal over the past few weeks has been to get it to a stable state so I can just deal with scaling it out.

To do that, I needed to reduce the amount of time I was spending in deploying updates and I needed a repeatable set of steps that I couldn't mess up. That meant I needed to use an automated deployment tool. Since this is a Ruby on Rails (RoR) project, Capistrano seemed the obvoius answer.

I hadn't used it in several years but I thought it wouldn't be too hard to get back up to speed. It turned out that I felt like I was having to relearn it all over again.

So I thought I would write this post to document how I went about getting up to speed again. It turned out to not be that bad.

To start with I ensured I had the prerequisites in this tutorial completed.

Since I already had a running application, steps 1 through 4 (which consist of installing Nginx, PostgreSQL, RVM, Ruby, Rails and Bundler) were already completed.

All I really had to complete was Step 5: Setting up SSH Keys so the server could download from the git repository.

Next I searched the net for tutorials on installing and configuring Capistrano. I soon realized the tutorials were a mashup of versions 2 and 3. It wasn't always clear that the post or tutorial I was looking at was about version 3.

My take away at this point was this wasn't going to be a one afternoon effort.

Additionally, as I feared, each author was very opinionated about how it should be setup and configured. Needless to say, my comfort factor was not high for this endeavor.

So here is how I ended up regrouping and getting it done.

I started at the Capistrano website. Reading through the documentation, it felt like I wasn't getting the whole picture. The documentation is very consise.

Don't get me wrong, I think for someone who knows what they are doing the docs are great, but for me (and my level of expertise at the time) it was confusing. I like to know things like "Why would I want to do this" and "How does this fit into the bigger context of what I am trying to do" but there is little, if any, of that in the docs.

It felt like I wasn't making any progress. What I did learn, however, was the lay of the land for the documentation, the different components involved (even though I didn't know how they connected) and this first nugget:

Nugget 1: Capistrano 3 is based on Rake

One post I read recommended to first get familiar with how Rake tasks are built. This turned out to be a good idea. It gave me a better understanding about the code I was seeing in the Capistrano 3 config files. This was the recommended post and it is well worth reading all the way through.

It was a great start.

After reading that post I came back to the Capistrano documentation. I read more. Specifically I read these sections:

  • Installation
  • Structure
  • Configuration
  • Preparing Your Application
  • Cold Start
  • Before/After Hooks
  • Authentication & Authorisation

I also read the "Advanced Feature - Properties" section and then I looked at some of the plugins.

At this point I felt like I was ready to roll up my sleeves and get started.

A lot of the tutorials I ran across started with building the application and the server, and since I had already done that I won't go into those steps.

Installation

Capistrano installation was fairly straight forward, you just need to make sure you include the right gems. Since this is a RoR app, including the capistrano-rails gem was critical.

Nugget 2: Each gem adds to the list of Capistrano tasks you have available. You will need several of them.

Here is the list of gems I added to the Gemfile

gem 'capistrano'
gem 'capistrano-rails'
gem 'capistrano-bundler'
gem 'capistrano-rvm'

After installing the gems and running bundle update I then 'capified' the app:

bundle exec cap install

Having to do this seems to get glossed over in the Capistrano documentation.

I next added the require statements to the generated Capfile

require 'capistrano/rails'
require 'capistrano/rvm'

Note: To start with I purposefully did not add the nginx or unicorn capistrano gems so I could just concentrate on getting the app to install properly. I also chose to deploy the app to a test directory on the production server so I could compare it to the running application to know that it was working correctly.

At this point I could run cap -T to see the list of available tasks.

Configuration - Deploying the Code

Next, most of the tutorials, will take you into configuring the deploy.rb and production.rb files that got laid down when you 'capified' the app.

The documentation in both of these files is pretty good, but the problem was, at least for me, it was overwhelming. What EXACTLY did I need to set up to get going.

Nugget 3: config/deploy.rb is for common settings and configuration, config/deploy/production.rb is for settings specific to your production deployment and config/deploy/staging.rb is for settings for your staging environment (i.e. a pre-production/test environment). The settings in depoy.rb will be overridden by settings in production.rb or staging.rb depending on what you are deploying.

Next I wrote down the steps I would have to do if I was deploying by hand. These were the steps I wanted my Capistrano deploy setup to do. The steps I needed to accomplish were:

  1. Run local tests
  2. Stop nginx
  3. Update the code
  4. Run Bundle install (if needed)
  5. Run rake db:migrate (if needed)
  6. Compile assets
  7. Link shared folders and files
  8. Restart nginx
  9. Restart unicorn

Knowing what I needed to do helped me figure out what settings I needed to set.

Here are the config settings I ended up with in the config/deploy.rb Note: anything listed in {} needs to be changed to match the server environment being run in.

set :application, '{my_app_name}'
set :deploy_user, '{deploy}'
set :scm, :git
set :repo_url, '{git@...}'
set :deploy_to, '{/home/deploy}'
set :format, :pretty
set :linked_files, %w{config/database.yml}
set :linked_dirs,  %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
set :keep_releases, 5
set :keep_assets, 2
set :tests [{a list of path+file names to the spec tests to run}]

Next I moved to the 'config/deploy/production.rb' file and set these attributes:

set :stage, :production
set :branch, 'master'
set :server_name, '{my server name}'
server '{my server name}', user: '{deploy}', roles: %w{web app db}, primary: true

set :rails_env, :production

set :ssh_options, {
    forward_agent: true,
    auth_methods: %w(password),
    user: '{deploy}'
}

I was finally ready to run the first deploy

cap production deploy

As expected it didn't work, but it was close.

There were some deprecation warnings I needed to fix, I found it was best just to look at the log and fix the issues.

The next problem I encountered was the deployment could not link to the 'database.yml' file from the shared directory. I expected this so I logged into the production server and put the file in place. Note: Capistrano has the ability to create/install the application on a new server. I chose not to go this route for this deployment as all the config files for nginx and unicorn were already in place.

Nugget 4: Review the capistrano output. It is important to fix as many problems as you see in the capistrano output. Some commands that are marked "failed" are ok as some directories may not exist in your project. You can use cap production deply --dry-run if you want to do a trial run of the deployment without actually doing it. This is good for when you make a large or potentially destructive change.

Basically you want to put anything that you can share between projects into the shared directory and symlink them over. One extra one I had to do was a hidden folder for my ssl certificates.

Once I cleaned as many issues up as I could, without doing any custom tasks I had items 3 through 7 working. Now on to the server stuff.

Installation - Controlling the Application Server

I first started by adding the nginx and unicorn gems to the Gemfile:

gem 'capistrano3-nginx'
gem 'capistrano3-unicorn'

I required both in the Capfile:

require 'capistrano/nginx
require 'capistrano3/unicorn' 

I also added the correct deploy steps to the deploy.rb file:

before :deploy, 'nginx:stop'
after 'deploy:publishing', 'deploy:restart'
task :restart do
  invoke 'nginx:restart'
  invoke 'unicorn:reload'
end    

At this point I started getting "sudo: no tty present and no askpass program specified" I fixed this by adding the NOPASSWD option to my deployers group on the server.

sudo visudo

#add this line to bottom of file
deployers ALL=(ALL) NOPASSWD: ALL

This allowed the Nginx service to be controlled by the deployer account.

At this point steps 2, 8 and 9 were complete on my list.

Installation - Running Tests Before Deployment

The final thing to complete was getting the tests running on the local development box prior to allowing the deploy to the server to happen.

To do this I created a custom task named run_tests.cap and placed it in the lib/capistrano/tasks directory. Here is it's contents:

namespace :deploy do
  desc "Runs test before deploying, can't deploy unless they pass"
  task :run_tests do
    test_log = "log/capistrano.test.log"
    tests = fetch(:tests)
    tests.each do |test|
      puts "--> Running tests: '#{test}', please wait ..."
      unless system "bundle exec rspec #{test} > #{test_log} 2>&1"
        puts "--> Tests: '#{test}' failed. Results in: #{test_log} and below:"
        system "cat #{test_log}"
        exit;
      end
      puts "--> '#{test}' passed"
    end
    puts "--> All tests passed"
    system "rm #{test_log}"
  end
end

I got this from a post I read. It should be fairly explanitory. It essentially fetches the tests from the array of tests I added to the deploy.rb file and runs them. If any test fails the deployment is halted.

To bring this custom task in I added these lines to the bottom of the Capfile:

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
Dir.glob('lib/capistrano/**/*.rb').each { |r| import r }   

I am probably including way many more types of files than I should but it does cover all the bases so I went for it.

Finally I added this line to the deploy.rb file:

before :deploy, 'deploy:run_tests'     

I reran cap production deploy and all was good.

Of course this post glosses over many of the stops, starts, restarts that I did to get this to work. I bet I deployed the app more than 50 times before I got it all right.

Here are some other issues I ran into along the way:

I ran into a problem where on the production server the Gemfile.lock was out of sync with the actual gems I had installed. I tried to run bundle install on the server but it would fail until I moved into a real directory and not by running it in the current directory. It seems the symlink messed that up.

Another thing that bothered me was all the "failures" the Capistrano log showed while deploying. I found this post where the comment by bruno at the bottom on Nov 6 will give you some comfort. At least it did for me.

Another thing that was still very opaque to me was what tasks were really being run and what did they do? There was really no documentation I could find on that. The only way I got a clue of what was going on was looking at the source. That was pretty enlightening, but not for the faint of heart.

One other thing I learned, if you change the ":format" setting to ":dot" instead of ":pretty" you will see red and green dots as the deployment script runs instead of all the verbose output. This makes it "feel" better at least. Only do this once you know everything is working though.

Final Thoughts

It was a long process to get this going. I spent many hours 'googling' and reading posts to come up with this configuration. Was it worth it? I think so. I can reliably deploy the app in about 2 minutes all from my dev box. As a side affect I also can run tasks like nginx:restart at any time I want to which will help out in day-to-day server management.

I guess my next steps in this area is to better understand what the tasks are that are run, and configure the deployment so I could actually install the app with Capistrano on a new server. But that will have to wait for another day. It's time to get back to coding.

Till next time