Abhi On Rails

Ruby On Rails - What I learned recently

Deploying a Rails app to a Sub URI

Recently I was working on a POC application. And I had to deploy that application to show a demo. Since it is a POC app I had to deploy it as a Sub URI of an existing app.

I modified the nginx conf file to set up the sub URI, based on the information from this doc. I have added passenger_base_uri option to the conf, but that was just starting the application from the sub URI. All the URLs in the application is using the base URL instead of the sub URI.

To make all the URLs from the application use the sub URI as the base URL, I had to add a scope in the routes:
SubUriApp::Application.routes.draw do
  my_draw = Proc.new do
   devise_for :users
  end

  if ENV['RAILS_RELATIVE_URL_ROOT']
   scope ENV['RAILS_RELATIVE_URL_ROOT'] do
     my_draw.call
   end
  else
   my_draw.call
  end
end
  

The scope I added to the routes was not working with the passenger_base_uri added to the nginx conf. So I have to modify the nginx conf like the following:
server {
        listen 80;
        server_name base_url.com;
	location / {
        	root /www/base_url.com/current/public;
	        rails_env production;
        	passenger_enabled on;
	}

	location /sub_uri {
        	root /www/sub_uri/current/public;
	        rails_env production;
        	passenger_enabled on;
	}
    }
  

Even after these changes the assets were loaded from the base URL. I had to set the following configuration in the environment file (production.rb) to make it work:
ENV['RAILS_RELATIVE_URL_ROOT'] = "/sub_uri"
config.assets.prefix = ENV['RAILS_RELATIVE_URL_ROOT']
  

At last the application started working like a normal application from the sub URI :)

comments...

Devise with token based authentication for API

Currently I am working on a project which has both API and mobile apps. I had to implement authentication for the API. Since I have to implement the authentication for the non browser clients (iOS, Android) I won't be able to use session based authentication. So I started implementing token based authentication. I am using Devise gem for token based authentication.

Devise is a flexible authentication solution for Rails applications. If you search in ruby tool box you will see that Devise is the most popular rails authentication plugin. Devise supports token based authentication too.

Token based authentication means authentication using a token instead of a session cookie. Since non browser clients won't support session cookies we can't use session based authentication. There is another approach that is to embed a web browser.

By default devise won't support JSON. For that we have to add respond_to :json to the controller to override the default behavior. Even after adding respond_to to the controller some devise actions will do redirect instead of responding with proper json data. So we should override the devise controllers to implement custom json response.

I was searching for some sample application which implemented the Token based authentication for API using devise gem. But I couldn't find any sample working application. So I created one for future reference. Please let me know your feedback.

comments...

marked for destruction

Today I had gone through an issue, which got fixed by using the marked_for_destruction? method. This marked_for_destruction is part of Active Record Autosave Association module. Autosave Association module takes care of automatically saving associated records when their parent is saved. For more details see Auto Save Association .

In my project I have a model called Directory and it has many Categories. But I have to add a validation that there should be atleast one category associated with the directory. So in the directory model I added a validation in the after_save callback, which will check the count of Categories is greater that 0.

But while we do the update directory if I deleted all the categories the validation won't work , because I am using Autosave Association, which  saves category only after directory got saved. So at the time of Directory got saved the category count was greater than one, so validation will pass. To fix this issue I changed the condition that the categories count should be greater than 1. This is handled in the Directory model, and I want to let the controller know about the Directory not get saved. For that I will throw an exception if the condition fails.

after_save: check_categories_count

def  check_categories_count
   if self.categories.count <= 1
      throw Exception
   end
end

Since the condition is checking for categories greater than 1 this will fail for update with only one category. Because on update also it will execute the after_save and there is only 1 category associated with this directory. But it should fail if we are deleting the only one category. So I added one more condition that checks if the only one category is marked for delete. So the condition becomes:

if self.categories.first.nil? || self.categories.first.marked_for_destruction?
   throw Exception
end

Now it will fail only if the user tries to delete the last category.

comments...