Ruby on Rails Namespacing and Nested Routing - ruby-on-rails

I'm learning Ruby and Rails and trying to figure out the most correct path to go for my scenario regarding possibly namespacing as well as routing.
Say I have a 'Zoo' model as well as 'Species' model. I also need a relationship between them to tell what Species are in what Zoos (a Zoo has many Species, which will also have their own properties such as qty, location, etc)
I would like my routes to be like:
/zoo
/zoo/:id
...
/zoo/:zoo_id/species/
/zoo/:zoo_id/species/:id
...
/species
/species/:id
I have tried messing with modules, namespaces, route scopes, etc. I can't seem to get it how I'd like and think there has to be a better/natural way to do this. The problem is mainly due to the fact that I have a species model as well as a species model of zoo (I've tried namespacing to Zoo and creating a Zoo::Zoo and Zoo::Species classes, creating a ZooSpecies class, and adjusting routes for those scenarios, etc)
From an organizational standpoint, it would be great to be able to have a Zoo class as well as a Zoo namespace, to have Zoo::Species and such, but that is not possible.
What is the proper way to organize something like this?
Update: My current setup...
Zoo Module
Zoo::Zoo Class
Zoo::Species Class
Species Class
Routes:
resources :zoos, path: 'zoos' do
resources :zoo_species, path: 'species', as: 'species', controller: 'zoo/species'
end
And a Zoo controller and a Zoo::Species controller. I feel like I'm getting closer to the desired result, but fighting against the framework more than I think I should. Still running into some issues properly setting up the relationships and paths, having to specify a lot of config values

Try using shallow nesting:
http://edgeguides.rubyonrails.org/routing.html#nested-resources
In your case:
resources :zoos
resources :species, shallow :true

To nest resources:
resources :zoos do # /zoos(/:id)
resources :species # /zoos/:zoo_id/species(/:species_id)
end
To namespace:
namespace :zoo do
resources :posts # /zoo/posts(/:id)
end
If you create that namespace, the you'll need to create app/controllers/zoo/posts_controller.rb with class definition:
class Zoo::PostsController < ApplicationController
# ...
end
Note: nested resources and namespaces are two different things. The Rails guide on routing is very readable - which you should definitely do: http://guides.rubyonrails.org/routing.html

Related

Is there any way to allow several controllers without writing on routes?

I have a question.
I'm new on rails 4 and ruby 2 and trying to find a way to allow controllers without typing several times on ROUTES
I'm tyring to create several actions inside my controller
Here my controller on rails 4:
class Managementbla::UserController < ApplicationController
class Managementpoke::UserController < ApplicationController
...
class Managementmultiple::UserController < ApplicationController
Here my routes on rails 4:
namespace :managementbla do
resources :user
end
namespace :managementpoke do
resources :user
end
...
namespace :managementmultiple do
resources :user
end
on rails 2.3 the sintaxis was
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
On rails 4 i found this:
match ':controller(/:action(/:id(.:format)))', via: [:get, :post]
The fact that you have 500 controllers suggests serious issues with the design of your application. Whatever problem you're solving, I really, really doubt that having 500 similarly-named controllers is the best solution. Do they all perform such vastly different tasks that you need 500 distinct controllers? Forced to guess, I would say that those controllers are all very similar and whatever work they're doing could, and should, be handled by a single controller instead.
If you're determined to continue down the 500-controllers path, though, it's not hard. It seems you may have forgotten that routes.rb is just Ruby code. You can solve this the same way you'd solve any problem that requires iterating over a bunch of numbers in Ruby:
1.upto(500) do |n|
namespace :"management#{n}" do
resources :user
end
end
Don't do this, though. Take a hard look at your 500 controllers. There's got to be a better solution.
It seems like you might simplify the application by using either a route parameter or query string parameter.
If you truly need this many routes in your application, you may consider a framework that does routing more efficiently. You might take a look at Roda: http://roda.jeremyevans.net/why.html.
They claim the routing algorithmic efficiency is on order O(log n) where n is the number of routes.

how to change multiple nested routes in rails

i'm doing a course system, and i would like to simplify the routes.
i routed like this:
resources :courses do
resources :modules do
resources :lesson
end
end
and returned this:
/courses/:course_id/modules/:module_id/lesson/:id
/courses/:course_id/modules/:id
/courses/:id
etc...
i want my routes like that:
/courses/:course_name/:module_name/:lesson_name
/courses/:course_name/:module_name/
/courses/:course_name/
etc...
but how?! :(
in the routes file
get "/courses/:course_name/:module_name/:lesson_name", as: :courses
then you should be able to generate the path:
courses_path(course_name: course.name, module_name: module.name, lesson_name: lesson.name)
But I would recommend against it as:
1) This is fighting conventions: don't expect good support for this, none of the new developers who join the project will like you for this.
2) You will have to make sure all course/module/lesson names are unique and url-friendly
3) You'll have to make sure the names never change, because then the urls would change.
I would advice sticking to the default nested paths and overriding #to_param on every module
to smth like:
def to_param
"#{super}-#{name.downcase.gsub(' ', '-')}" # you need a better regex here
end
so the urls look like
/courses/33-computer-science/modules/23-engineering/lesson/56-design-patterns
More about custom routes: http://guides.rubyonrails.org/routing.html#dynamic-segments

