I'm trying to allow for one resource, Site
, to conditionally have a parent resource, Organization
.
Here's what I currently have:
resources :site do
resources :posts do
resources :comments
end
end
this results in paths like
/sites/1
/sites/1/edit
/sites/1/posts
/sites/1/posts/123/comments
# etc. etc.
I want the ability to have paths like
/parent_id/sites/1
/parent_id/sites/1/edit
/parent_id/sites/1/posts
/parent_id/sites/1/posts/123/comments
but only if the Site belongs to an Organization.
I also don't want to have to change every single path helper already in use across my site (there are literally hundreds of places).
Is this possible?
Here's what I've tried:
scope "(:organization_id)/", defaults: { organization_id: nil } do
resources :site
end
# ...
# in application_controller.rb
def default_url_options(options = {})
options.merge(organization_id: @site.organization_id) if @site&.organization_id
end
but that didn't work. organization_id
wasn't getting set.
# in my views
link_to "My Site", site_path(site)
# results in /sites/1 instead of /321/sites/1
I also tried setting the organization_id in a route constraint, and that didn't work as well.
I ended up writing a monkey patch overriding the dynamic method generated for all of my relevant paths. I used a custom route option, infer_organization_from_site
that I look for when dynamically generating routes. If the option is set, I add site.organization
as the first argument to the helper call.
# in config/routes.rb
scope "(:organization_id)", infer_organization_from_site: true do
resources :sites do
resources :posts
# etc.
end
end
# in an initializer in config/initializers/
module ActionDispatch
module Routing
# :stopdoc:
class RouteSet
class NamedRouteCollection
private
# Overridden actionpack-4.2.11/lib/action_dispatch/routing/route_set.rb
# Before this patch, we couldn't add the organization in the URL when
# we used eg. site_path(site) without changing it to site_path(organization, site).
# This patch allows us to keep site_path(site), and along with the proper
# optional parameter in the routes, allows us to not include the organization
# for sites that don't have one.
def define_url_helper(mod, route, name, opts, route_key, url_strategy)
helper = UrlHelper.create(route, opts, route_key, url_strategy)
mod.module_eval do
define_method(name) do |*args|
options = nil
options = args.pop if args.last.is_a? Hash
if opts[:infer_organization_from_site]
args.prepend args.first.try(:organization)
end
helper.call self, args, options
end
end
end
end
end
end
end