I need to have one path accessible through multiple names. In my routes.rb I did
get '/route' => 'controller#edit', :as => 'name_a'
get '/route' => 'controller#edit', :as => 'name_b'
This works nicely but loads the routes table for nothing. From my understanding of the documentation, :as defines a helper method when called.
So I went to my ApplicationController and added
alias_method :name_b, :name_a
and I removed the second line from routes.rb
but that fails with Uncaught exception: undefined method name_a for class ApplicationController
is there any proper way of having two names for a single path?
=================EDIT====================
Elaboration:
I use Devise gem to manage session, registration, locking, etc. of 2 kinds of users, let's call them Admin and Guest. The gem is very well put but it asks for definitive route names to behave properly.
In my case, as far as devise is concerned, only the registration process is different so I'm trying to build a structure which looks as follow:
app
controllers
users
admin
registration_controller.rb
guest
registration_controller.rb
session_controller.rb
password_controller.rb
registration_controller.rb
the Admin and Guest controllers inherit from the above registration_controller which inherit's from Devise.
Now, to work properly, Devise needs for instance the names guest_user_password and admin_user_password to create or delete password retrievals. In my case, both are under the same path so I want both names to redirect to the same 'users/password' controller.
More important, and that's why I really wanted the alaising. Is that my views should not care whether it is dealing with Admin and Guest routes when redirecting to password retrieval controller. Both are users so I want to use user_password for both.
Hence my question. :)
Also note that as I wrote it, things works. I'm just trying to get the 'most elegant way' of writing it.
How about putting the alias in your ApplicationController?
class ApplicationController < ActionController::Base
alias_method :route_new, :route_old
helper_method :route_new
Remember that it's new name first, then old name.
The helper_method call is in order to use these in your views and not just controllers.
If you like, you can then place this in an included module called something like RouteAliases
You can add something like this to your routes.rb:
Rails.application.routes.draw do
...
Rails.application.routes.named_routes.tap do |named_routes|
named_routes['new_name'] = named_routes['real_name']
end
end
This will create new_name_path and new_name_url helpers. I have tested this with Rails 5.0.6.
Related
I see this new_confirmation_path(resource_name) called in generated Devise views.
I want to call this a custom view. The user isn't logged in.
new_confirmation_path(resource_name) is the way to generate a link to new confirmations page.
What is a resource_name?
How do I get it?
Is it possible to get it, in another (non-Devise) view?
Thanks.
resource_name in Devise is typically the name of the resource that you passed to devise_for in your config/routes.rb. So if you use devise_for(:users) it will be :user.
class DeviseController < Devise.parent_controller.constantize
def resource_name
devise_mapping.name
end
end
Devise uses the same views / controllers for many different models and does so through this mapping created in the routes which is injected into request.env.
If you know the model name you can also just use the named route helpers created for your app which would typically be new_user_confirmation_path.
I have completely re-written our Single Page Application (SPA) using a different technology, however instead of enforcing new UI to all the users, I would like them to opt to try new UI and similarly switch back to old UI, before enforcing the new UI to every user. The new UI was written keeping this is mind, however in routes.rb I need to define root manually to pick one of them. Ex:
root :to => 'dash#new' # for new UI
or
root :to => 'dash#old' # for old UI
How can this be achieved automatically? Something like:
default root will be 'dash#old'
when user opts to try new UI it should be stored in a new field in User model. (say user.newui = true)
as per the value of user.newui root should be picked. something like:
user.newui ? 'dash#new' : 'dash#old'
However obviously this is not possible. I do not have any user object in routes, most probably my whole solution is pointing south. Can someone please guide me on how to achieve this or whats the best practice?
You'd be best changing the layout in the controller, not middleware...
#config/routes.rb
root "dash#index"
#app/controllers/dash_controller.rb
class DashController < ActionController::Base
before_action :authenticate_user!, :set_layout
def index
current_user.newui?
# define data
else
# old data
end
end
private
def set_layout
layout (current_user.newui? "new" : "old")
end
end
You must remember that your "routes" are like a menu - you pick what you want and browse to it.
What is delivered from those routes is entirely dependent on the controller. In both instances, you're trying to invoke the dash#index action; what you're trying to do is ascertain whether the user has a different preference for how that will be displayed.
Since the middleware is meant to deal with the request (it doesn't know about the User), you'll be best using the controller to make the change.
If you had user object in routes it would be really easy.
Something like devise gem gives that to you out of the box. It allows you also define boolean triggers to user model.
For example I have default false admin tag that actually changes the route. Devise MODEL always gives you routes automatically so if you generate
rails g devise User you will have devise_for :users that you can use in your routes.
devise_for :admin do
root to: "admin#index"
end
root to: "home#index"
Without having user model there you can still define roots in controller but how you you persist them per user?
I have a bunch of controllers that extend the ApplicationController and each one loads a different cancan resource, e.g. UsersController -> #user, PostsController -> #post. I was wondering, is it possible to reference the resource from the ApplicationController without knowing the instance variable name? Something like resource_instance.
Ok, I got my final answer and it's No. I explored cancancan, cancan's well maintained spiritual child and the code is there, but is not meant to be used by the end user.
You could get what you want, it isn't pretty:
self.class.cancan_resource_class.new(self).send(:resource_instance)
That said, please don't. cancan_resource_class isn't documented to to be used by end users and :resource_instance is protected, hence the send. The developers could choose to change this and break your application.
If all have a company, they belong too, I recommend you nest the routes, so all routes have a :company_id you could use to get the #company object you want: companies/:company_id/users, etc.
Frustrating, I can't find an eligible solution for my problem.
In my Rails 4 app, I want to give my users the possibility to add their own custom post types to their sites. Like:
www.example.com/houses/address-1
www.example2.com/sports/baseball
Both would work, but only for the linked sites. Sports and houses would be the (RESTful) post types, taken from the db, added by users.
I have been struggling to find a elegant solution to accomplish this. I found http://codeconnoisseur.org/ramblings/creating-dynamic-routes-at-runtime-in-rails-4 but that feels kinda hacky and I'm not sure if reloading the routes works in production, I'm getting signals that it won't.
I'd say I have to use routes constraints http://guides.rubyonrails.org/routing.html#advanced-constraints but I don't have a clue how to approach this.
To be clear, I have no problem with the site setting stuff, the multi tenancy part of my app is fully functional (set in Middleware, so the current site is callable in the routes.rb file). My issue is with the (relative) routes, and how they could be dynamically set with db records.
Any pointers much appreciated.
I think route constraints don't work for you because your domain is a variable here. Instead, you should be examining the request object.
In your ApplicationController, you could define a method that would be called before any action, like so:
class ApplicationController < ActionController::Base
before_action :identify_site
def identify_site
#site = Site.where(:domain => request.host).first
end
end
As you scale, you could use Redis for your domains so you're not making an expensive SQL call on each request.
Then you can just add the #site as a parameter to whatever call you're making. I'm assuming you're doing some sort of "Post" thing, so I'll write some boilerplate code:
class PostController < ApplicationController
def show
#post = Post.where(:site => #site, :type => params[:type], :id => params[:id])
end
end
Just write your routes like any other regular resource.
I have an engine mounted to my main app and I want to protect certain controllers and actions within that engine.
The engine is mounted with:
mount SomeEngine::Engine => '/some_engine'
Devise/CanCan is working with the rest of the main app's controllers and actions, but letting things run without anything else produces this error:
This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check.
So I open up the engine controllers from the main app using the decorator approach and add:
load_and_authorize_resource
Then I get this error:
No route matches {:action=>"new", :controller=>"devise/sessions"}
I can get things working using the following, but it's clunky when I try to implement roles:
authenticate :administrator do
mount SomeEngine::Engine => '/some_engine'
end
By clunky I mean I'll have to reproduce the above block of code in the routes.rb file for each role that has access to the engine...unless there's another way to use authenticate with roles that I don't know about???
I'd like to use the normal Devise/CanCan authorization/authentication approach in the controller if possible. But I think "no route match" error occurs because the engine does not know how to get to the main app's Devise controllers. But how do I get around this from the main app?
To throw one more issue into the mix...there is one specific controller/action in the engine that I do want to make public to all users. Thus far I've just added this before the authenticate block of code in the routes.rb file.
match '/some_engine' => 'some_engine/some_controller#public_action'
It works...but this line with the block in the routes.rb seems like I'm doing something wrong. And it doesn't allow me to implement roles nicely.
You can inherit application controller for use devise and cancan from main app.
module SomeEngine
class ApplicationController < ::ApplicationController
before_filter :merge_abilities
private
def merge_abilities
current_ability.merge(SomeEngine::Ability.new(current_user))
end
end
end
After this you can create abilities for engine by create own.
module SomeEngine
class Ability
include ::CanCan::Ability
def initialize(user)
return if user.nil?
can :manage, SomeModel
end
end
end
SomeModel (SomeEngine::SomeModel) is model at SomeEngine engine.
At resource controllers you must specify class name of resource.
load_and_authorize_resource class: SomeEngine::SomeModel
And do not forgot change route helper to main_app.MAIN_APP_PATHS at main application layout if you want to use it at engine.