Forum: Ruby on Rails Tutorial - Creating a Global REST Controller

Posted by Joel Dezenzio (elricstorm)
on 2010-01-19 04:12
=========================================
Creating a Global REST Controller
=========================================

Let's imagine that you have an application that houses close to 400
controllers.  Each of these controllers accesses a specific model that
contains normalized data.  While there are a lot of tricks you can
perform for normalization, let's assume that after you further normalize
and consolidate databases you still have 300 controllers left over.

Of these 300 controllers, half of them are composed of normal REST
methods, and the other half are comprised of normal REST methods and
special methods which perform specific actions.

This means that each of those controllers at a bare minimum could have
close to 80 lines of code.

Everyone talks about thin controllers.  I'm going to show you a way to
create a Global REST controller that will make most if not all of your
controllers very thin.

http://gist.github.com/280611

Take a look at the code above and look closely at the Global REST
Controller.

Let's pretend that you have a controller that manages pages, ie. a Pages
Controller.  It's comprised of only index, new, edit, show, create,
update, and destroy.  No other actions.

By implementing a Global REST controller, you could place this in your
pages controller:

class PagesController < GlobalRestController
end

.. and that's all you need (2 lines of code)

In your index.html.erb view you would replace one instance variable
@pages with @objects and you are done.

Now then, going back to the old scenario I posted above, you could
effectively take 150 controllers with say 78 lines of code and reduce
11,700 lines of code down to .. 300.

What could you do with your other 150 controllers?

class SomeOtherController < GlobalRestController

  def some_custom_action
    .. etc.
    .. etc.
  end

  def some_other_custom_action
    .. etc.
    .. etc.
  end

end

Your custom actions would be listed but you would automatically inherit
the other 7 rest methods automatically.  No extra code needed.

What if you want to overwrite one of the REST methods with a custom REST
method of your own?

class SomeOtherController < GlobalRestController

def index
  @pages = Page.find(:all, :conditions => 'parent_id IS NOT NULL')

  respond_to do |format|
    format.html
    format.xml  { render :xml => @pages }
  end
end

end

You just simply place the REST method into your controller and it still
picks up the other 6 REST methods.  You then keep the index.html.erb
page intact without @objects changes and you are done.

How well does this work?

I currently run a football statistics site that houses 75 controllers
and (66 of them) are part of a Global REST Controller mechanism.  I have
had no issues whatsoever using it.

I hope this tutorial helps someone out.  If you liked this tutorial, you
can check out a very lengthy tutorial I wrote yesterday on mailers and
observers here:

http://www.ruby-forum.com/topic/202316

Take care.
Posted by Joel Dezenzio (elricstorm)
on 2010-01-19 04:19
In a follow-up on your edit.html.erb and new.html.erb you could use 
something similar:

Replace @page with @object.  I've added a partial and a couple of 
example forms for these as well in the gist posted above.

Posted by Ar Chron (railsdog)
on 2010-01-19 04:44
Very cool.  I'm glad to hear it is working out so well for you Alpha...
Posted by Aleksey Gureiev (Guest)
on 2010-01-19 06:45
(Received via mailing list)
Interesting tutorial. Thanks.

I found there are a couple of little problems. First, your index view
code has some extra closing round brackets, and then the destroy
action redirects back to the deleted object. I took a little while to
fork and update the controller code. Also made it a bit more DRY:

http://gist.github.com/280670

One question though, why wouldn't you use, for example,
inherited_resources plugin for this purpose?

- Aleksey
Posted by pharrington (Guest)
on 2010-01-19 08:43
(Received via mailing list)
On Jan 19, 12:43 am, Aleksey Gureiev <spyro...@gmail.com> wrote:
> inherited_resources plugin for this purpose?
>
> - Aleksey

