Nested Dynamic Routes

Hi,

I have a url scheme where I would like to include a public and private interface, and I have a nested resource for it.

The two schemes are

/my/books/pages /:username/books/pages

I would like to sperate the books and pages controllers into MyBooks, MyPages #=> the user here will always be the currently logged in user and PublicBooks, PublicPages #=> The users books will be whatever user is specified.

All public stuff is read only, although there will be a further nested resource on both (comments) that should be mapped to one controller to controll all comments, read and write.

I’m thinking at the moment that this will eliminate a lot of messy controller logic that tries to decide if I can access things or not, and tries to decide what user the context of the request is in etc.

The messiest that I can see this getting is if the user is accessing thier own book through a public interface and wants to say edit it. In this case I’m thinking of just re-directing to the edit action of the MyBooksController

I really don’t have a clue how to proceed here. Here’s what I’m thinking but I don’t believe this works.

map.resources :books :path_prefix => ‘my’, :controller => ‘my_books_controller’ do books.resources :pages, :controller => ‘my_pages_controller’ do pages.resources :comments end end

map.resources :books :path_prefix => ‘/:username’, :controller => ‘public_books_controller’ do

books.resources :pages, :controller => ‘public_pages_controller’ do

pages.resources :comments

end

end

Can anyone give a little advice please? Is this even a good way to go, or should I be looking elsewhere for a solution?

Cheers Daniel

Hey,

I'm assuming that you're asking if using different controllers for
those routes is the way to go.

Whenever I'm presented with those sorts of decisions, the strategy I
reach for the most is "a separate controller for each map.resource(s)".

It's not always the strategy that I *finish* with, but I find it's
always the best starting point.

Doing it like this stops me from over-generalizing too early. Once
the controllers have all been written with their specific use-cases
in mind ("Make it work"), everything is laid out in front of me. Any
ways I can make things DRYer are blindingly obvious ("Make it right").

Sometimes I'll decide to pull something back into a single do- everything controller. Other times I'll just create a few mixins or
maybe even an inheritance hierarchy to reduce duplication.

Nearly all of the time I'm just really grateful I resisted the
temptation to start off saying "hey, these are the same so I can just
use one controller!".

HTH, Trevor

I think what your suggesting is that what I’m contemplating isn’t too bad of an idea :wink:

I guess what I’m really asking is not so much if the urls are right. But if the concept of splitting the controllers like this is correct for what I’m trying to achive. Ie having MY user resource, and a public interface to OTHERs resources.

If this is the way to go. I would like to generate the urls I listed since I think they are self explanitory. So then in that case are these routes on the right track?

/my/books/pages

/:username/books/pages

map.resources :books :path_prefix => ‘my’, :controller => ‘my_books_controller’ do

books.resources :pages, :controller => ‘my_pages_controller’ do

pages.resources :comments

end

end

map.resources :books :path_prefix => ‘/:username’, :controller => ‘public_books_controller’ do

books.resources :pages, :controller => ‘public_pages_controller’ do

pages.resources :comments

end

end

Thanx for you help

Daniel

I think what your suggesting is that what I'm contemplating isn't too bad of an idea :wink:

I guess what I'm really asking is not so much if the urls are right. But if the concept of splitting the controllers like this is correct for what I'm trying to achive. Ie having MY user resource, and a public interface to OTHERs resources.

Yeah, what I'm saying is: absolutely yes, start by splitting the controllers. You may end up consolidating them once you've fleshed everything out, but *start* by coding the controllers to deal with their specific use-cases in mind.

Once that's done, the opportunities for doing this "right" will be staring you right in the face because your *code* will be telling you how it should be architected, not some random guy spouting rubbish on a mailing list :slight_smile:

I.e. you may look at the code and say "gee, all my urls should be /:username/books etc 'cause the only conditional would be a quick check to see if the current user name == :username to determine whether they can edit this resource". You won't know that for certain until all your moving parts are actually doing what they are supposed to do.

If this is the way to go. I would like to generate the urls I listed since I think they are self explanitory. So then in that case are these routes on the right track?

/my/books/pages /:username/books/pages

Don't forget to make sure a user can't use the word 'my' as their username :slight_smile:

HTH, Trevor

Thanx for the explianation.

At this stage it doesn’t seem as simple as a quick check because the required permission set makes it all a bit nasty together. Having said that, I currently have them all together and it smells a bit. Hence the question.

Thanx again. Daniel

I’ve created a branch to modify my code to reflect this, and it has quite suddenly occured to me that this will make views very difficult to do and keep them DRY. I would need to duplicate my views for each controller because of the named routes.

For example and forms where I have

<% form_for @book do |f| %>

No longer work since they look for a book_path. If I then change this to a my_book_path in the routes, then my new public controller will not be able to use it.

