Rails on the Run

Rails experiments by Matt Aimonetti

Browsing Posts tagged restful

resftul_authentication is a great plugin, but not always resftful… especially if like me you dropped unit test for RSpec.

I have a set of RSpec examples I use all the time on all my projects requiring restful_authentication, obviously I always end up tweaking them because each application has its specific needs.

However, I was recently asked what’s the way of dealing with controllers specs when a controller has a limited access ?

Let’s look at the following controller for instance:

class AssetController < ApplicationController
 before_filter :login_required, :except => [ :index, :show ]

my assets_controller_spec.rb file will have the following code:

describe AssetController, "handling GET /assets/new" do

  before do
    @asset = mock_model(Asset)
    Asset.stub!(:new).and_return(@asset)
  end

  def do_get
    get :new
  end

  it "should be successful" do
    do_get
    response.should be_success
  end

end

and guess what… it will fail! Why? simply because we are trying to access an action requiring login. Right, so what’s the best way of dealing with this issue and get our test to pass?

The best solution I found was to use a simple helper that I put in my spec_helper.rb file

def login_as(user)
  case user
    when :admin
      @current_user = mock_model(User)
      User.should_receive(:find_by_id).any_number_of_times.and_return(@current_user)
      request.session[:user] = @current_user
    else
      request.session[:user] = nil
  end
end

You might wonder why I use when :user, well, in a lot of the application I’m working on, I have different levels of access and I want to tests different accounts so I the case conditional loop.

Anyway, let’s implement our helper in our previous RSpec example:

describe AssetController, "handling GET /assets/new" do

  before do
    login_as :admin
    @asset = mock_model(Asset)
    Asset.stub!(:new).and_return(@asset)
  end

  def do_get
    get :new
  end

  it "should be successful" do
    do_get
    response.should be_success
  end

end

and there you go, now your example passes properly ;)

Have fun

In part I I explained how to access Rails data from Flash.

However Yves aka Kadoudal was wondering what I did with Rails to return the event record:

Rails.get(‘events’, rails_events); how rails returns the event record as we don’t call a controller/action … ? I believe doing it RESTFul it’s depending upon your route ? is it not?

We are actually calling a controller/action If you look at the Restfulflash class, you’ll notice a function called get

public function get(controller, callback){
        var railsReply:XML = new XML();
        railsReply.ignoreWhite = true;
        railsReply.onLoad = function(success:Boolean){
            if (success) {
                    trace ('Rails responded: '+railsReply);
                    callback.text = railsReply;
            } else {
                    trace ('Error while waiting for Rails to reply');
               callback.text = 'error';
            }
        }
        var railsRequest:XML = new XML();
        railsRequest.contentType='application/xml';
        railsRequest.sendAndLoad(this.gateway+controller, railsReply);
        delete railsRequest;
    }

What’s really interesting are the following 2 lines:

railsRequest.contentType=”application/xml”;
railsRequest.sendAndLoad(this.gateway+controller, railsReply);

I’m preparing a request that I will send to my gateway mentioning the controller name. In the previous example I was calling the ‘events’ controller: http://mysite.fr/events because I’m using REST and because my request is a ‘get’ Rails will call the index action.

for more info on RESTful design check the nice series available from the softies on rails blog

Let’s just look at my code and see what’s up with rails magic :)

Here is my index action sending you back a different object based on the header of your request.

class EventsController < ApplicationController
  # GET /events
  # GET /events.xml
  def index
    @events = Event.find(:all)

    respond_to do |format|
      format.html # index.rhtml
      format.xml  { render :x ml => @events.to_xml }
      format.json { render :json => @events.to_json }
    end
  end

That’s it, and it was automatically created for you by the script/generate scaffold_resource command :)

Since I’m answering Yves’ questions, let’s look at his final question:

problem : what if the xml returned is not a record, but it has to be prepared by Rails…
I explain, right now my rails view .. I have a javascript object passing all the info via JS
I would like to replace the datasource file by a direct call to rails from Flash, so this param will disappear… so (correct me if I am wrong)
1- I need to pass a user_id value in JS to Flash in the script…
2- Flash need to send a request to Rails (controller/action/id to prepare the xml… ) to get in return a correct xlm object to be displayed

1- I’m not sure why you need to pass the user_id from JS to Flash but I guess Flash doesn’t know what user to query?? (anyway that would work)

2- If you are using REST, Flash just needs make a get call to /controller/id Make sure to set the request header type as xml, like I did in my function railsRequest.contentType=”application/xml”; Otherwise I believe (but didn’t try) that you can call /events/1.xml where 1 id the id of the event you want to retrieve.

By the way, it will call the show action from the events controller which looks like that:

def show
  @event = Event.find(params[:id])

  respond_to do |format|
    format.html # show.rhtml
    format.xml  { render :x ml => @event.to_xml }
  end
end