I don't wanna speak for Alpha Blue, but straight plain inheritance is
a simple answer to a common problem...
Posted by Marnen Laibow-Koser (marnen)
on 2010-01-19 09:00
Alpha Blue wrote:
> =========================================
> Creating a Global REST Controller
> =========================================
> 
> Let's imagine that you have an application that houses close to 400
> controllers.  Each of these controllers accesses a specific model that
> contains normalized data.  While there are a lot of tricks you can
> perform for normalization, let's assume that after you further normalize
> and consolidate databases you still have 300 controllers left over.

Why would you ever have that many controllers in the first place?  That 
makes no sense.

> 
> Of these 300 controllers, half of them are composed of normal REST
> methods, and the other half are comprised of normal REST methods and
> special methods which perform specific actions.
> 
> This means that each of those controllers at a bare minimum could have
> close to 80 lines of code.

What?  No.  An 80-line controller means you have too much logic in the 
controller and not enough in the model.

> 
> Everyone talks about thin controllers.  I'm going to show you a way to
> create a Global REST controller that will make most if not all of your
> controllers very thin.

Why not just use make_resourceful?

Best,
-- 
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
Posted by paron (Guest)
on 2010-01-19 13:35
(Received via mailing list)
It seems like to create 300 ReST controllers via Rails Scaffolding or
make_resourceful, one would need to type all the specs for all the
tables into the Rails Scaffolding generator. If I am reading
correctly, one could use Global Rest Controllers on existing tables,
and those controllers would read the tables structures for themselves.

I work a lot with legacy tables, so I would dislike typing the
structure into the Rails Generator all the time. I don't do that: I
use home-brew Scaffolding Generators that read the table structure,
like the old pre-Restful Rails scaffolding.

Anyway, could that be one difference?

Ron
Posted by Joel Dezenzio (elricstorm)
on 2010-01-19 15:19
Aleksey Gureiev wrote:
> Interesting tutorial. Thanks.
> 
> I found there are a couple of little problems. First, your index view
> code has some extra closing round brackets, and then the destroy
> action redirects back to the deleted object. I took a little while to
> fork and update the controller code. Also made it a bit more DRY:
> 
> http://gist.github.com/280670
> 
> One question though, why wouldn't you use, for example,
> inherited_resources plugin for this purpose?
> 
> - Aleksey


Thanks for that Aleksey, I had copied and pasted from an older 
controller that was using not so polished code.

I very rarely use plugins in my app.  I don't like to use code that I 
can't control myself.  I would rather find a plugin that is suitable, 
dive into the code, learn from it, and create my own modified plugins.

This is just a very simple tutorial outlining to some newer folk how 
they can create an inheritance template for their controllers.

I have my own private inheritance template for my models, which works 
equally well.

> Marnen Laibow-Koser
> Why would you ever have that many controllers in the first place?  That 
> makes no sense.

Your statement doesn't make any sense.  I'll give you one answer before 
you throw out the assumptions once again mate.  You really should stop 
doing that.  My one application is comprised of:

4 separately run applications tied together on different servers.
Posted by Marnen Laibow-Koser (marnen)
on 2010-01-19 15:58
Alpha Blue wrote:
> Aleksey Gureiev wrote:
>> Interesting tutorial. Thanks.
>> 
>> I found there are a couple of little problems. First, your index view
>> code has some extra closing round brackets, and then the destroy
>> action redirects back to the deleted object. I took a little while to
>> fork and update the controller code. Also made it a bit more DRY:
>> 
>> http://gist.github.com/280670
>> 
>> One question though, why wouldn't you use, for example,
>> inherited_resources plugin for this purpose?
>> 
>> - Aleksey
> 
> 
> Thanks for that Aleksey, I had copied and pasted from an older 
> controller that was using not so polished code.
> 
> I very rarely use plugins in my app.  I don't like to use code that I 
> can't control myself.  I would rather find a plugin that is suitable, 
> dive into the code, learn from it, and create my own modified plugins.

http://c2.com/cgi/wiki?NotInventedHere

I have the opposite philosophy.  If someone has already written 
something suitable, I see little reason to duplicate their effort. 
Extend it, sure.  Write something that's a better fit for me, sure.

