Can't add sub pages in a controller - ruby-on-rails

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?

Related

How to Create Basket of Shop site model in rails 4?

My project is about an online shopping site, using Ruby on Rails to buy phones.
My Database is User, Product, Phone.
I'm trying to create Basket model.
My route:
resources :products do
resources :phone do
resources :baskets
end
end
And my Code is:
class User < ActiveRecord::Base
has_many :baskets
end
class Phone < ActiveRecord::Base
belongs_to :product
has_many :baskets
end
class Basket < ActiveRecord::Base
belongs_to :user
belongs_to :phone
end
When i in the Show action of Product,it Show name of Product and index Phones in this Product,i want to add 1 Phone to Basket,the error is :
No route matches {:action=>"new", :controller=>"baskets", :id=>"38", :product_id=>"30"} missing required keys: [:phone_id]
I think the problem is :
http://localhost:3000/products/30/phone/38
It's Product_id = 30,but not Phone_id = 30,in here just is Id = 30.
Someone could help me fix it !
resources :products do
resources :phone do
resources :baskets
end
end
means you have to have route like this:
/products/:product_id/phones/:phone_id/baskets/:basket_id(.:format)
Which means, that in link_to you should pass the phone_id as well:
link_to 'show basket' product_phone_basket_path(product_id: #product.id, phone_id: #phone.id, basket_id: #basket.id)
link_to 'New basket' new_product_phone_basket_path(product_id: #product.id, phone_id: #phone.id)
Regardless of whether you got it working (I upvoted #Andrey's answer), you'll want to consult your routing structure.
Resources should never be nested more than 1 level deep. docs
--
In your case, I am curious as to why you have phones nested inside products. Surely a phone is a product?
Further, why are you including resources :baskets? Surely the basket functionality has nothing to do with whether you're adding a product, phone, or anything else?
I would personally do the following:
resources :products, only: [:index, :show] do
resources :basket, path:"", module: :products, only: [:create, :destroy] #-> url.com/products/:product_id/
end
#app/controllers/products/basket_controller.rb
class Products::BasketController < ApplicationController
before_action :set_product
def create
# add to cart
end
def destroy
# remove from cart
end
private
def set_product
#product = Product.find params[:product_id]
end
end
I've implemented a cart (based on sessions) before (here).
I can give you the code if you want; I won't put it here unless you want it. It's based on this Railscast.

Mask URL path on 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.

How to create nested routes with default parent in rails?

I have the following models:
class Article < ActiveRecord::Base
belongs_to :category
end
class Category < ActiveRecord::Base
end
Articles are nested under categories like this:
resources :categories, only: [] do
resources :articles, only: [:show]
end
which gives me the following route
GET /categories/:category_id/articles/:id articles#show
To use this in my views do i need to write
link_to category_article_path(article.category, article)
# => "/categories/1/articles/1
Their isn't any article_path, all articles will always be nested under the associated category. However i was wondering if their is a way for me to DRY this up so i could simply write
link_to article_path(article)
# => "/categories/1/articles/1
and still have the route nested, under category.
I know that i could just write a helper or something similiar, but i would like a more general solution, because i know that i would get more nested resources under categories at some point (like blogs, videos, etc.) that all has a belongs_to relation to the categories.
So basically am I looking for something like this
# Example, doesn't work (article_id is not available here):
resources :categories, only: [] do
resources :articles, only: [:show], defaults: { category_id: Article.find(article_id).category.id }
end
However that doesn't work, as article_id isn't available in the defaults block. As far as i know, can you only create static default values, and i need a dynamic one based on the current article.
You can achieve what you want by aliasing your resource like you would do in a normal route.
resources :categories, only: [] do
resources :articles, only: [:show], defaults: { category_id:Article.find(article_id).category.id }, as: 'article'
end
Now you can use article_path(article) instead of category_article_path

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

Nested Routes with has_one in rails

G'day guys,
Having a bit of an issue with Rails routes, at the moment.
Have a top resource: /Customer/ which itself has only one /Quote/ resource
Quotes can have both first_resources and second_resources
which are collections of resources associated with the quotes
Building the route, though how do I nest multiple routes under a has_one route?
map.resources :customer, :has_one => :quote
how do I do?
quote.resources :first_resources
quote.resources :second_resources
by mapping them as sub-elements to the substructure?
Or would it be easier to manage the collection in a different way?
for this, I would nest inside of a block:
map.resources :customers do |customer|
customer.resource :quote do |quote|
quote.resources :first_resources
quote.resources :second_resources
end
end
alternative syntax:
map.resources :customers do |customer|
customer.resource :quote, :has_many => [:first_resources, :second_resources]
end
This would give you urls of
customers/:customer_id/quote/first_resources/:id
customers/:customer_id/quote
customers/:id
Or the way you provided I believe you would need to map plural quotes in order to be able to get to a specific quote if you don't want to nest
map.resources :customers, :has_one => :quote
map.resources :quotes, :has_many => [:first_resources, :second_resources]
that would give you urls of
customers/:customer_id/quote
customers/:id
quotes/:quote_id/first_resources/:id
I think the first one is what you are after. Hope this helps.
Resources: http://api.rubyonrails.org/classes/ActionController/Resources.html
map.resources :customers, :has_one => :quote
map.resource :quote, :has_many => :first_resources
map.resource :quote, :has_many => :second_resouces

Resources