Search code examples
ruby-on-railsrubyurl-routing

Wrapping Named Route Helpers for Nested Routes


So I have just come out of an intense religious argument with a colleague.

We have object model and routes.rb:

resources :orgs do
  resources :lists do
    resources :posts do
      resources :replies
    end
  end
end

All these relations are one-to-many i.e. a list will always belong to exactly 1 org, a post will always belong to exactly 1 list etc. We are very aware of the general aversion to multi-nested routes, but have consciously decided to go in this direction.

Unfortunately this means that when you want to link to editing a reply, you have to write:

edit_org_list_post_reply_path( reply.post.list.org, reply.post.list, reply.list, reply )

which feels very silly.

I want to be able to do something like:

fast_path( action: :edit, model: reply, type: :path )

and use the fact that reply only belongs to one post, which belongs to one list etc. to work out the rest. Something like:

def fast_path options
  action = options[:action]
  model = options[:model]
  path_or_url = options[:type]

  model_fields = {
    reply: [:org, :list, :post]
    post: [:org, :list],
    list: [:org]
  }[model.class.name]

  arguments = model_fields.map { |field| model.send(field) } + [model]
  named_route_name = model_fields.join("_") + "_" + path_or_url
  named_route_name = action + "_" + named_route_name if action

  send(named_route_name, arguments)
end

although I haven't verified that this works or is particularly nice code.

HOWEVER, my colleague has done something like this before, where he overwrote many named routes in the name of making them more pleasant to call. He claims that it lead to nothing but misery, despair and torment, and is fighting tooth and nail to keep such attempts out of our code this time round.

I am very happy to be wrong, so please let us know what you think!


Solution

  • If you don't want to overwrite the path helpers (and there are undoubtedly good reasons not to do so), you could instead add a method to the Reply model like so:

    def path_args  
      [post.list.org, post.list, post, self]
    end
    

    and then just call: edit_org_list_post_reply_path(*reply.path_args).