But by just saying "I don't like plugins", you're making yourself a lot 
of extra work for no good reason.

> 
> This is just a very simple tutorial outlining to some newer folk how 
> they can create an inheritance template for their controllers.
> 
> I have my own private inheritance template for my models, which works 
> equally well.

Sure.

> 
>> Marnen Laibow-Koser
>> Why would you ever have that many controllers in the first place?  That 
>> makes no sense.
> 
> Your statement doesn't make any sense.  

Of course it does.  No matter how many models your app has, I can't see 
why a well-designed app would ever need to expose 300 different types of 
resource -- which is essentially what controllers are about.

> I'll give you one answer before 
> you throw out the assumptions once again mate.  You really should stop 
> doing that.  

I'm not assuming anything in particular, so this is not relevant.

> My one application is comprised of:
> 
> 4 separately run applications tied together on different servers.

Then it isn't one application.  Or it's one application run clustered. 
In neither case do I see this making the slightest difference to the 
number of controllers.

See, the number of controllers is a UI issue.  No matter how many types 
of object you're dealing with under the hood, if the user has to 
navigate among 300 controllers (which, after all, are generally the 
user-facing part of the app), then in 99 cases out of 100, I'd say you 
need to refactor the UI or split up the app.

Best,
-- 
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
Posted by Frederick Cheung (Guest)
on 2010-01-19 16:15
(Received via mailing list)
On Jan 19, 2:58 pm, Marnen Laibow-Koser <li...@ruby-forum.com> wrote:
> > I very rarely use plugins in my app.  I don't like to use code that I
> > can't control myself.  I would rather find a plugin that is suitable,
> > dive into the code, learn from it, and create my own modified plugins.
>
> http://c2.com/cgi/wiki?NotInventedHere
>
> I have the opposite philosophy.  If someone has already written
> something suitable, I see little reason to duplicate their effort.
> Extend it, sure.  Write something that's a better fit for me, sure.

I find it a difficult one to call - while the above is true it's
horrible find come rails upgrade time that the author has abandoned
the project. Lots of rails plugins are small one man things - I would
certainly hesitate to use a plugin unless either it has sufficient
backing/momentum that I can trust it will stay around or I understand
it enough that I could rewrite it if I had to.

> See, the number of controllers is a UI issue.  No matter how many types
> of object you're dealing with under the hood, if the user has to
> navigate among 300 controllers (which, after all, are generally the
> user-facing part of the app), then in 99 cases out of 100, I'd say you
> need to refactor the UI or split up the app.

That is assuming these are end user facing controllers (as opposed to
ones for api clients). (and if the OP does indeed have 4 apps then
they have already split up the app)

Fred
Posted by Marnen Laibow-Koser (marnen)
on 2010-01-19 16:26
Frederick Cheung wrote:
> On Jan 19, 2:58 pm, Marnen Laibow-Koser <li...@ruby-forum.com> wrote:
>> > I very rarely use plugins in my app.  I don't like to use code that I
>> > can't control myself.  I would rather find a plugin that is suitable,
>> > dive into the code, learn from it, and create my own modified plugins.
>>
>> http://c2.com/cgi/wiki?NotInventedHere
>>
>> I have the opposite philosophy.  If someone has already written
>> something suitable, I see little reason to duplicate their effort.
>> Extend it, sure.  Write something that's a better fit for me, sure.
> 
> I find it a difficult one to call - while the above is true it's
> horrible find come rails upgrade time that the author has abandoned
> the project. 

That's certainly true.

> Lots of rails plugins are small one man things - I would
> certainly hesitate to use a plugin unless either it has sufficient
> backing/momentum that I can trust it will stay around or I understand
> it enough that I could rewrite it if I had to.

Also a good point.  I mention make_resourceful because it doesn't seem 
to be going away, and Hampton Catlin is certainly not going away (well, 
unless he pulls a _why).

