Devise has a very useful Trackable module used to track user’s sign in count, timestamps and IP address. There are some occasions when you need to disable tracking. For example for API requests where user signs in on every request; for instances where admin might sign in as an user; and similar.

To disable Devise Trackable module you need to set request.env["devise.skip_trackable"] = true. You have to do that before trying to authenticate user, so you’ll want to put it in a before_filter, or even better prepend_before_filter to make sure it’s before authentication.

Add this to your controller in which you want to disable tracking:

1
2
3
4
5
6
prepend_before_filter :disable_devise_trackable

protected
  def disable_devise_trackable
    request.env["devise.skip_trackable"] = true
  end

Note to self: here is how to upgrade Ubuntu 8.04 LTS (or any other release that is no longer supported) to newer Ubuntu release.

When you are upgrading unsupported release of Ubuntu if you try to do the usual sudo apt-get update it will most likely fail because… well, it’s unsupported. The simple fix for this is to change your /etc/apt/sources.list and replace repository URLs from something like us.archive.ubuntu.com to old-releases.ubuntu.com.

After that you should be able follow normal upgrade procedure (use sudo if you are not root):

1
2
3
apt-get update
apt-get install update-manager-core
do-release-upgrade

References:

Here is a quick way to setup VirtualBox using Vagrant with Heroku-like box on Mac.

  1. Install VirtualBox from https://www.virtualbox.org/wiki/Downloads

  2. Install Vagrant from http://downloads.vagrantup.com/

  3. Create Vagrantfile for Heroku-like box (based on https://github.com/ejholmes/vagrant-heroku) that looks something like:

1
2
3
4
5
6
    Vagrant.configure("2") do |config|
        config.vm.box = "heroku"
        config.vm.box_url = "https://dl.dropboxusercontent.com/s/rnc0p8zl91borei/heroku.box"
        config.vm.synced_folder ".", "/vagrant", :nfs => true
        config.vm.network :private_network, ip: "192.168.1.42"  # required for NFS
    end

Beside telling Vagrant to use Heroku-like box from https://github.com/ejholmes/vagrant-heroku it also sets up shared dir between host and VM machine. It will mount Vagrantfile dir (.) to /vagrant in VM.

vagrant up will setup the VM and start it up.

Now you can use vagrant ssh to login to VM.

Vagrant Heroku-like box comes with Postgresql, but if you want you can easily setup sqlite:

1
sudo apt-get install libsqlite3-dev

Bonus tip: when you are working on multiple projects sometimes you can forget which VMs are running. You can list all running VMs using:

1
VBoxManage list runningvms

Further reading:

RailsDiff is a very useful site when upgrading Rails versions (for example, from Rails 3.2 to Rails 4). It will generate default Rails app using two different Rails versions and it will compare them. The result is that you can see all the configuration changes (like in application.rb) and all other changes – which is really useful when upgrading to new Rails version.

Assume that you have the usual setup with model (MyFile) using simple Carrierwave uploader (MyFileUploader):

# app/models/my_file.rb
class MyFile < ActiveRecord::Base
  mount_uploader :file, MyFileUploader
end

To be able to test Carrierwave uploaders with RSpec using FactoryGirl factories you need:

  • define factory with uploaded file
  • modify test environment storage so test file uploads are separated from other uploads
  • turn off image processing to speed up tests
  • perform cleanup after each test

Define factory

# spec/factories/my_files.rb
FactoryGirl.define do
 factory :my_file do
   photo Rack::Test::UploadedFile.new(File.open(File.join(Rails.root, '/spec/fixtures/myfiles/myfile.jpg')))
 end
end

Setup Carrierwave

First we need to make sure Carrierwave is using local file system for storage and to disable file processing for testing environments. Disabling file processing will speed up tests considerably. We can do that by adding following to Carrierwave initializer:

if Rails.env.test? || Rails.env.cucumber?
  CarrierWave.configure do |config|
    config.storage = :file
    config.enable_processing = false
  end
end

Next we should separate test uploads from any other uploads. We can do that by modifying cache_dir and store_dir methods for all Carrierwave models (i.e. all models that are descendants of CarrierWave::Uploader::Base). So the whole Carrierwave initializer looks something like:

# config/initializers/carrierwave.rb
if Rails.env.test? || Rails.env.cucumber?
  CarrierWave.configure do |config|
    config.storage = :file
    config.enable_processing = false
  end

  # make sure our uploader is auto-loaded
  MyFileUploader

  # use different dirs when testing
  CarrierWave::Uploader::Base.descendants.each do |klass|
    next if klass.anonymous?
    klass.class_eval do
      def cache_dir
        "#{Rails.root}/spec/support/uploads/tmp"
      end

      def store_dir
        "#{Rails.root}/spec/support/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
      end
    end
  end
end

Clean up uploaded files

Using factory defined above will create uploaded files in cache_dir and store_dir. These are just temporary files and should be removed after each test, so each of them has a clean slate. By adding after :each hook in RSpec configuration block we can remove these files simply by deleting spec/support/uploads dir.

# spec_helper.rb
RSpec.configure do |config|
  config.after(:each) do
    if Rails.env.test? || Rails.env.cucumber?
      FileUtils.rm_rf(Dir["#{Rails.root}/spec/support/uploads"])
    end 
  end
end

Here are some random Rails tips I’ve found useful:

  • rails console sandbox – if you open console like this it will rollback all database changes once you exit. Pretty useful for playing around without making any changes to database.
  • rake db:migrate:status – useful when you want to see the status of current database. It will show the status of all migrations.
  • User.pluck(:email) – since Rails 3.2.1 you can use pluck method to get an array of values in one particular column. It’s the equvivalent of doing User.select(:email).map(&:email)

Setting up Paperclip to use Amazon’s S3 is as simple as setting :storage => :s3 and providing right credentials to Paperclip by setting :s3_credentials option. Best way to provide S3 credentials is to use an YML file (usually config/s3.yml) which allows you to set different credentials for each environment. For example:

# config/s3.yml
development:
  access_key_id: XYZXYZXYZ
  secret_access_key: XYZXYZXYZ
  bucket: mygreatapp-development
production:
  access_key_id: XYZXYZXYZ
  secret_access_key: XYZXYZXYZ
  bucket: mygreatapp-production

Of course you want to treat s3.yml same as database.yml – i.e. you don’t want to track it with git and you want for each person/server to have it’s own.

However, consider this: you are working on Open Source app in a public git repository and you are deploying it on Heroku. Heroku doesn’t allow you to create files (unless they are in git repository) and you can’t commit s3.yml with your credentials to public repository.

One solution is to define different :s3_credentials hash in one of the environment files or to load different YML file for each environment and generate hash from it. Downside is that you need to have a separate YML file for each environment and/or you need to convert YML to hash. Other solution could be to have separate local branch from which you will push to Heroku. Problem with this is that you have to have a local branch for deploying. This means if there are multiple developers who deploy to production each should have separate local branch.

Much simpler way to deploy Paperclip with different S3 credentials for each environment (with one of the environment being deployed on Heroku; and repository being public) is to create s3.yml file as usual (and don’t commit it to git), but define values only for local environment.

For production deployment on Heroku you can write initializer which will set :s3_credentials from ENV variables.

# initializers/s3.rb
if Rails.env == "production"
  # set credentials from ENV hash
  S3_CREDENTIALS = { :access_key_id => ENV['S3_KEY'], :secret_access_key => ENV['S3_SECRET'], :bucket => "sharedearth-production"}
else
  # get credentials from YML file
  S3_CREDENTIALS = Rails.root.join("config/s3.yml")
end

# in your model
has_attached_file :photo, :storage => :s3, :s3_credentials => S3_CREDENTIALS

and you can easily set persistant ENV vars on Heroku with:

$ heroku config:add S3_KEY=XYZXYZ S3_SECRET=XYZXYZ

(according to Heroku docs)

I’ve been planning to catchup with all the new Rails 3 stuff. To get me started I’ve compiled a small Rails 3 related reading list.

  1. Ruby on Rails 3.0 Release Notes
  2. Active Record Query Interface 3.0
  3. The Skinny on Scopes (Formerly named_scope)
  4. Rails 3 Beautiful Code
  5. Railscasts – rails-3.0 episodes
  6. jQuery with Rails 3
  7. The Rails Module (in Rails 3)

Once I’m done with it I plan to get even more from:

I have been working on extending Rails’ I18n Simple backend to make it work with Serbian grammar (post on that will follow soon), but I kept getting an error:

Expected ./lib/serbian_simple.rb to define SerbianSimple (LoadError)

I’ve just spent an hour trying to figure out why this keeps happening and I found that there’s a lot of people with similar problem.

It seems that the problem appears when Rails tries to autoload files. In my case there was a simple solution – I just added require ‘serbian_simple.rb’ in environment.rb to manually load the file.

Last week I’ve taken a break from Ruby/Rails development and I’ve worked on a site that uses PHP with CodeIgniter framework.

Despite the fact that CodeIgniter has a very nice documentation I found it very difficult to find a way to do some simple things, that are more or less obvious, but which can be a problem for someone who hasn’t worked with CodeIgniter before. (for example, I found myself more than once looking at CI code to figure out how it works, so I can use it)

I had to make a simple password protected administration section. One admin user, one password, no user registrations, no roles – simple as possible. As I was using CI framework I decided to find a plugin/library that does this. Unfortunately most CI authorization plugins/libraries are very bloated and too complicated for this simple task. I tried to find some examples how to handle this simple use case, but nothing came up.

Finally I’ve found a small authorization plugin: Erkanaauth.

First you need a user table (must be named ‘users’) which only needs to have an id field and all other fields are optional because you will manually specify what other columns will be used. I opted for simple id, username, password:

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
);

