Ruby on Rails Routes: Namespace with more params - ruby-on-rails

i have a namespace "shop". In that namespace i have a resource "news".
namespace :shop do
resources :news
end
What i now need, is that my "news" route can get a new parameter:
/shop/nike (landing page -> goes to "news#index", :identifier => "nike")
/shop/adidas (landing page -> goes to "news#index", :identifier => "adidas")
/shop/nike/news
/shop/adidas/news
So that i can get the shop and filter my news.
I need a route like:
/shop/:identfier/:controller/:action/:id
I tested many variations but i cant get it running.
Anyone can get me a hint? Thanks.

You can use scope.
scope "/shops/:identifier", :as => "shop" do
resources :news
end
You will get those routes below:
$ rake routes
shop_news_index GET /shops/:identifier/news(.:format) news#index
POST /shops/:identifier/news(.:format) news#create
new_shop_news GET /shops/:identifier/news/new(.:format) news#new
edit_shop_news GET /shops/:identifier/news/:id/edit(.:format) news#edit
shop_news GET /shops/:identifier/news/:id(.:format) news#show
PUT /shops/:identifier/news/:id(.:format) news#update
DELETE /shops/:identifier/news/:id(.:format) news#destroy
http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing

If you have those nike, adidas etc. in the database then the most straightforward option is to use match.
namespace :shop
match "/:shop_name" => "news#index"
match "/:shop_name/news" => "news#news"
end
However it seems to me that shop should be a resource for you. Just create a ShopsController (you don't need a matching model for it, just a controller). Then you can do
resources :shops, :path => "/shop"
resources :news
end
Now you can access the news index page (/shop/adidas) like this:
shop_path("adidas")
In the NewsController use :shop_id to access the name of the shop (yes even though it's _id it can be a string). Depending on your setup you may want news to be a singular resource, or the news method to be a collection method.
Also are you sure just renaming the news resource isn't something you want?
resources :news, :path => "/shop" do
get "news"
end
Keep in mind also that controller names and the number of controllers need not match your models. For example you can have a News model without a NewsController and a ShopsController without a Shop model. You might even consider adding a Shop model to your database if that makes sense.
In case this is not your setup then you might have oversimplified your example and you should provide a more full description of your setup.

Related

Self nesting rails categories

I have an store application, where I need to make custom routing system where URL stores categories for products. For example, http://example.com/languages/ruby/rails will display category#show named 'rails', that has parent named 'ruby', that has parent named 'languages' and and URL of http://example.com/languages/ruby/rails/store will display product in this category.
Currently I have:
category.rb
belongs_to :parent, class_name: 'Category'
has_many :categories, foreign_key: :parent_id
has_many :products
routes.rb
resources :categories, :path => '', :only => [:index, :show] do
resources :products, :path => '', :only => [:show]
end
root :to => 'products#index'
but it still stacks up to 2, e.g. URL http://example.com and http://example.com/languages shows list of categories/subcategories, but http://example.com/languages/ruby have params: {"action"=>"show", "controller"=>"products", "category_id"=>"language", "id"=>"ruby"}
Removing products from routes does not help at all - then it just says that No route matches [GET] "/language/ruby", although I assume It might cause need for extra check if current URL point on category or product later on.
Also I tried get '*categories/:id', to: 'category#show' variations
+ I am using friendly_id gem so that path do not look like http://example.com/2/54/111/6
I just want to find out what is the best ruby on rails solution for this kind of situations, when you need search engine optimizations + endless (e.g. no way to define how deep such recursion can go) nested resources that nest themselves (including fact that category/language/category/ruby/category/rails just looks ugly).
Note: most information I used is taken from Stack Overflow and railscasts.com (including pro/revised episodes), so mentioning a good source with information like this will be great too.
I solved this myself recently with a CMS I built on Rails recently. I basically construct the routes dynamically at runtime from the database records. I wrote this blog post on the strategy:
http://codeconnoisseur.org/ramblings/creating-dynamic-routes-at-runtime-in-rails-4
The core of the solution (adapting the blog post above) is simply iterate over the database records and construct the routes needed for each category. This is the main class for doing that:
class DynamicRouter
def self.load
Website::Application.routes.draw do
Category.all.each do |cat|
get cat.route,
to: "categories#show",
defaults: { id: cat.id },
as: "#{cat.routeable_name}_#{cat.name}"
end
end
end
def self.reload
Website::Application.routes_reloader.reload!
end
end
For the above, the Category model should implement a "routeable_name" method which simply gives an underscored version of the category name that uniquely names that category's route (its not strictly necessary, but helps when doing "rake routes" to see what you have). and the #route method constructs the full route to the category. Notice the defaults which sets the ID param for the category. This makes the controller action a very simple lookup on the category's ID field like so:
class CategoryController < ApplicationController
def show
#category = Category.find(params[:id])
end
end

Rails routes, how to specify this member proper

I currently have some routes that look like the following:
resources :contests do
member do
get :enter
end
end
This allows me to do an HTTP GET on a URL that looks like /contests/5/enter. Now, a user can go in, fill in some forms, and be able to submit an entry to the contest. So I'd also like to be able to POST to this URL. I tried doing the following:
resources :contests do
member do
get :enter
post :enter
end
end
This posts to the same controller#action as the GET member that I have specified, so it's not really intuitive. I'd like to be able to direct it to a separate action if at all possible. What's the best way of doing that? I am using Ruby on Rails 4 beta currently.
** UPDATE **
I tried the following but I get an ArgumentError exception when I start the server:
resources :contests do
member do
get :enter
post :enter => "contests#create_entry"
end
end
You can do something like this:
resources :contests do
member do
get :enter
post '/enter', to: "contests#create_entry", as: "create_entry"
end
end
However i agree with Ola Tuvesson, you should definitely create a new controller and routes for entries, even though you may not have a model, similiar to how you often have a session controller for login and logout. Something like this:
resources :contests do
resources :entries, only: [:new, :create]
end
You can specify the controller/action you want to point a route at.
get :enter => "controller#get_enter"
post :enter => "controller#post_enter"
I would suggest you make the entries for a contest a child model of contests. That would give you all the CRUD methods on entries and your routes would simply be:
resources :contests do
resources :entries
end
A GET of /contests/5/entries/new will give you the form for adding an entry and when this POSTs to /contests/5/entries it would create a new entry. It also makes it easy to list all entries for a competition etc. You can easily create the controller, model and the associated views with the scaffold generator, for example:
rails g scaffold Entry contest:references name:string email:string
The references column type tells the generator to link Contests to Entries in a one to many relationship. Job done.
Edit:
If you still want to rename your routes, here's how:
http://guides.rubyonrails.org/routing.html#overriding-the-named-helpers
HTH

Rails 3: Getting the ID of a Namespaced Model

I have a model called Contributor, which also acts as a namespace for several other models, such as Contributor::Alias and Contributor::Reassignment. I want to use a URL that includes the Contributor ID like so:
/contributors/1/reassignments/new
But I get this error:
No route matches [GET] "/contributors/1/reassignments/new"
My routes.rb file includes:
namespace :contributor do
resources :reassignments
end
resources :contributors
I've also tried:
resources :contributors do
resources :reassignments
end
This results in a different error:
uninitialized constant ReassignmentsController
Any idea how to approach this? Perhaps I shouldn't use a namespace that also acts as a model? I haven't seen this done in any tutorials, though it seems like it could be possible.
UPDATE:
How do you handle a deeply nested namespace model, such as:
resources :contributors do
resources :reassignments, :module => "contributor" do
resources :approvals, :module => "reassignment"
end
end
Using this approach, I get the error:
No route matches {:action=>"create", :controller=>"contributor/reassignment/approvals"}
My controller directory does have the following structure:
contributor ->
reassignment ->
approvals_controller.rb
This seems related to the first error, but perhaps it's something new.
It's not clear if you have a Contributor resource or not. If you do, the following in your routes.rb:
resources :contributors do
resources :reassignments, :module => "contributor"
end
If not, try:
resources :reassignments, :module => "contributor", :path => "/contributors/:contributor_id/reassignments"
Just note that in the 2nd case you will need to construct an url and explicitly pass :contributor_id to it in calls to link_to, form_for, and similar places.
If you want to use [#contributor,#reassignment] format there you better stick to the 1st approach where yu do have a Contributor resource.
UPDATE: for three-level nesting if your controllers directories don't also nest in parallel with resources you could specify controllers explicitly, e.g.:
resources :contributors do
resources :reassignments, :controller => "contributor/reassignments" do
resources :approvals, :controller => "reassignment/approvals"
end
end
But, please, don't do that. 3-and-more-level nesting is actively discouraged in Rails. See what is recommended instead here: http://weblog.jamisbuck.org/2007/2/5/nesting-resources

Rails 3 routes to wrong controller

I wanted to create a new action and I call it "showemployees". That's what I did already:
-> in the controller:
def showemployees
end
-> creating app/views/employees/showemployees.html.erb
-> in config/routes
match "/employees/showemployees" => "employees#showemployees"
I thought this is enough for opening the showemployees.html.erb now via localhost:3000/employees/showemployees , but it seems like Rails still routes through the show action (from resources :employees) and doesn't take the showemployees-action, because it tells me
ActiveRecord::RecordNotFound in EmployeesController#show
Couldn't find Employee with ID=showemployees
What do I need to change so Rails takes the showemployees-action?
the source code of my route:
System::Application.routes.draw do
match "/employees/showemployees" => "employees#showemployees" #für showemployees.html.erb
root :to => "employees#index"
resources :course_to_dos
resources :current_qualifications
resources :expected_qualifications
resources :skills
resources :employees
resources :positions
resources :admin
end
try to walk by Rails-way, if you want to get collection, use the collection
resources :employees do
collection do
get :showemployees
end
end
If you post your full routes file we can make a definitive call, but based on the error message, it looks like you have a broader route definition mapping to employees#show defined above this route in such a way that it is getting matched.
Routes are evaluated in the order they are defined, so if you have a very broad route pattern defined above a narrow one, your narrow route will never be called.
edit: you'll want to take the forward slash out of your route and add the showemployees to the actual URL, so that it reads
match "employees/showemployees" => "employees#showemployees"

How to map a new CRUD action in 'routes.rb'?

In my 'routes.rb' file I have this code:
resources :users
that maps my user's controller like this.
If I want to map the "reset" view/url for users (Path: /users/reset) what code I have to insert in the 'routes.rb' file?
Two options - I'm assuming you're just going to act on the session user so you don't need to pass in an id to operate on? If so, you'll need to make a few additional changes...
Use an explicit route:
match "/users/reset" => 'users#reset', :as => 'reset_user'
The 'as' part is optional.
Add a new route that operations on a 'collection'. This gets you your route but feels like a hack, I wouldn't recommend it.
resources :users do
collection do
get 'reset'
end
end
Do this:
resources :user do
member do
get 'reset'
end
end
See this section in the Rails Guide you referred to.

Resources