This isn’t so bad for forms, since the public controller doesn’t have write access to anything and so these should never be rendered for it.

Where the issue lies is in the link generation. e.g.

<%= link_to @book.title, book_url( @book ) -%> needs to become my_book_url( @book ) OR public_book_url( @book ) depending on how it is accessed.

If I have to duplicate my views, or have a bunch of logic in every time I want to set a link, I don’t think I will change over since I think this would be messier in the long run.

Any ideas on how to avoid this?

Thanx Daniel

Sorry if this comes twice. I’m having some trouble with gmail

I think what your suggesting is that what I’m contemplating isn’t too bad of an idea :wink:

I guess what I’m really asking is not so much if the urls are

right. But if the concept of splitting the controllers like this is correct for what I’m trying to achive. Ie having MY user resource, and a public interface to OTHERs resources.

Yeah, what I’m saying is: absolutely yes, start by splitting the controllers. You may end up consolidating them once you’ve fleshed everything out, but start by coding the controllers to deal with

their specific use-cases in mind.

Once that’s done, the opportunities for doing this “right” will be staring you right in the face because your code will be telling you how it should be architected, not some random guy spouting rubbish on

a mailing list :slight_smile:

I.e. you may look at the code and say "gee, all my urls should be /:username/books etc 'cause the only conditional would be a quick check to see if the current user name == :username to determine

whether they can edit this resource". You won’t know that for certain until all your moving parts are actually doing what they are supposed to do.

If this is the way to go. I would like to generate the urls I

listed since I think they are self explanitory. So then in that case are these routes on the right track?

/my/books/pages /:username/books/pages

Don’t forget to make sure a user can’t use the word ‘my’ as their

username :slight_smile:

HTH, Trevor

Thanx for the explianation.

At this stage it doesn’t seem as simple as a quick check because the required permission set makes it all a bit nasty together. Having said that, I currently have them all together and it smells a bit. Hence the question.

Thanx again. Daniel

I’ve created a branch to modify my code to reflect this, and it has quite suddenly occured to me that this will make views very difficult to do and keep them DRY. I would need to duplicate my views for each controller because of the named routes.

For example and forms where I have

<% form_for @book do |f| %>

No longer work since they look for a book_path. If I then change this to a my_book_path in the routes, then my new public controller will not be able to use it.

This isn’t so bad for forms, since the public controller doesn’t have write access to anything and so these should never be rendered for it.

Where the issue lies is in the link generation. e.g.

<%= link_to @book.title, book_url( @book ) -%> needs to become my_book_url( @book ) OR public_book_url( @book ) depending on how it is accessed.

If I have to duplicate my views, or have a bunch of logic in every time I want to set a link, I don’t think I will change over since I think this would be messier in the long run.

Any ideas on how to avoid this?

Thanx Daniel

Hey,

comments inline:

I've created a branch to modify my code to reflect this, and it has
quite suddenly occured to me that this will make views very
difficult to do and keep them DRY. I would need to duplicate my
views for each controller because of the named routes.

For example and forms where I have

<% form_for @book do |f| %>

No longer work since they look for a book_path. If I then change
this to a my_book_path in the routes, then my new public controller
will not be able to use it.

This isn't so bad for forms, since the public controller doesn't
have write access to anything and so these should never be rendered
for it.

Cool, so we can just ignore that one.

Where the issue lies is in the link generation. e.g.

<%= link_to @book.title, book_url( @book ) -%> needs to become
my_book_url( @book ) OR public_book_url( @book ) depending on how
it is accessed.

If I have to duplicate my views, or have a bunch of logic in every
time I want to set a link, I don't think I will change over since I
think this would be messier in the long run.

Okay, first I'll give you the "roll your own, low-impact" version.

The important thing to remember is that book_url etc are just
methods. I'm guessing that you're not going to permit editing and
deleting so really it's just book_path(book) and books_path() that
are important at the moment. Note that I didn't say book_url -
because you really only need to use that for redirects (which implies
editing, and the public controller doesn't need that).

Anyhow, in the public controller, you just do:

class PublicBooksController    protected    def book_path(*args)      public_book_path(*args)    end    def books_path(*args)      public_books_path(*args)    end    helper_method :book_path, :books_path end

So any time a request that's being served by PublicBooksController
sees a book_path or books_path call, it calls the relevant method.
Now MyBooksController and PublicBooksController can use the same
partials.

Now, there *are* times when you want to re-use views completely
(rather than just sharing partials), and when it's much more than
just book_path or books_path that you're calling. If this is one of
those cases then I've got a plugin called resource_fu which makes
wiring that up much easier:

http://agilewebdevelopment.com/plugins/resource_fu

But if the code I quote above is enough for you then I'd just stick
with that.

HTH, Trevor