> 
>> See, the number of controllers is a UI issue.  No matter how many types
>> of object you're dealing with under the hood, if the user has to
>> navigate among 300 controllers (which, after all, are generally the
>> user-facing part of the app), then in 99 cases out of 100, I'd say you
>> need to refactor the UI or split up the app.
> 
> That is assuming these are end user facing controllers (as opposed to
> ones for api clients). 

True, although what API you expose is sort of a UI issue.  In any case, 
an API so bloated that it exposes 300 controllers is scary. :)

> (and if the OP does indeed have 4 apps then
> they have already split up the app)

Right.  In which case it's not one app and, at least to me, it doesn't 
mean much to count the total number of controllers across the 4 apps.

But on reflection, I see that the original point was code duplication, 
so from that point of view it *may* make sense.  Hmm.

> 
> Fred

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
Posted by Joel Dezenzio (elricstorm)
on 2010-01-19 16:40
Thanks again guys for your input.  As a point of clarification...

My app(s) contain a combined 63 controllers across 4 applications, so if 
you wanted to divide that number by 4 you have less than 16 controllers. 
Many of these controllers are "not" client-facing controllers.  I have 
really 6 client-facing controllers in my entire combined app cluster.

The point of this thread was how to reduce code duplication across 
multiple controllers.  I threw out an absurd amount of controllers to 
make a point of reference.

As for the topic on plugins, I'm in agreement with Fred on this.  I have 
experienced on quite a few occassions upgrading to a newer version of 
rails only to find out the author abandoned the project and I had to dig 
into the code and fix it all myself.

There are plugins that I do use that I do not create or modify myself 
that have substantial backing.  RedCloth is an example of this. 
Restful_Authentication is another.
Posted by Marnen Laibow-Koser (marnen)
on 2010-01-19 18:49
Alpha Blue wrote:
> Thanks again guys for your input.  As a point of clarification...
> 
> My app(s) contain a combined 63 controllers across 4 applications, so if 
> you wanted to divide that number by 4 you have less than 16 controllers. 
> Many of these controllers are "not" client-facing controllers.  I have 
> really 6 client-facing controllers in my entire combined app cluster.

Ah, that makes more sense. :)

> 
> The point of this thread was how to reduce code duplication across 
> multiple controllers.  I threw out an absurd amount of controllers to 
> make a point of reference.

OK.  It seemed like you were talking about an actual case.  Sorry for 
misunderstanding.

> 
> As for the topic on plugins, I'm in agreement with Fred on this.  I have 
> experienced on quite a few occassions upgrading to a newer version of 
> rails only to find out the author abandoned the project and I had to dig 
> into the code and fix it all myself.

I've only experienced that very rarely.  Then again, I try not to choose 
plugins that look stagnant in the first place.

> 
> There are plugins that I do use that I do not create or modify myself 
> that have substantial backing.  RedCloth is an example of this. 
> Restful_Authentication is another.

(Of course, those happen to be two of the most broken plugins in common 
use...RedCloth, at least for a while, had sort of stagnated (is that 
still so?), and restful_authentication is based on generated crap code, 
and should be abandoned completely in favor of Authlogic.)

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
Posted by Joel Dezenzio (elricstorm)
on 2010-01-19 20:49
> (Of course, those happen to be two of the most broken plugins in common 
> use...RedCloth, at least for a while, had sort of stagnated (is that 
> still so?), and restful_authentication is based on generated crap code, 
> and should be abandoned completely in favor of Authlogic.)
> 

RedCloth has gotten better and there are multiple versions of the gem 
with some being more updated than others.  I try to find the people that 
use it the most and I use that gem or compile the binaries myself.

I liked restful authentication when I first tried it out but I have to 
agree with you that it's missing a lot of features that I personally 
would like to have in an authentication system.  I've had to tailor it 
to fit my needs.  I'll take a peek at Authlogic and see what it has to 
offer.

Not to get off topic, but what do you personally like about Authlogic, 
or some of the big features?

Thanks mate.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.