1

I am writing on a Rails engine (Subscribe) which will be the base of a multi-tenant Rails app and handle subdomains, accounts, users (with Devise) and subscriptions.

Everything works fine except for when the devise_invitable Devise::InvitationsController renders the application layout. When this happens, the layout rendering crashes with errors such as: undefined local variable or method `destroy_user_session_path'

I have no idea why the url helpers are not available in this context as they seem available for the rest of the application.

The application layout still have access to Devise variables such as current_user and if I remove the link to destroy_user_session_path the invitation#new action renders the layout just fine.

Running "rake routes" within the dummy application (spec/dummy) shows the routes correctly. I have also tried the following:

  • Use pry and insert a breakpoint using "binding.pry" inside the application layout itself. "show-routes" still shows the routes correctly.
  • Override Devise::InvitationsController with my own controller and included the Rails URL helpers manually to no avail.

config/routes.rb

require 'subscribe/constraints/subdomain_required'
require 'subscribe/constraints/no_subdomain'

Subscribe::Engine.routes.draw do

  constraints Subscribe::Constraints::SubdomainRequired do
    scope module: "account" do
      root to: "dashboard#index", as: :account_root
    end

  devise_for :users, 
    class_name: "Subscribe::User", 
    module: :devise, 
    path: "/",
    :controllers => { :invitations => 'subscribe/user_invitations' }

  resources :users

end

spec/dummy: 'rake routes'

Routes for Subscribe::Engine:
          account_root GET    /                            subscribe/account/dashboard#index
      new_user_session GET    /sign_in(.:format)           devise/sessions#new
          user_session POST   /sign_in(.:format)           devise/sessions#create
  destroy_user_session DELETE /sign_out(.:format)          devise/sessions#destroy
         user_password POST   /password(.:format)          devise/passwords#create
     new_user_password GET    /password/new(.:format)      devise/passwords#new
    edit_user_password GET    /password/edit(.:format)     devise/passwords#edit
                       PATCH  /password(.:format)          devise/passwords#update
                       PUT    /password(.:format)          devise/passwords#update
accept_user_invitation GET    /invitation/accept(.:format) subscribe/user_invitations#edit
remove_user_invitation GET    /invitation/remove(.:format) subscribe/user_invitations#destroy
       user_invitation POST   /invitation(.:format)        subscribe/user_invitations#create
   new_user_invitation GET    /invitation/new(.:format)    subscribe/user_invitations#new
                       PATCH  /invitation(.:format)        subscribe/user_invitations#update
                       PUT    /invitation(.:format)        subscribe/user_invitations#update
                 users GET    /users(.:format)             subscribe/users#index
                       POST   /users(.:format)             subscribe/users#create
              new_user GET    /users/new(.:format)         subscribe/users#new
             edit_user GET    /users/:id/edit(.:format)    subscribe/users#edit
                  user GET    /users/:id(.:format)         subscribe/users#show
                       PATCH  /users/:id(.:format)         subscribe/users#update
                       PUT    /users/:id(.:format)         subscribe/users#update
                       DELETE /users/:id(.:format)         subscribe/users#destroy

config/initializers/devise.rb

config.parent_controller = 'Subscribe::ApplicationController'
config.router_name = :subscribe
config.scoped_views = true

spec/dummy/app/views/layouts/application.html.slim

doctype html
html
  head
    title
    = javascript_include_tag "application", "data-turbolinks-track" => true
    = csrf_meta_tags
  body
    - flash.each do |k,v|
      div class = "flash #{k}"
        = v
    br

    - binding.pry
    - if user_signed_in?
      = "Signed in as #{current_user.email}"
      = link_to "Sign out", destroy_user_session_path, method: :delete 
    -  else
      = "Not signed in"
    br

    == yield

app/controllers/subscribe/user_invitations_controller.rb

module Subscribe
  class UserInvitationsController < Devise::InvitationsController
    include Rails.application.routes.url_helpers
    include Rails.application.routes.mounted_helpers

    def new
        #binding.pry
        super
    end

  end
end

subscribe.gemspec

$:.push File.expand_path("../lib", __FILE__)

# Maintain your gem's version:
require "subscribe/version"

# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
  s.name        = "subscribe"
  s.version     = Subscribe::VERSION
  s.authors     = ["author"]
  s.email       = ["author@example.com"]
  s.homepage    = "https://.com"
  s.summary     = "Subscriber engine for .com"
  s.description = "..."

  s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]

  #Framework
  s.add_dependency "rails", "~> 4.1.0"

  #Auth
  s.add_dependency "devise", "~>3.5.0"
  s.add_dependency "devise_invitable", "~>1.5.5"
  s.add_dependency "pundit", "~>0.2.3"

  #Layout and interface
  s.add_dependency "slim-rails", "~> 2.1.2"
  s.add_dependency "jquery-rails", "~> 3.1.4"

  #Testing
  s.add_development_dependency "rspec-rails", "~> 3.0.0"
  s.add_development_dependency "capybara", "~> 2.3.0"
  s.add_development_dependency "capybara-screenshot", "~> 0.3.17"
  s.add_development_dependency "factory_girl_rails", "~> 4.4.1"
  s.add_development_dependency "database_cleaner", "~> 1.2.0"

  #Tools and support
  s.add_development_dependency "spring", "~> 1.1.3"
  s.add_development_dependency "spring-commands-rspec"
  s.add_development_dependency "sqlite3"

  s.add_development_dependency "better_errors"
  s.add_development_dependency "binding_of_caller"
  s.add_development_dependency "pry-rails"
  #s.add_development_dependency "guard-rspec" - does not work atm
  s.add_development_dependency "rb-inotify"
  s.add_development_dependency "libnotify"

end

app/controllers/subscribe/application_controller.rb

module Subscribe
  class ApplicationController < ActionController::Base
    layout "application"
    protect_from_forgery with: :exception    
  end
end

Full source for current work-in-progress can be found here (README.md is outdated):

https://github.com/heliohm/Subscribe

heliohm
  • 29
  • 6

1 Answers1

0

Within the context of the Devise controllers it seems the paths must be namespaced with the engine name.

Changing destroy_user_session_path with subscribe.destroy_user_session_path resolves the problem.

Since the engine is namespaced it is also necessary to override the default views included with devise_invitable (such as app/views/devise/mailer/invitation_instructions.html.erb) as they use routes such as root_url which do not exist in this case.

This is also the case for all Devise controllers, e.g. the Devise SessionsController will also not find these routes in the top level name space.

heliohm
  • 29
  • 6