Mask URL path on Ruby on Rails - ruby-on-rails

I'm trying to mask the following Route
get '/products/1' => 'products#show', :as => "map"
as hotels/2/map inside my Application, but I'm having no luck. I'm not sure what I'm doing wrong here, as this is already nested inside the Hotels resource. I'm automatically creating two products when the hotel is created, so 1 will be the Map and 2 will be the App.
resources :hotels do
resources :contacts
resources :products
get '/products/1' => 'products#show', :as => "map"
end

What you've got will create hotel_map_path at /hotels/:hotel_id/products/1(.:format). If you instead want map_path without the hotel_prefix, you need to move the map route out of the hotel namespace, like so:
resources :hotels do
resources :contacts
resources :products
end
get 'hotels/:hotel_id/products/1' => 'hotels/products#show', :as => "map"
Hope that helps
edit
I'm gonna revise my suggestion. What you're trying to do will create a dependency between the routes and the details of db storage (the id), which feels wrong. Instead, I'd suggest pushing that dependency into the active_record model, which is the only class that should be responsible for knowing about how Hotels and Products are persisted.
So, your route would be something like:
resources :hotels do
member do
get :map
end
resources :contacts
resources :products
end
which should give you
/hotels/:id/map
in the hotels_controller you'll have
def map
#hotel = Hotel.find(params[:id])
#map = #hotel.location_map
end
and in the Hotel model:
def map
products.location_map
end
and in the Product model:
def location_map
...
end
or add a scope instead.
It's more lines to get the same routing effect, but less brittle.

Related

How to construct the following JSONAPI url in Rails?

Im following http://jsonapi.org/format/#document-top-level
I notice the following end-point (route):
/articles/1/relationships/author
In Rails, how would this route be contstructed in routes.rb?
resources :articles do
# What goes here?
# Should relationship be a namespace or other?
# I guess author can be defined as a collection, or just a simple get
end
relationships doesn't need to have any of the 7 RESTFUL actions.
The route should be like following code snippets:
resources :articles do
resources :relationships, :only => [] do
collection do
get :author
end
end
end
This is how the route file should look like. Please let me know if any update needed.
Bouncing on my idea of concerns, as you probably will have to use this in several resources (the rails doc is quite well-written)
concern :has_author do
scope '/relationships' do
resource :author
end
end
resources :articles, concerns :has_author
Which is equivalent to
resources :articles do
scope :'/relationships' do
resource :author
end
end
The scope :'/relationships' didn't work for me (At least in Rails 5).
I got it working with this:
resources :articles do
resources :author, path: 'relationships/author'
end
This will just use the AuthorController and map it to /articles/:id/relationships/author.

Scoping resource routes in Rails 4

I have a resources :shops
which results in a /shops, /shops/:id, etc
I know I can scope collection or members with
resources :shops do
scope ":city" do
# collection and members
end
end
or do it before with
scope ":city" do
resources :shops
end
But I can't figure out how to make the route be on all members (including the standard REST ones) and collection, like so
/shops/:city/
/shops/:city/:id
As per your use case and question, you are trying to have logically wrong routes. You have shops within the city, NOT city within the shop.
Firstly you should normalize your database. You should create another table cities and replace your city attributes with city_id in shops table.
You need has_many and belongs_to association between cities and shops.
# Models
class City < ActiveRecord::Base
has_many :shops
... # other stuff
end
class Shop < ActiveRecord::Base
belongs_to :city
... # other stuff
end
Routes
resources :cities do
resources :shops
end
It will generate routes like:
POST /cities/:city_id/shops(.:format) shops#create
new_city_shop GET /cities/:city_id/shops/new(.:format) shops#new
edit_city_shop GET /cities/:city_id/shops/:id/edit(.:format) shops#edit
city_shop GET /cities/:city_id/shops/:id(.:format) shops#show
PATCH /cities/:city_id/shops/:id(.:format) shops#update
PUT /cities/:city_id/shops/:id(.:format) shops#update
DELETE /cities/:city_id/shops/:id(.:format) shops#destroy
Logically, these routes will show that in which city the particular shop exists.
Namespace
You may wish to consider including a namespace
Since you're trying to pull up the cities for shops (IE I imagine you want to show shops in Sao Paulo), you'd be able to do this:
#config/routes.rb
namespace :shops do
resources :cities, path: "", as: :city, only: [:index] do #-> domain.com/shops/:id/
resources :shops, path: "", only: [:show] #-> domain.com/shops/:city_id/:id
end
end
This will allow you to create a separate controller:
#app/controllers/shops/cities_controller.rb
Class Shops::CitiesController < ApplicationController
def index
#city = City.find params[:id]
end
end
#app/controllers/shops/shops_controller.rb
Class Shops::ShopsController < ApplicationController
def show
#city = City.find params[:city_id]
#shop = #city.shops.find params[:id]
end
end
This will ensure you're able to create the routing structure you need. The namespace does two important things -
Ensures you have the correct routing structure
Separates your controllers