Nested resources or not for categories?

I have an application, built with Rails 3.1.3, that has products and categories. The categories are related to other categories, so a category can be a parent or a child category. The products are then related to a child category.
Now, I'm thinking about how I should define the routes. Is it a good idea to somehow nest the categories and products resources? Ideally, I would like URLs like this:
example.com/parent/child/product-1234
like this:
example.com/clothes/underwear/some-socks-1234
or maybe like this to keep it restful?
example.com/p/clothes/c/underwear/....
But maybe that's a bit messy to achieve with the routes? I would have to nest the category with itself I guess?
Any ideas on how to achieve something like this?
EDIT:
Do I create the category routes like this:
resources :categories, :as => "parent" do
resources :categories, :as => "child"
end
or similar? It's not that important to have the product nested inside the categories. Maybe it will just make it hard to manage...
You can either be RESTful or not, you can't have it both ways.
If Category is a resource, then you can define routes with resource :categories statement and use Rails built-in support for REST. But then all categories should be accessible through categories/id. If you want different categories be accessible through different routes, then you are not RESTful and you can't use Rails REST support.
Alternatively you can say that parent and child categories are different resources and be RESTful again.
Edit: looking at my answer now (after it was accepted) I feel like I was probably too stiff. The answer should probably be "it depends" (as usual). If the application is CRUD (admin), I would stick to plain categories and nested products (shallow). On the storefront where you need nice looking url you can totally have non-REST routes and actions. It just mean more coding.
I would add an extra route besides the RESTful ones to support your URLs. This ignores the categories (they don't even have to exist) and routes directly to the product.
...
resources :products
resources :categories
match ':parent/:child/:product' => "products#show"
...
In your show action you can simply check which of params[:product] and params[:id] that is set and handle the different routes.
params[:parent] and params[:child] will also be available but i would suggest just looking for the product and get the category through that object since that should be more reliable.

Retrieve the controller name from a class name following RoR conventions

I am using Ruby on Rails 3.0.9 and I would like to build a controller name from a class name as well as possible following RoR naming conventions. For example, if I have the Articles::Comment class I would like to retrieve the articles/comments string.
Maybe it exists a RoR method created by developers to handle internally these conventions, but I don't know that.
How can I retrieve the controller name as in the example above?
You're looking for the underscore method. More info here.
"Articles::Comment".underscore
Or, if you've got the controller class itself, it would be like this:
Articles::Comment.name.underscore
EDIT
As of routes, they are built one piece at a time, even when you namespace them. When you do something like this:
map.resources :articles do |articles|
articles.resources :comments
end
What rails is going to do is, first:
"articles". classify # which yields "Article" then rails is going to append "Controller" to it
Then it's going to get "comments" and do the same, but this one is going to be routed under "/articles". Rails does not namespace internal resources, so, the controller has to be CommentsController, not Articles::CommentsController.
Only then you clearly namespace something, Rails is going to namespace your classes:
map.namespace :admin do |admin|
admin.resources :articles # will require controller "Admin::ArticlesController"
end
I hope it's clearer now.

ActiveResource : Polymorphic Routes

Using ActiveResource, Ruby on Rails, Is there a clean way to do the following:
I have house ActiveResource model and chair ActiveResource model, and both can have comments. I want to reuse the comment ActiveResource model for both.
# ActiveResource wraps HTTP POST Requests to the following
# And then parsess the responses, and then creates instances of my ActiveResource models
POST http://3rd.party.restful.api.com/admin/houses/1/comments
POST http://3rd.party.restful.api.com/admin/houses/1/chairs/3/comments
I can only think of the following:
class Comment < ActiveResource::Base
self.site = "http://3rd.party.restful.api.com"
self.prefix = "/admin/:prefix_path/"
end
And then doing the following:
comment = Comment.new(:text => "some text", :prefix_path => "houses/1/chairs/3")
Please stop me from doing this.
I think you're asking if you can define the routes for each while only using one model, right?
There are two options:
First, the simplest way: just define the route twice.
resources :houses do
resources :comments
resources :chairs do
resources :comments
end
end
The routes file won't care that you're telling it the comments model can be reached from two places, and it will work basically as you expect -- views will just live in the 'comments' folder.
A second, more complex way to do it, define a namespace that you want to nest under. Then you'll end up with two controllers: CommentsController and Chairs::CommentsContoller. You might also create two sets of views, but you don't have to (the second controller can simply explicitly render the first controller's views).
For a good explanation of how namespacing would work, you can see the answer where I originally learned about it.
For what it's worth, this second approach is nice, because you can make some minor tweaks in how the model is presented depending on how it's accessed, but you've still only got one model in the DB.
Good luck! I'll be happy to try and answer questions in the comments!

Resources