ActionController for Resources

Ok guys, its been a while since I've posted here. I don't think I've had a Rails feature added since the hash-as-conditions patch from forever and a half ago. I've been busy building apps and working on new languages. Oh, well and working on make_resourceful too. I'm not sure how familiar you guys are with make_resourceful but its a tank of an abstraction that helps "ninjas" to build really abstracted code for their resource based controllers.

Ever since the beginning, I knew that m_r wasn't right for Rails core. It was best as a plugin. There was a ton of magic and mystery in it... and frankly. It did too much. Lots of people use it today and enjoy it, but it requires *learning* a lot of new stuff to use it effectively.

I've been getting a lot of emails recently asking "what's next" for m_r... and so it got me thinking. What _is_ next? m_r has solved my issues with app development for 90% of my controllers for the past year and a half. So, should I really add anything?

Then, it hit me. Why not take the lessons I've learned from being a professional rails developer who has done (GASP) 14 production Rails apps with resources and come up with something a lot less ninja, and a lot more core.

So, here is the idea. One of the best things that m_r does is give you nice accessor methods for the resources you are currently working with. I noticed that in most of my controllers I was repeating lines like this..... "User.find(params[:user_id]).messages.find(:all)". Over, and over, and over, and over again. Even with the create method for that "message", I'd want to scope it,because I'd want the magic of having an automatically assigned user_id on the message (or, I'd just be lazy and do a second line). Anyhow, that's the least DRY part about doing a resourceful controller. Accessing the stuff you want.... over and over and over.

So, here is my proposal: http://pastie.org/230474

The way its structured is first is a really simple example. Followed by two complex examples, THEN followed by those last two examples in their *explicit* form. Basically, the explicit form is the code that would be automatically added by the ::load call.

Basically, you call the class method "load" on ActionController::Base and pass it an array of classes or symbols (read the code there to figure out the symbol bit... I'll just stick to classes here). The last arguement to it is your resource. And the ones before it are the scopings.

If I say "load User"... it builds four methods.... #user, #users, #build_user, and #update_user. Read the explicit versions before to get the gist of what each one of them does.

The problem with m_r and other proposals are that they assume too much. However, I think that this is just the right mix. I'd be surprised to find a resource controller that couldn't be cleaned up by this abstraction.

Too much magic? Not really. Think of it like belongs_to for your controllers. No one complains that belongs_to "creates" (I know, not literally) methods on your AR models.

I'd love feedback on this. If its not right for core, I'll just make a plugin and go about pimping it. If it seems like something that MIGHT be interesting for core, then I'll fork and fiddle.

Thoughts?

-hampton.

+1

I'd love feedback on this. If its not right for core, I'll just make a plugin and go about pimping it. If it seems like something that MIGHT be interesting for core, then I'll fork and fiddle.

I think there's definitely something to this, at the very least it's worth investigating what kind of functionality we can add in. For a while now there's been talk about how there really should be some concept of resources that you could introspect to do these kind of useful things.

Have a look at Resources Controller (RC) plugin, which is a pretty mature and functional set of functionality for getting the relevant resources for a controller. RC is strong -I gave up my own solution to adopt RC (and contribute a bit). Ian White, the author, has really kept it going.

http://groups.google.com/group/resources_controller?hl=en

Also, see my blog post on how Rails routing could help the cause without having to pick (yet) a particular implementation::

http://cho.hapgoods.com/wordpress/?p=151

"Full Circle REST" is where it's at.

-Chris

Also, see my blog post on how Rails routing could help the cause without having to pick (yet) a particular implementation::

http://cho.hapgoods.com/wordpress/?p=151

"Full Circle REST" is where it's at.

Definitely, and I think we've discussed this previously on #rails-contrib?

I'm not necessarily sold on having the concept of the 'resource' you're looking at tied so tightly to routes. Perhaps there's another class we should have which is available on the request? request.resource or something with the necessary bits and bobs to introspect successfully.

I think the best way forward is for the authors of the relevant plugins (m_r and RC) to figure out if there's a subset of functionality which they could share, and then figure out what changes we'd need to make to rails to enable that functionality, or if we should adopt it ourselves.

My point with all of this is to massively strip down the functionality that is available in the plugins and just have an extremely simple interface for the most basic, basic resource facilities in a controller. Then, build from there if the need arises.

-hampton.

My point with all of this is to massively strip down the functionality that is available in the plugins and just have an extremely simple interface for the most basic, basic resource facilities in a controller. Then, build from there if the need arises.

Sounds great to me :slight_smile:

There's also my plugin resource_controller - a little naming collision there, but nonetheless...

All three of these plugins have a decent amount of traction, and something to offer. I do think that it makes sense for the three of us to sit down, and brainstorm over what we can do to add this to rails. Then, our respective plugins can use that as a foundation for building magical abstractions on top of.

I like Hampton's proposal - I think it covers a lot of cases, while remaining relatively neutral in terms of what it expects.

The only things I'm not clear on is how it would cover polymorphism, or cases where the association has a non-standard name. Sure, you could override all the methods that it 'generates', but that would be a pain, and not very DRY. It'd be nice to expose some methods like m_r and my r_c do that would allow you to define the way objects, collection, and associations are loaded for each level.

For polymorphism, sticking with Hampton's syntax, we'd end up with:

load [User, Project], [Project], [Account, Project]

.. which I'm not crazy about. I think something like:

resource_for Project # could be inferred from controller name if std belongs_to x, y, [z, a] # args here are x, OR y OR z, a

...potentially replacing belongs_to with parents, or another keyword...

Lastly, I personally dislike the idea of controller actions being tied to routes, as they are in resources_controller. We tried that in a branch as well, and I wasn't crazy about it (http://github.com/ giraffesoft/resource_controller/commits/automatic_route_discovery). It's too much magic, too much generated code, too hard to understand what's going on for anybody not familiar with the plugin. And, I'm betting that it's pretty damn hard to debug too.

J.

I like Hampton's proposal - I think it covers a lot of cases, while remaining relatively neutral in terms of what it expects.

I wholeheartedly concur.

The only things I'm not clear on is how it would cover polymorphism, or cases where the association has a non-standard name. Sure, you could override all the methods that it 'generates', but that would be a pain, and not very DRY. It'd be nice to expose some methods like m_r and my r_c do that would allow you to define the way objects, collection, and associations are loaded for each level.

For polymorphism, sticking with Hampton's syntax, we'd end up with:

load [User, Project], [Project], [Account, Project]

.. which I'm not crazy about. I think something like:

resource_for Project # could be inferred from controller name if std belongs_to x, y, [z, a] # args here are x, OR y OR z, a

...potentially replacing belongs_to with parents, or another keyword...

I don't know, I kinda think this bloats the controller and ties it to strictly to the model. If I think of my projects, it's probably 95% standard (i.e. pluralized table names, etc. and no polymorphism) and I guess it's not that much different for other people (correct me if I'm wrong). And in the 5% where we actually deviate from the standard case, we could easily handcode similar methods. I know that's not ultra-DRY but this way the actual API stays clean and keeps it down to a reasonable amount of magic while still covering most cases.

- Clemens

I'm not necessarily disagreeing.

However, without some hooks for overrides, and polymorphism, plugins that do RESTful abstraction layers, like the ones mentioned in this thread, will still be forced to write quite a bit of custom, duplicated code in order to handle those cases. It would be nice to merge all that duplicate code in to core, and let the plugins do the truly magical stuff (abstracting entire actions, customization DSLs, and even action inference based on routes).

...or maybe Hampton, Ian, and I should come up with a base plugin / API that we base all of our plugins on?

...or maybe Hampton, Ian, and I should come up with a base plugin / API that we base all of our plugins on?

You guys are bound to have a bunch in common, basic core stuff. Of that set of features there's probably some stuff that still won't be fit for inclusion in the core, but odds are there's a subset of that which is. Whether that functionality is user-visible stuff or just nice hooks, I don't know.

You guys are the experts, I'm sure you'll come up with some awesome stuff :).

