Ian White

Rails edge is getting lots of changes under the hood, consequently garlic has been kicking up a fuss for response_for and inherit_views...

So, I've rewritten them! Both plugins are now a lot cleaner, and have less LOC, due to some great changes under the actionpack hood.

inherit_views | response_for

inherit_views is a pretty simple concept. When you subclass a controller in rails, it would be great if you controller inherited its views from the the view path of the superclass controller. Here's how to use it (or you can just Skip ahead to the rewrite).

Using inherit_views

If you want every controller to inherit it's views from parent controllers, then do this:

class ApplicationController < ActionController::Base
  inherit_views

I think this is generally a bad idea, I use inherit views when I need to (when I subclass a controller):

class AccountImagesController < ImagesController
  inherit_views 'images'

When you use inherit_views, all view types are supported, as are partials. This means that with the following setup:

images/_form.html.erb
images/edit.html.erb

account_images/_form.html.erb

rendering 'edit' from AccountImages controller will render account_images/_form.html.erb inside images/edit.html.erb.

Rewriting inherit_views for edge

The inherit_views plugin used to acheive this by alias_method_chaining render on the controller and the view, and it would substitute an inherited view if the standard view couldn't be found. Along the way, it got patched for various changes in the rails internal, and well... it got pretty messy.

Edge rails has had a lot of template love recently, and one of the great things is that the notion of picking a template to render now has a single point of entry: ActionView::Base#_pick_template. (there is also _pick_partial_template, but that just massages the filename into the partial format, then calls _pick_template).

This made me stoked because all of the shennanigans in inherit_views could be swept away, and replaced with #_pick_template_from_inherit_view_paths. The only thing left for the plugin to do is to manage the inherit view paths (these are stored on the controller).

A note for current inherit_views users, I've removed some of the API which I never used (render_parent, and some other things). Let me know if you really need that stuff...

inherit_views | response_for

Ian White

Rails edge is getting lots of changes under the hood, consequently garlic has been kicking up a fuss for response_for and inherit_views...

So, I've rewritten them! Both plugins are now a lot cleaner, and have less LOC, due to some great changes under the actionpack hood.

inherit_views | response_for

Summary: response_for lets you specify a default response for an action. If that action (or a before_filter) doesn't perform a render or redirect, then the default response is rendered. You can also 'stack' responses - the latter ones override the former ones.

