Variable root in Rails router - ruby-on-rails

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?

Related

How do I create the users root path route using the Devise gem?

I did a search and found this question on SO, but the accepted answer doesn't seem to be working for me. Basically, the Divise wiki says...
After signing in a user, confirming the account or updating the
password, Devise will look for a scoped root path to redirect. For
instance, for a :user resource, the user_root_path will be used if it
exists, otherwise the default root_path will be used.
With my amateur knowledge of RoR, I have a devise model called Player, and I created the following statement in my routes.rb file...
match 'player_root', to: 'pages#play', as: :player_root, via: :all
...but with that, my app always redirects to my root path, instead of the players root path, which I defined above. What am I doing wrong?
Thanks in advance for your wisdom! Also, I'm using Ruby 2 with Rails 4.
As I understand, you are trying to specify root_path for :players.
If order to do that, you can use following:
authenticated :players do
root to: 'pages#play', as: :authenticated_root
end
This will give you custom root_path for signed in users (players).
Further to Andrey Dieneko, there are two other options you have:
Use unauthenticated_path
Use authenticate_user! in your controller
The bottom line here is that you may be thinking about incorrectly. You may be trying to work out where to take users if authenticated. However, you may be better suited to actually using the authentication methods in the controller to test whether the user is logged in, and if not route them to a login page:
#config/routes.rb
root to: "players#play"
#app/controllers/players_controller.rb
class PlayersController < ApplicationController
before_action :authenticate_user!
end
This will take a user to the "login" path if they are not logged in.
Alternatively, you can use unauthenticated_path like so:
#config/routes.rb
root to: "players#play"
unauthenticated do
root to: "application#landing"
end
--
This method will only be best if you have an app like Facebook (IE it has no "landing page" etc)
I think Andrey's answer is more apt (especially if you have a landing page)

Add new view to a controller in Rails

I have a controller, clients_controller, with corresponding index, show, edit, delete, new & form views. Is there a way to create a new view like clients/prospects.html.erb that acts the same way as clients/index.html.erb, except is routed at clients/prospects/?
I've tried this:
match '/clients/prospects' => 'clients#prospects'
And some other things in routes.rb, but of course get the error "Couldn't find Client with id=prospects".
The goal here is basically to have a prospects view and a clients view, and by simply switching the hidden field to a 1, it (in the user's mind) turns a prospect into a client (it's a CRM-like app).
There's a couple of things you need to do. First you need to put the your custom route before any generic route. Otherwise Rails assumes the word "prospects" is an id for the show action. Example:
get '/clients/prospects' => 'clients#prospects' # or match for older Rails versions
resources :clients
Also you need to copy / paste the index method in your ClientsController and name it prospects. Example:
class ClientsController < ApplicationController
def index
#clients = Client.where(prospect: false)
end
def prospects
#prospects = Client.where(prospect: true)
end
end
Lastly, you need to copy the index.html.erb view and name the copy prospects.html.erb. In the example above you would have to work with the #prospects instance variable.
Create a new action in clients controller named prospects. And then define a collection route in routes.rb for it as either resource full way. Or u directly use match as you were doing.
What you're doing is not wrong (although I'd change match to get, otherwise POST and DELETE requests to that url will also render your prospects view). Presumably you have
resources :clients
in your routes file? If so, what you have will probably work if you just move the line you quoted above the resources declaration -- the problem is that /clients/prospects matches the show route for the clients resource, so if it's defined first then that's the route that gets matched.
However, there's a more idiomatic way to define this route
resources :clients do
collection do
get :prospects
end
end
See Rails Routing documentation for more
Also see migu's answer for what else needs to be done once the url is being routed correctly (though there are other things you can do -- if you the two views are similar enough, you can reuse the view template, for example).

Obtaining ID of containing resource via params[:id] for custom actions

I have the following routes in my config/routes.rb file:
resources :employees do
get 'dashboard'
get 'orientation'
end
employees refers to a regular resource handling the standard RESTful actions. dashboard and orientation are what I currently refer to "custom actions" which act on Employee instances. I apologize if I have my terminology mixed up and dashboard and orientation are really something else. These custom actions respond to URLs as follows:
http://myhost/employees/1/dashboard
i.e. They're "member" actions much like show, edit etc.
Anyway, this all works well enough. Regular actions such as show on EmployeesController obtain the ID of the associated Employee through params[:id]. However, with this current structure, dashboard and orientation have to use params[:employee_id] instead. This is not too difficult to deal with, but does lead to some additional code complexity as my regular before_filters which expect params[:id] don't work for these two actions.
How do I have the routing system populate params[:id] with the ID for these custom actions in the same way as show etc.? I've tried various approaches with member instead of get for these actions but haven't got anything to work the way I would like yet. This app is built using Ruby on Rails 3.2.
This might help you:
resources :employees do
member do
get 'dashboard'
get 'orientation'
end
end
and the above will generate routes like below, and then you will be able to use params[:id] in your EmployeesController.
dashboard_employee GET /employees/:id/dashboard(.:format) employees#dashboard
orientation_employee GET /employees/:id/orientation(.:format) employees#orientation
I haven't tested this example, but you can set the resourceful paths explicitly.
Something like this might work:
resources :employees, path: '/employees/:id' do
get 'dashboard', path: '/dashboard'
get 'orientation', path: '/orientation'
end

how to create dynamic routes and helpers in rails3

i have a task to create mapping of different urls at run time .
In the application i have a GUI interface which displays list of routes from routes.rb file.
User has the ability to change that url to some different name from the interface
eg. (abc/mno) --user can change them to --(hello)
so if user type /hello in the browser request is redirected to /abc/mno
i have to store those mapped routes in a database.
how to add a dynamic mapped route to already defined routes(routes.rb) while creating a new record in database
how to add routes from the database while loading routes.rb file.
i am not able to figure out how to extend the default router so that it can include routes from the database ..
I don't have a complete solution for you, but you can start with two approaches:
Use custom URL constraint: Dynamic URL -> Controller mapping for routes in Rails
Use Rack middleware: Dynamic Rails routing based on database
If you don't want to use rack middleware, you can use constraints. Hopefully, your dynamic routes are scoped to something, like "/abc/anything-after-here-can-be-dynamic", as opposed to straight off the root...
So, lets say you wanted dynamic routes based upon User's first name, then you would do the following:
#config/routes.rb
match '/abc/:route' => "abc#dynamicroute", :contraints => DynamicRouteConstraint.new
#lib/dynamic_route_constraint.rb
class DynamicRouteConstraint < Struct.new
def matches?(request)
User.find_by_first_name(request.params[:route]).present?
end
end
#app/controllers/abc_controller.rb
class AbcController < ApplicationController
def dynamicroute
#user = User.find_by_first_name(params[:route])
#render or redirect, however you wish
end
end

Alias route's name

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.

Resources