This is full of win. +1

+1 and I look forward seeing the solution.

My point with all of this is to massively strip down the functionality that is available in the plugins and just have an extremely simple interface for the most basic, basic resource facilities in a controller.

Sounds like a great plan. So what's basic?

(I'm the resources_controller guy, btw)

- crud: Hampton's proposal of generated cruddies looks really nice, is easy to understand, and override (let's say you want to order your messages by some param, you can just override messages). Handling non standard names should be easy enough to handle

  load User, Message => {:as => :tweet}

# or, it might be nicer if spread over two lines?

  load User   load Message, :as => :tweet

- scoping: leveraging AR's assocs to provide scoping. Also nicely covered by Hampton's proposal. However, singleton vs collection resources need different handling. In Hampton's example, perhaps:

  load User, Image => {:singleton => true}

# or

  load User   load Image, :singleton => true

  # doesn't generate images, and generates this:   def image     user.image   end

- polymorphism. Not obvious, as James points out. I use polymorphism a lot, which is why I gave r_c the ability to load up the resources differently depending on the route that invoked it. I guess people might argue whether this is 'basic'. (From my perspective, I got sick of writing the same comments controller over and over again.)

if this is regarded as a 'basic', then

  load :commentable, Comment

could do the trick.

To implement this sort of thing, r_c gleans info from the route that invoked the controller, this means we need to introspect the route that the controller was invoked with.

So, it looks like Hampton's proposal covers the 'has_many' resource case, and should cover the 'has_one' case with a little tweaking, and could handle non standard names. It seems that 'has_many :as', and 'has_one :as' would require something of a different nature (route introspection). Perhaps for that reason alone, perhaps polymorphism should not be regarded as basic?

I would be happy with this, because that's what plugins are for.

(polymorphism is a specific case of being able to nest resources at arbitrary points - this problem is solved by the same technique. the one images controller can service /users/:id/images /cats/:id/images)