We will need to “install” ErkanaAuth library. You should download it and unzip it.

Next we should create an Admin controller which will handle all administration tasks (remember I’m making simple admin here, so I don’t need to protect multiple controllers).

<?php
class Admin extends Controller {
  function Admin()
  {
    parent::Controller();   
    $this->load->database();
    $this->load->helper(array('url', 'form', 'date'));
    $this->load->library(array('form_validation', 'upload', 'Erkanaauth', 'session'));
  }
}
?>

Constructor just connects to database and loads some standard helpers and libraries (including Erkanaauth) that are usually used.

Next step is to add a function which we can call to verify if user is logged in:

private
  function authorize()
  {
      if($this->erkanaauth->try_session_login())
          return true;
  
      redirect('admin/login');
  }

Function uses Erkanaauth’s try_session_login which checks if user is already logged in (checks session for user id). If user isn’t logged in we’ll redirect him to our login page:

function login()
  {
    $username = $this->input->post('username', true);
    $password = $this->input->post('password', true);
    if($username || $password)
    {
      if($this->erkanaauth->try_login(array('username' => $username, 'password' => $password)))
        redirect('admin');
    }
    
    $this->load->view('admin_login');
  }

  function logout()
  {
    $this->erkanaauth->logout();
    redirect('admin');
  }

Key command here is try_login in login function which tries to find an entry in users table that fulfills given conditions. If you have different users table than the one I made this is the place where you should enter your column names.

Logout function is has just a simple call to Erkana’s logout function. Nothing special there.

Of course we also need a login page template which should contain a simple user/pass form. It’s pretty basic and you can see it if you get the complete code (see at the end).

Finally we have everything needed to protect any page in Admin controller. In order to protect a page all you need to do is to add a call to authorize function to any function you want to protect. Like this:

function index()
  {
    $this->authorize();
    echo "Do something useful... For now just display logout link: ";
    echo anchor('admin/logout', "Logout");
  }

That’s it. Now you have fully functional administration section which requires username and password authorization.

You can get the complete sample application from Github repository. Feel free to expand on it or use it any way you like.