Rails 3: Different namespace inside nested resource?

Is there a way to use a (different) namespaces inside nested resources? For example,
I have:
resources :users do
resources :tags
end
and I'd like to place the tags controller inside controllers/common, while placing users controller inside controllers/user, with the equivalent for templates.
If I try this:
namespace :user do
resources :users do
namespace :common do
resources :tags
end
end
end
I'll get redundant route names:
user_common_tags, etc. But I want something like common_tags
This way you will have common_tags, and users_tags, both linking to the same controller.
resources :users do
resources :tags
end
namespace :common do
resources :tags
end

Can't add sub pages in a controller

Using RoR 2.3.8
This is my cities_controller.rb
class CitiesController < ApplicationController
def show
#city = City.find(params[:id])
...
end
def shops
...
end
def countries
...
end
end
Here's my routes.rb
map.resources :cities, :collection => {:shops => :get, :countries => :get}
The show url for each id is:
http://localhost/cities/1
I want to have some contents shops and countries for each associated id, which I want:
http://localhost/cities/1/shops
http://localhost/cities/1/countries
I can't get the pages showed in empty code in the first place. What have I done wrong?
Thanks.
The :collection option is for when you want to act on the whole collection - so your routes will show up as:
http://localhost/cities/shops
http://localhost/cities/countries
What you want is
map.resources :cities, :member => {:shops => :get, :countries => :get}
Reference: http://apidock.com/rails/ActionController/Resources/resources
Shops and Countries would probably not be methods in the controller but other models. you would want a Countries.rb and Shops.rb
You would then nest the resources like
resources :cities do
resources :shops
end
and you would need a belongs_to :city in the shops model and a has_many :shops in the cities model which would let you access cities/1/shops.... or something like that
However think about the way the data is structured, do countries really belong to cities (which nesting the resources would imply) or would countries contain cities. You would want cities belongs_to :country and so on...
This help?

Is it possible to add more routes when I already did resources :posts

In my routes i have:
resources :posts
now say I want to create a new action/view, is it possible to add it inside a block like:
resources :posts do
// new routes other than the show/new/create/delete/update that REST gives me.
end
Yep
resources :post do
get :action, :on => :member
get 'other', :on => :collection
post :change, :on => :member
resources :another_model
end
note: these are just samples of what you can do and this does assume Rails 3. For more information you should read the Ruby on Rails Guide: Rails Routing from the Outside In
Yes, just add them in. For example, if you want to be able to post to "new_action" you would do
resources :posts do
post "new_action"
end
Have you looked at the Rails Guide: Rails Routing from the Outside In? Section 2.9 Adding More RESTful Actions describes exactly what you're looking for. I frequently refer to the other guides there too.
You can do this for a resource member
resources :posts do
get :preview, :on => member
end
or this for a resource collection
resources :posts do
get :active, :on => collection
end
When I was starting out with rails I was confused about what members and collections were. For a Post resource a member is an individual post while a collection is the collection of all posts. For example...
# url for a member
/posts/5/preview
# url for a collection
/posts/active
The rails guide goes into more detail on routing

Resources