1

Hacking a Rails controller and template inheritance strategy

Developing with Ruby on Rails can be fun, but sometimes, especially for newbies like me, we can get stuck somewhere. I’ll document my strategy for single model inheritance and how I did with the controllers and templates.

I have two types of users in my app, User (a common user) and ThingsAdmin (a user, but can have and administer Things). The two type of users basically share the same attributes and the same behavior, though the ThingsAdmin user can have and administer many Things.

For more about Single table inheritance, check the docs http://api.rubyonrails.org/classes/ActiveRecord/Base.html

Let me show you my code for the models:

class User < ActiveRecord::Base
   #attributes, validations, filters, methods and other stuff for User model
end

And the ThingsAdmin is something like this:

class ThingsAdmin < User
   has_many :things
   #Specific attributes, validations, filters, methods and other stuff for ThingsAdmin model
end

In the migration, I just needed to add a new field “type”:

  def self.up
    change_table :users do |t|
      t.string :type
    end
  end

It’s not needed to create a new table for ThingsAdmin.

Having these two models defined like this, when I create a new User, the field type is set to nil but If I create a new ThingsAdmin, the field “type” is automatically set to “ThingsAdmin”, i.e., the name of the model.

I can test this in the rails console:

irb(main):008:0> user = User.new
irb(main):009:0> user.type
=> nil
irb(main):008:0> admin = ThingsAdmin.new
irb(main):009:0> admin.type
=> ThingsAdmin

Now, I can have two different classes of users and it’s time to move to the controllers. I decided to start by mapping all things_admin resources to the controller User, something like this in my routes.rb:

resources :things_admins, :controller=>"users"

However, this does not works as expected because in my UserController the new action is something like:

class UsersController < ApplicationController
  def new
     @user = User.new
   end
   ##rest of the controller
end

So, how can I distinguish between the request for a new User or request for a new ThingsAdmin? My first though was to have a parameter moving around so I can differentiate the type of user I want to create, something like:

class UsersController < ApplicationController
  def new
      @user = User.new if params[:role]=='user'
      @user = ThingsAdmin.new if params[:role]=='admin'
   end
   ##rest of the controller
end

But this will bring some ugly things into my code, for example in the create action I would need to do something like:

class UsersController < ApplicationController
  def create
      @user = User.new(params[:user]) if params[:role]=='user'
      @user = ThingsAdmin.new(params[:things_admin] if params[:role]=='admin'
   end
   ##rest of the controller
end

I cannot forget about moving the param “role” when doing these operations…

So, I decided to try other solution, which I thing is cleaner. I created a controller for ThingsAdmin that only contains the actions “new” and “create”:

class ThingsAdminsController < UsersController
  def new
      @user = ThingsAdmin.new
      render "users/new"
   end
  
  def create
      @user = ThingsAdmin.new(params[:things_admin] if params[:role]=='admin'
      create_user(@user)
  end
   ##rest of the controller
end

And modified the UsersController to looks something like:

class  UsersController < ApplicationController
  def new
      @user = User.new
   end
  
  def create 
      @user = User.new(params[:things_admin] if params[:role]=='admin'
      create_user(@user)
  end
  protected
  def create_user(user) 
       #Code to create users (User or ThingsAdmin)
   end
   ##rest of the controller
end

Now, I have to tell the rails routing engine to redirect every action for business_admins to the UsersController except for “new” and “create” actions. My routes.rb look something like:

resources :users
match "/things_admins/new(.:format)" => "things_admins#new", :via=>[:get], :as=>:new_things_admin
match "/things_admins(.:format)" => "things_admins#create", :via=>[:post], :as=>:things_admins  
resources :things_admins, :controller=>"users", :except=>[:new, :create]

Now, every action concerned to a User or ThingsAdmin will be handled by the controller UsersController, except the “new” and “create” action.

There is something more I have to do, now concerning the update action. To have only one action handling the update of both User and ThingsAdmin, I just need to modify one line in the UsersController:

class  UsersController < ApplicationController
    
  def update
      @user = User.find[params[:id])
      ##This method is handling both the update of BusinessAdmins and Users....
      if @user.update_attributes(params[@user.class.name.underscore]) 
        flash[:success = "User updated"
        redirect_to user_path(@user)
     else
         flash[:error] = "Could not update user"
        render :edit
     end
  end
     ##rest of the controller
end

Now, everything works as expected. Really, I don’t know if this is the best strategy because it’s the first one I tried. If you have a better solution, please share because I would like to know.

2

Getting Rails 3.0.3 running with Mysql

Just as a memory aid for me and though I can share this.

While experimenting the Ruby on Rails 3.0.3, I created an application that by default uses SQLite, but I want to use MySql instead.

So here the steps I gone through:

1) Install MySql Server

sudo apt-get install mysql-server

2) Install MySql client dev package

sudo apt-get install libmysqlclient-dev

3) Create a new mysql user to use during my development and tests
3.1 ) Connect to mysql

