Rails Engine not properly generating named paths when a used nested resource and render is called through a helper method #8533

Closed
tcannonfodder opened this Issue Dec 17, 2012 · 6 comments

Projects

None yet

5 participants

@tcannonfodder

Hello,

I've started to experiment with Rails engines, and noticed some odd behavior with Rails Engines and generating names paths when the engine is mounted as a nested resource.

Rails.application.routes.draw do
    resources :people do
        #when the engine is mounted as a nested resource, it works as desired, except when it comes to generating our paths in our engine's helpers
        mount BucketList::Engine => "/" #the goal is to access our lists like: person/:person_id/lists
    end

    #I don't want the route to be mounted at the top level, lists are owned by people
    #mount BucketList::Engine => "/bucket_list"

    #When I try to trick the route, I get the following error: ActionController::RoutingError (No route matches {}): app/views/people/show.html.erb:4:in `_app_views_people_show_html_erb___209278167088923249_70190424439760'
    #mount BucketList::Engine => "/people/:person_id/"
end

I have an engine that provides a simple todo list with items, and my dummy application creates a Person model. The lists can be owned, using a polymorphic association ownable. I have a helper method defined that is patched into ActiveRecord::Base named has_list, which gives the model the proper polymorphic association. This is mixed into the application in engine.rb.

The application works perfectly as a nested resource, which is great. However, I wanted to extend the functionality a bit further by creating a few helper methods to generate some "standard" views on the fly (like when showing a person's details, it would be nice if a list of all their todo lists was visible on that page). I opted to create a helper method to accomplish this goal ownable_list_list, which would take an ownable object and generate a simple list of all items with links to show/edit/delete. The helper simple calls render on _ownable_list_list.html.erb in my engine

following the advice provided in The Ruby on Rails Guides, I prefixed all of my named links in bucket_list (and even tried person_bucket_list, which is the name given by the routes in the dummy application). However, I receive the following error when trying to use these helpers:

undefined method `segment_keys' for nil:NilClass in app/views/people/show.html.erb:4:in `_app_views_people_show_html_erb___4217889445762085296_70138985747440'

For some reason it looks like when an engine is used as a nested resource, it is not properly being loaded up in ActiveController within our application so that we can properly generate our named routes (even when the named route is prefixed by our engine name or our generated name from our application's route file). A reason this behavior does not occur when used as a proper nested resource is that the application relinquishes control to the Engine at that point, which has its own route file and version of ActiveController. Plus the rendering is done within the scope of the Engine, as opposed to our helper, which "injects" the rendering into the scope of the application.

I've put my experiments into a git repo: https://github.com/LockeCole117/bucket_list

Excuse the mess, today I just wanted to see what Rails Engines were capable of.

Thanks for your time.

@steveklabnik
Member

/cc @drogus

@mattiassvedhem

Hi @LockeCole117 I'm having the exact same problem. Did you find a solution or workaround?

@mattiassvedhem

I managed to get the result I wanted by mounting the engine at the top level and then putting a generic resource nesting in the engines route set. Reusing @LockeCole117's example:

Engine

resources :owners, only: [] do
  resources :lists
end

Main app

mount BucketList::Engine => '/bucket_list', as: :bucket_list

bucket_list.owners_lists_path(Person.id)
=> http://domain.dev/bucket_list/owners/4f99f0eea2b279ec3d000006/lists 

Not ideal, but worked for my use case.

Update:
I ended up on another workaround where I provided a helper method, like devise_for, that sets up the routes in the main application instead.

@jdonas
jdonas commented Oct 24, 2013

Hi @mattiassvedhem I'm facing the same problem, could you help us with the helper method you used to set up the routes in the main application?

Thanks!!

@drogus
Member
drogus commented Oct 30, 2013

Sorry for a super late reply, I haven't been actively on Rails lately and I'm just going through some issues where I was mentioned.

I pushed a fix for this.

Could one of you test it in your engines and confirm that this is what you're looking for?

@drogus
Member
drogus commented Oct 30, 2013

PR was not linked here, a fix is here: #12699

@drogus drogus added a commit that closed this issue Dec 10, 2013
@drogus drogus Fix mounting engines inside a resources block
When a route is mounted inside a resources block, it's automatically
prefixed, so a following code:

    resources :users do
      mount Blog::Engine => '/blog'
    end

will generate a user_blog path helper.

In order to access engine helpers, we also use "mounted_helpers", a list
of helpers associated with each mounted engine, so a path to blog's post
can be generated using user_blog.post_path(user, post).

The problem I'm fixing here is that mount used a raw :as option, without
taking nestings into account. As a result, blog was added to a route set
as a `user_blog`, but helper was generated for just `blog`.

This commit applies the proper logic for defining a helper for a mounted
engine nested in resources or resource block.

(closes #8533)
e6c602d
@drogus drogus closed this in e6c602d Dec 10, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment