Named route in Rails 4 - ruby-on-rails

I am playing with Rails 4 in a test application. I have an arbitrary URL that isn't standard like a resources :foo type URL. Ideally the end result I'd like is to be able to go to:
/contests/:id/enter
In views, it would be great if I can then set a link using a named helper such as:
edit_contests_enter(:id)?
What would be the best way to define the route above so I can use the helper path with an arbitrary URL like the one above? It doesn't necessarily have to be edit_contests_enter(:id) but as long as the helper path leads to the URL as suggested above, that would be fantastic.

I assume that your contest is a resource, and when your visitor goes to /contests/:id/enter you want them to create an object user <=> contest. Let's call it participation.
Now participation is exactly like any other resource in your Rails app, so you'd have a routes.rb file looking like
resources :contests do
resources :participations
end
You don't want people to do anything other than create a participation, like edit or destroy them. And perhaps you want a nice URI like /contests/:id/enter. All you have to do is
resources :contests do
resources :participations, :only => [:new, :create]
get "enter" => "participations#new"
end
Doing such will give you a routes helper named contest_enter. In your participations#new form, you'll POST as usual to /contests/:id/participations.

If you have a resources block for :contests, you could just define a new "member" route on the ContestsController using:
resources :contests do
member do
get :enter
end
end
And that would automatically generate you a named member route, the name of which you could find by running rake routes.

Related

ruby on rails: path and URL helpers don't work with singular names

I'm new to Ruby on Rails, I'm trying to create my first application. I'm using resourceful routes for instance 'subjects':
resource :subjects do
member do
get :delete
end
end
I've created controller SubjectsController and views for its methods. In index view I'm trying to create a link to show view like that:
link_to("Show", subject_path(1))
but it returns the error:
undefined method `subject_path' for #<#<Class:0x007fc820551488>:0x007fc8229468c0>
Did you mean? subjects_path
The same thing with helper 'new_subject_path'. But what weird new_subjects_path doesn't give any error and creates a proper link. What's wrong with my app? Should I use plurals for path helpers???
I believe your problem is because you are using the resource singular, when you mean to use the resources plural version. Try changing your routes to:
resources :subjects do
member do
get :delete
end
end
And you should find you get the expected rails url helper methods.
The convention is when you expect to have several items of the resources existing, to use the plural versions resources :subjects, but when you expect only a single entity to exist use the singular resource :subject which will create different routes and helpers that don't require an id param.
If you want singular resource, change :subjects to :subject.
resource :subject do
member do
get :delete
end
end
Or use full resources instead of resource:
resources :subjects do
member do
get :delete
end
end
With either change, you'll be able to use subject_path helper. But with the first one, you cannot pass in a ID. If you need that, use the second one.

How to change default path/url in routes in Rails 4

I'm working on a simple reservation application. Here are my routes
resources :users do
get 'reservations', on: :member
end
resources :listings do
resources :reservations
end
When I try to make a reservation, action reservations#new takes me to reservations_path . Of course I'm getting error as this path doesn't exist. I'd like action new to take me to listing_reservations_path instead. I was hopping it will be done automatically since resources :reservations is in nested resources. I read about routes and tried many things but can't find any working way of doing it. Is it possible?
You seem to be unclear on the nature of routes. The action reservations#new exists independently from any route. A route is just a way to map a URL path to a controller and action. If you are trying to do something like:
redirect_to controller: :resources, action: :new
You will have trouble, as all of your routes require some context. Instead, you need to provide whatever the URL helper you're using with a context:
redirect_to listing_reservations_path(#listing)
link_to "New Reservation", new_listing_reservation_path(#listing)
link_to "Reservation", [#listing, #reservation]

Dynamic named routes in Rails

I have a simple problem where in a routes/url name is determined by a user role. Currently the route displayed is /new_admin/dispensaries. If the user has a role of either manager or executive then the named route should be '/dashboards/dispensaries'.
It's kind of simple but the hard part is that in my routes.rb:
namespace :new_admin do
resources :vendor_templates
resources :markdown_docs
resources :email_lists
namespace :moderation do
resources :reported_reviews
end
resources :users do
member do
get :user_bans
post :ban_unban, to: 'user_bans#create'
delete :ban_unban, to: 'user_bans#destroy'
end
end
# TODO - this should be written generically to support dispensary/doctors/whatever
get '/dispensaries/reviews', :to => "reviews#all", :as => :all_reviews
get '/dispensaries/pictures', :to => "pictures#all", :as => :all_pictures
get '/dispensaries/videos', :to => "videos#all", :as => :all_videos
get "/dispensaries/autocomplete", to: "dispensaries#autocomplete"
resources :vendors do
resources :ownership_transfers, only: [:new, :create]
end
...
I'm kind of stuck since if I change the new_admin routes, so many other routes will be affected. Any idea guys?
We've actually done something like this. It's not pretty, but this solution worked for us:
Slugs
You're basically alluding to a type of your routes called Slugs. This is where you use a name instead of an ID, allowing you to make a user-friendly route (such as /delivery/today). The problem is that in order to create these routes, you have to define them individually in the routes file
There are two Gems you can use to handle your slugged routes -- FriendlyID & Slugalicious. Both of these allow you to create slugged routes, but FriendlyID basically just changes the ID, whilst Slugalicious is a totally independent system
We used Slugalicious for the code below, however, you'll probably want FriendlyID (there's a RailsCast for it here):
Routing
The problem you have is that routes are outside the scope of the RESTful controller interface, which means you'll have to call all the routes exclusive of your resources references in the routes.rb file
If you use Slugalicious, it has its own Slugs database, which means we can use it to create the routes on the fly, like this:
#Slugs
begin
Slug.all.each do |s|
begin
get "#{s.slug}" => "#{s.sluggable_type.downcase.pluralize}#show", :id => s.slug
rescue
end
end
rescue
end
This is live code, and outputs all the slugs in the routes file dynamically. The way we managed to get this to update programmatically was to use an Observer Class like this:
class SlugObserver < ActiveRecord::Observer
def after_save(slug)
Rails.application.reload_routes!
end
def after_destroy(slug)
Rails.application.reload_routes!
end
end
I appreciate you may have your answer already, but as you're a beginner, I felt I could help out by explaining the slug stuff for you

How to force custom Rails route to use :id instead of :<model>_id

I defined the following custom Rails route in routes.rb:
resources :projects do
get 'members'
end
This results in the following route (output from rake routes):
project_members GET /projects/:project_id/members(.:format)
What I would like, though, is for the route to map to this instead (change :project_id to :id)
project_members GET /projects/:id/members(.:format)
How can I make that happen?
Is members a nested resource? If so define it as one, and understand that this is why you have :project_id in the route, because :id in a nested resource is used by the final child item - you can't have multiple nested resources all using the same variable to define their id.
resources :projects do
resources :members
end
Add in a third level of nesting and it becomes a bit clearer to explain:
resources :projects do
resources :members do
resources :colours
end
end
With this nesting you could visit app/projects/:project_id/members/:member_id/colours/:id which would be served by the colours controller, which knows that :id defines an instance of that controllers model, and any other named id's belong to other resources.
Otherwise I think you just need to define it as a member method:
resources :projects do
member do
get 'members'
end
end
This tells the route that the action members is a non-resource action belonging to an instance of project, which I think should sort you out, but be sure it's the right thing to do.
See section 2.10 of Rails Routing from the Outside In

what is the proper convention for restful routing via namespaces?

Let's say I have a receipts model, and I want to offer a controller action to print one... The un-restful way would be to do:
# receipt_controller.rb
def print
...
end
#routes.rb
resources :receipts do
get :print, :on => :member
end
... The restful way would be:
# receipt_printings_controller.rb
def create
...
end
#routes.rb
resources :receipts
resources :receipt_printings, :only => :create
My question is..... Let's say I wanted to have the following structure:
/app
/controllers
receipts_controller.rb
/receipt
printings_controller.rb
That would mean my class would look like:
class Receipt::PrintingsController < ActiveRecord::Base
def create
...
end
end
But I don't know how to properly route in this context because I still need to be able to do:
receipt_printings_path(123) to get /receipts/123/printings
The only way I know how to accomplish this is to do:
#routes.rb
match "/receipts/:id/printings" => "receipt/printings#create", :as => :receipt_printings
resources :receipts
But, I am wondering if there is a better way?
I think you can do something like this:
resources :receipts do
resources :printings, :controller => "receipt/printings", :only => :create
end
It will generate :
receipt_printings POST /receipts/:receipt_id/printings(.:format) receipt/printings#create
Then to access to your route :
receipt_printings_path(:receipt_id => #receipt.id)
I hope it helps
If i'm right, you need a nested resource, have look in this rails guide
You can use nest routes, but the way I read your question it sounds to me like you want namespaces. Namespaces might look like the following:
resources :receipts
namespace :receipts do
resources :printings
end
This would route /receipts/printings/:id to app/receipt/printings_controller.rb with an id for the printing (not the receipt).
You might really want nested routes. If you want to use the receipt id, and have only one print action (per receipt), you could use a singular resource.
resources :receipts do
resource :printing
end
This will route /receipts/:id/print to app/printings_controller.rb as show.
To organize the printings controller in a namespace, I would leave it out of the routes, because that will try to insert another receipts namespace in the URL. Instead, use,
resources :receipts do
resource :printing, :controller => "receipt/printings"
end
This is how to be RESTful. However, you might not have a RESTful case. Is printing really doing a create? Is it really doing a show or update? If it's a service which doesn't fit into a CRUD operation, then it's time to deviate from the golden path, and go ahead and use a non-RESTful verb.

Resources