But, if you think polymorphism should be regarded as basic...

Michael Koziarski wrote:

request.resource or something with the necessary bits and bobs to introspect successfully.

This would be a great abstraction. r_c currently emulates this behaviour, by abstracting the resources from the path (using the routing code). This enables the polymorphic and nesting cases

In Rails, routing does some funky awesomeness to figure out the mapping of url to controllers and params, but it throws (most of) that work away before the controller is invoked. Having a resource abstraction would be the container for passing around RESTful resources, while being agnostic about how they were generated, and what they map to.

On this last point, Chris Cruft, and other rc_ers have made the point to me that they sometimes have restful controllers which don't have ActiveRecords behind them (or have AR enclosing resources, but not at the endpoints), it would be nice to have an ordered array of abstract resources handed to the controller, and that request.resource would be.

(btw. It just so happens I'm giving a talk at RailsConfEurope about r_c, and how rails could help /.*resource.*/ plugin authors by passing essentially request.resource to the controller. I shall watch this thread in earnest - maybe I'll have to rewrite the damn thing. Yay for rewriting the damn things!

Cheers, Ian

Hi All,

> http://pastie.org/230474

I've used m_r, r_c and others, and while promising at start, I had to remove them because most of the time the defaults weren't the defaults as I want them to be and they became too obtrusive. The problem, as hampton already indicated, is that they try to do too much. In my case especially with regards to action behaviour. Not so much with regards to resource facilities.

What I do like about hamptons proposal is that he's indicating he wants the most basic resource facilities.

However, instead of baking this in the ActionController I think resources deserve to be first class citizens. This idea was actually born by Dan Yoder after long discussions on the Waves project. The term "resource controller" that most are using is a misnomer, the two should be decoupled imo.

I.e., when I do map.resources :posts, I would like to see this routed to:

  /app/resources/posts_resource.rb

This class would inherit or mixin a ResourceService (a term r_c uses).

There you would do the kind of magic that hampton suggests in his pastie and that is happening in all the plugins that have been plugged in this thread. You would think more in terms of the resources instead of in terms of the controller action. This is the place where you set up the resources required, where you define how a new resource is created, etc. All this would stay out of your controllers.

Once the resources are set up you go to your controllers. Just like views have access to the instance variables defined in a controller the controller would have access to the instance variables defined in the resource. This also makes controllers more re-usable for multiple resources, and gives greater potential for DRYness. Which is the other part that all these "resource controllers" are trying to solve. So instead of having a controller for every resource you could have just a few controllers (ideally just one). This again would highlight that controllers are about action, orchestration, driving behaviour, and not about managing resources.

Lawrence

What I do like about hamptons proposal is that he's indicating he wants the most basic resource facilities.

Definitely, I think it's a great idea to come up with a nice evolutionary enhancement to the resources code, along with some added metadata for reflecting on what's going on.

However, instead of baking this in the ActionController I think resources deserve to be first class citizens. This idea was actually born by Dan Yoder after long discussions on the Waves project. The term "resource controller" that most are using is a misnomer, the two should be decoupled imo.

I think that this kind of experimentation should definitely take place in a plugin. The controller + routes thing we have now works really well, and some minor enhancements could well improve that. Something larger like you're talking about here should be left in plugins till it's had a chance to prove itself and get fully baked.

Yes, we did. You might also say that I badgered you about Full-circle REST...

...

> "Full Circle REST" is where it's at.

Definitely, and I think we've discussed this previously on #rails-contrib?

...

So my main disagreement with the other arguments here is that they are too simple for an initial feature. Originally, AR didn't support Polymorphic and so I'm hesitant to spend too much time worrying about it. I find I use polymorphic things in about 5% of my resources. Of course, some projects require more polymorphism and others require 0%. Just based on the requirements.

My main thoughts with this is that it defines a clean and simple API and pattern for controllers with resources. And its a *starting point*. After this kind of things gets acceptance (and is put through heat) then I think solving more complex issues is the next step.

Most of the people reading this do very very advanced Rails applications. And to be honest, I'm not concerned about us here. I mean, I do want to solve my own problem, but I really want to give a simple solution for a common problem and then build on that.

Even when it comes to has-one associations... they kind of suck. I mean, maybe I'm just a lame developer, but I do a fuckload of projects and I *rarely* find has-one to be required. Rarely, rarely, if ever. I'd rather just break out of the stock REST controller and handle it. Or, usually, I just fucking put it in the parent model.

I'm going to build this as a simple plugin. And no, it won't be integrated into m_r. m_r handles more complex cases. This is for when m_r is overkill.

-hampton.

This is awesome, and does a lot of what we talked about in this thread: http://intridea.com/2008/7/28/fetches-bringing-your-actioncontroller-its-slippers

+1

I loved m_r for quite a while before dropping it because of huge amount of magic and difficulty of the underlying code.

a super simple/transparent replacement would be great.