Add new view to a controller in Rails - ruby-on-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).

Related

change routing name dynamically

I have to modify the routes file in order to have SEO improvement.
This is my context, a rails backend generate a JSON feed with the route's name in, I have to read it and change the default name.
For example, I have this:
get '/people' => 'people#show', as: :people
and I'd like to change /people in some value read from my JSON feed.
I created a class to get the JSON object in my app
class JSONDatabase
def initialize(kind_of_site)
#kind_of_site = kind_of_site
end
def fetch_database_remote(url)
JSON.parse(open(url).read)
end
end
but how can i access it in routes file?
Thank you
You don't necessarily need to modify your application's routes. What you can do is define a wild card route that leads to a unique controller where you read the updated route. This approach is kind of hackish but gives you the unlimited routes you need without modifying the routes.
Your config/routes.rb file would look something like this:
resources :defined_models
root to: 'controller#action'
# At last we define the wildcard route
get '/:route' => 'routing_controller#routing_action'
Then, at this routing action we can do the job of seeing if this route (now defined in the params[:route] variable) corresponds to the modified one. Just remember to redirect to a 404 if the route given is not defined, since with this approach you loose the Rails default way of dealing with undefined routes.

Creating semi-private urls for show action in rails app

I am building a small app, that allows users to create lists and within these lists they can add gifts that they want. So far its very similar to a ToDo list app.
I have three models:
User - Can have many Lists
List - Can have many gifts and belongs to User
Gift - Belongs to List
In my List model as well as storing the name of the list, Im also creating a unique string of letters and numbers and storing it as shared_key in the record. The code looks like this:
def create_unique_url
begin
self.shared_key = SecureRandom.urlsafe_base64(10)
end while self.class.exists?(shared_key: shared_key)
end
and ideally I want the url to look something like this app.com/public/long_string_shared_key_goes here
My main Question is, how should do I go about setting up a route to access the record at this public address.
Should I create another controller called public and have a show method there? Or should I create a public action in my llist controller and somehow manually create a route to it?
Since it's just a matter of single action I'd not suggest to redefine the #to_param, since it might affect all of your existing functionality. Still a matter of taste, mostly
routes:
resources :lists, except: [ :show ]
get '/public/:shared_key' => 'lists#show'
controller:
def show
#list = List.find_by(shared_key: params[:shared_key])
end
view:
link_to list.name, list_path(shared_key: list.shared_key)
In you case i would just alter a little the showing of the list. First in the list model redefine the to_param method
def to_param
long_string_shared_key_goes #by default this was returning the id - example...to access show a list you had to navigate to /lists/:id
end
Now you need to change the routes.rb
match 'public/:long_string_shared_key_goes', to :'lists#show', via: [:get]
Now you just have to change some find methods (if you are accessing the lists with example list.find(params[:id] you now have to take in consideration that you dont have the id in the params, but you long_string_shared_key).
Hope this answered your question.

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

Trying to make urls of the form <base_url>/boards/<name> in ruby on rails and getting errors

I am using ruby on rails to make a simple social networking site that includes different message boards for each committee of a student group. I want the url structure for each board to look like https://<base_url>/boards/<committee_name> and this will bring the user to the message board for that committee.
My routes.rb file looks like:
resources :committees, only: [:index]
match '/boards/:name', to: 'committees#index(name)'
My index function of committees_controller.rb file looks like:
def index(name)
#posts = Committee.where(name: name)
end
And then I'll use the #posts variable on the page to display all of the posts, but right now when I navigate to https://<base_url>/boards/<committee_name> I get an Unknown Action error, and it says The action 'index(name)' could not be found for CommitteesController.
Could someone guide me through what I have done wrong?
Once I get this working, how would I make a view that reflects this url structure?
Set up your routes like this:
resources :committees, only: [:index]
match '/boards/:name', to: 'committees#show'
and the controller like this:
def index
#committees = Committee.all
end
def show
#committee = Committee.find_by_name!(params[:name])
end
You can't really pass arguments to controller actions the way you were trying to with index(name). Instead, you use the params hash that Rails provides you. The :name part of the route declaration tells Rails to put whatever matches there into params[:name].
You also should be using separate actions for the listing of committees and displaying single committees. Going by Rails conventions, these should be the index and show actions, respectively.
When routing, you only specify the method name, not the arguments:
match '/boards/:name', to: 'committees#show'
Generally you will declare something with resources or match but not both. To stay REST-ful, this should be the show method. Index is a collection method, usually not taking any sort of record identifier.
Arguments always come in via the params structure:
def show
#posts = Committee.where(name: params[:name])
end
Controller methods that are exposed via routes do not take arguments. You may construct private methods that do take arguments for other purposes.

Get resource name from URL when using a custom controller in Rails

I have a set of routes that are generated dynamically at runtime, but that all point to the same controller i.e.
map.resources :authors, :controller => 'main'
map.resources :books, :controller => 'main'
These all work fine, producing routes like /authors/1, /books, /books/55, etc and then all end up being processed by the 'main' controller.
However, I can't seem to find how to get the name of the resource in the controller i.e. in the index action when the URL is /authors or /books I'd like to be able to determine which resource it is, i.e. Author or Book
I cannot use separate controllers for this.
Is this at all possible ?
EDIT: complete change of answer because it was waaay off.
So because it changes the params that you see in your action you'll have to get at the actual uri. It is really just as simple as what Terry suggested.
def index
if request.request_uri =~ /books/
#...
else
# if it is a author
end
end
This compares the request uri (the part that would be after localhost:3000) to books and so you can see what the user has requested.
I don't think there's anything like a .resource method, but you could look at request.request_uri, which in your case would return things like /authors or /books, and could act accordingly.
See the "Defaults routes and default parameters" section of the ActionController::Routing documentation. You can program into your routes arbitrary extra parameters you would like sent to your controller.
Looking at the request URI will force you to keep routes and controllers in sync, which will make your code more fragile and less easily re-used. Avoid if you possibly can.

Resources