mysql -u root -p

3.2 Create a user

create user 'railsuser'@'localhost' identified by 'somepass';

3.3) Grant all privileges to the user, so rails can do everything for me, such as creating the databases:

grant all privileges on *.* to 'railsuser'@'localhost';

4) Edit my app Gemfile:
Comment, or remove, the following line

gem 'sqlite3-ruby', :require =&gt; 'sqlite3'

Add this line:

gem 'mysql2'

5) Run the Bundler tool, the dependency management tool for Rails app:

sudo bundle install

Hopefully, everything went just fine till here.

Now, just configure my database.yml to use MySql as a database:

development:
adapter: mysql2
encoding: utf8
reconnect: false
database: myapp_development
pool: 5
username: railsuser
password: somepass
socket: /var/run/mysqld/mysqld.sock

test:
adapter: mysql2
encoding: utf8
reconnect: false
database: myapp_test
pool: 5
username: railsuser
password: railsuser1234
socket: /var/run/mysqld/mysqld.sock

For production, just use the MySql user that I want in production.

6) Let rake create my databases, by running:

rake db:create

It’s done.

0

Yeahh, I’m building it with enterprise ready technologies… who cares?

If you have an web product idea and want to test it, i.e. put it in front of your users, what it’s the most critical aspect to consider first? I usually get my head into a conflict, because I’m used to work with the so-called “enterprise ready technologies”, namely JEE. My mind if formatted to think about all aspects that a good enterprise product should have. I start giving more attention to a set of quality attributes like performance, usability, reusability, testability, portability, modifiability, etc, etc, and give less attention to one critical aspect, if I want to get a product in front of the users fast: productivity.

So, I start looking to what is out there that is enterprise ready, like EJBs, JSF, Hibernate, JBoss, Tomcat, whatever, and start getting the pieces together…. out there in the world there was some a guy with the same product idea, but considered first the productivity aspect of the equation and started to materialize the idea with, let’s say, Ruby on Rails…. guess who’s the winner?

I’m not against the use of JEE, of course, I just think that in some cases it does not make sense. I also have this false argument in my mind: What if I start building it with PHP and then hit a performance problem with my 1Million users? Well, If I ever had one million users, I’ll be happy to deal with these performance issues :)

0

Expressive programming in Java

In Ruby On Rails, for example, you can use a construct like this:

raise ActiveRecord::RecordNotFound unless condition

It’s simple to understand and easier to use, though you can also use the more classical format

if !condition
    raise ActiveRecord::RecordNotFound
end

Because of that I like also to use this kind of expressiveness in Java. Instead of thinking like:

if this then do that

I like to think like:

do that if this

Of course, is a matter of felling well while programming :)

Let’s take an example in Java. In this example there’s a simple API to put something in a cache and you will use a “key” to identify your objects in the cache. But because I, as API designer, don’t want you to use the string “key” as key, I will code the cache like this:

package eu.jpereira.exprogramming.cache;
public class MyCache {

  public static void addToCache(String key, Object object) throws Exception {
    if ( key.equals("key") ) {
      throw new Exception("Hey dude, key cannot be 'key'!!");
    }
    //do that cache thing
    System.out.println("Cool! You've added it to the cache!!");
  }	
}

But I prefer doing something like this:

package eu.jpereira.exprogramming.cache;
import static eu.jpereira.exprogramming.cache.ExceptionRaiser.*;

public class MyCache {

  public static void addToCache(String key, Object object) throws Exception {
    throwIfTrue(new Exception("Hey dude, key cannot be 'key'!!"),key.equals("key"),);
    //do that cache thing
    System.out.println("Cool! You've added it to the cache!!");
  }	
}

And the throwIfTrue method is coded like this:

package eu.jpereira.exprogramming.cache;

public class ExceptionRaiser {

  public static <T extends Throwable> void throwIfTrue(T exception, boolean condition) throws T {
    if (condition) {
      throw exception;
    }
  }
}

Even If at the first sight it may seems to be a lot of more work, it’ll pay off with my amusement while programming :) Moreover, it’s easier to understand and to program like this.