To get the full benefit of response_for, you need to write your actions with it in mind. It's not allways appropriate - but for some things it's great. if you have a set of actions that you use a lot (say if you're a resources_controller user), or if you use superclass controllers, then you'll probably get some benefits form response_for.

Before talking about the rewrite, I'll just give a before and after example of a common use case for response_for - subclassed controller action. If you want to skip this just head straight to the section regarding the rewrite.

Before

Here is a fairly standard controller with an action that does some stuff, then conditionally renders a response.

class SuperclassController < ApplicationController
  # the action does some stuff, then conditionally renders a repsonse
  # for :html, and :js
  def the_action
    do_some_stuff
   
    did_yet_more_stuff = if do_some_more_stuff
      do_yet_more_stuff
    else
      do_the_other_thing
    end
   
    respond_to do |format|
      if did_yet_more_stuff
        format.html do
          flash[:notice] = "Well done you"
          redirect_to the_done_url
        end
        format.js
      else
        format.html do
          flash[:notice] = "Bad boy"
          redirect_to the_bad_url
        end
        format.js { render :action => 'the_action' }
      end
    end
  end
end


There are two main reasons to override this action in a subclassed controller, (i) to change the response, and (ii) to change the logic. Both of these require a rewrite of the entire action! This is painful.

Code speaks louder than words, so here are two subclassed controllers, both of which add authentication, one which chnages the html response on success, the other which changes the logic. Click here to skip the code.

class Subclass1Controller < SuperclassController
  # We add authentication, and change the response
  before_filter :require_login
 
  def the_action
    do_some_stuff
   
    did_yet_more_stuff = if do_some_more_stuff
      do_yet_more_stuff
    else
      do_the_other_thing
    end
   
    respond_to do |format|
      if did_yet_more_stuff
        format.html do
          # We change the response
          flash[:notice] = "Well done #{current_user.display_name}"
          redirect_to the_done_url
        end
        format.js
      else
        format.html do
          flash[:notice] = "Bad boy"
          redirect_to the_bad_url
        end
        format.js { render :action => 'the_action' }
      end
    end
  end
end


class Subclass2Controller < SuperclassController
  # We add authentication, and change the logic
  before_filter :require_login
 
  def the_action
    do_some_stuff
   
    # Here we change the logic, but we have to write out the response again
    did_yet_more_stuff = if do_some_more_stuff_for_user(current_user)
      do_yet_more_stuff
    else
      do_the_other_thing
    end
   
    respond_to do |format|
      if did_yet_more_stuff
        format.html do
          flash[:notice] = "Well done you"
          redirect_to the_done_url
        end
        format.js
      else
        format.html do
          flash[:notice] = "Bad boy"
          redirect_to the_bad_url
        end
        format.js { render :action => 'the_action' }
      end
    end
  end
end


After..

Here's the same effect, with response for. We do need to write the superclass action a little differently first. We just take the response out of the action. We need to add an instance variable, so the response can conditionally render different things.

class SuperclassController < ApplicationController
  # this time with response_for
  def the_action
    do_some_stuff
   
    @did_yet_more_stuff = if do_some_more_stuff
      do_yet_more_stuff
    else
      do_the_other_thing
    end
  end
 
  response_for :the_action do |format|
    if @did_yet_more_stuff
      format.html do
        flash[:notice] = "Well done you"
        redirect_to the_done_url
      end
      format.js
    else
      format.html do
        flash[:notice] = "Bad boy"
        redirect_to the_bad_url
      end
      format.js { render :action => 'the_action' }
    end
  end
end

Now, our two subclassed controllers look like this:

class Subclass1Controller < SuperclassController
  # We add authentication, and change the response
  before_filter :require_login
 
  # we only override the repsonse that has changed
  response_for :the_action do |format|
    if @did_yet_more_stuff
      format.html do
        flash[:notice] = "Well done #{current_user.display_name}"
        redirect_to the_done_url
      end
    end
  end
end


class Subclass2Controller < SuperclassController
  # We add authentication, and change the logic
  before_filter :require_login
 
  def the_action
    do_some_stuff
   
    # Here we change the logic, we don't need to touch the response
    @did_yet_more_stuff = if do_some_more_stuff_for_user(current_user)
      do_yet_more_stuff
    else
      do_the_other_thing
    end
  end
end


The rewrite

The original response_for had to do some pretty tricky messin about to kick in once the action had done its thing. Things got a whole lot easier when actionpack introduced the default_render hook.

default_render is called after your action if a response has not explicitly been rendered (or redirected). You can use this yourself in your controllers if you want to do something other than just rendering the default template. response_for alias_method_chains it to render any declared responses..

If you're a previous response_for user

Along with the rewrite, I changed the way repsonse_for works. It used to intercept respond_to, so that you could override any repsonse declared in your action. This resulted in a bunch of weird edge cases, and so I decided to ditch it (read more about this at the end of the response_for README). If you need the old functionality use the 0.1-stable branch.

inherit_views | response_for

Nick Rutherford

This isn't really a web issue, but it's not something I found any help with on Google so I thought I'd share here (as we are Mac users at ArDes).

I recently installed Matlab for Uni work and it wouldn't load, seemingly with Java issues, complaining of being unable to load a native library. I then noticed that Vuze and TuxGuitar also wouldn't load, yet I could run a simple Java application I'd written without error.

This was odd, and worried me. However, it turned out to be quite simple, and something I'd caused myself by trying to be clever. It's to do with the Leopard Java 6 runtime environment which you may have installed as an update a few months ago.

If after doing this you may have gone into Java Preferences and told it to use this new runtime environment as the default things probably stopped working. The trouble is it's 64-bit only, which causes compatibility issues with 32-bit code (such as the windowing/graphics library SWT that you see in many applications).

Symptoms

  • Java applications won't load
  • Error -10810 given in console when using open -a /Applications/appname.app and elsewhere
  • Reports of 64bit SWT issues in the Console (which you can find with spotlight, in /Applications/Utilities/ )

Console log content

  • [JavaAppLauncher] A 64-bit JVM is available for 1.6.0
  • [JavaAppLauncher] and it is the only version available
  • [JavaAppLauncher] JVMArchs not found in Java dictionary
  • java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM

Solution

Search for Java Preferences in spotlight (/Applications/Utilities/Java/ ) and when it loads set Java version back from Java SE 6 to J2SE 5.0 Make sure that you put 5.0 above 6.0 (64-bit) for applets too or you'll have the same problems with things like the Matlab student product activation applet.

Good luck!

RailsConf Talk

September 12th, 2008

Ray Drainville

My business partner Ian White is is a very busy man. Currently, he’s just arrived in his native Australia, but only a few days ago he attended & gave a talk at RailsConf Europe—you can find the précis for his talk, Writing Resources_Controller: Discovering REST Patterns in Rails, on their site.

Once he’s recovered from his jet lag, Ian’s planning on giving some more information on his talk here. Stay tuned!

Update

The slides have finally appeared on the oreilly RailsConfEurope page (near the bottom).

Liquid error: undefined method `current_page' for #