Rails 3 route scoping and/or nesting - ruby-on-rails-3

I am having trouble scoping routes that I don't want to nest.
I have the following routes:
resources :foos
resources :bars
resources :bazs do
resources :hellos
resources :worlds
end
The foo, bar, and baz models all belong_to a user model. I don't want to nest another layer, but I do want to have a prefix in my url that corresponds to a user's permalink attribute (similar to each github repo prefixed by a username). So I have a before filter on all of my controllers
def get_scope
#user = User.find_by_permalink(params[:permalink])
end
Modified to_param thanks to #cowboycoded
class User < ActiveRecord::Base
def to_param
permalink
end
end
I wrapped those routes with
scope ":permalink", :as => :user do
#nested routes here
end
Now everything works fine as long as I pass #user to every non-index route. It doesn't seem very dry to have to go back to all of my views and replace (#foo) with (#user, #foo) when it is already scoped.
Unless I am mistaken, the to_param method simply replaces :id so that urls such as /users/:id appear as users/permalink instead of users/1. I attempted to use this :id in my scope, but it conflicts with foo's :id param and breaks everything. Maybe there is a connection to paths that I am missing?
Thanks for any suggestions that you may have!

Have you tried using the to_param method in your model? This will allow you to override the default and use something other than id, and will work with the URL helpers
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-i-to_param
Example from documentation:
class User < ActiveRecord::Base
def to_param # overridden
name
end
end
user = User.find_by_name('Phusion')
user_path(user) # => "/users/Phusion"
I'm not sure how well this plays with scope, since I haven't tried it, but I guess its worth a shot.

Related

how can I make a simple route on rails and can I use it for an ajax form?

Ive been trying to create a simple route on rails, following this instructions
http://guides.rubyonrails.org/routing.html
my problem is that when I want to enter to my method I get a weird error.
I have a controler user and on my routes I wrote something like this
resources :users do
match "/custom/" => "user#custom"
end
So, at my controller I add this code
def custom
#user = User.find(params[:user_id])
end
but when I try to enter doing localhost:3000/users/1/custom I get an error like
uninitialized constant UserController
doing rake routes I can see
user_custom /users/:user_id/custom(.:format) user#custom
Any idea how to solve this problem?
I want this route to submit a form... is it possible to use this route (if i make it run) for use ajax? I want to submit a form.
Thanks
Change your route to:
resources :users do
match "/custom/" => "users#custom"
end
You should avoid the use of match though, since it will be deprecated in Rails 4. Try this instead
resources :users do
get :custom, on: :member
end
get is the verb, :custom the route and on: :member means that you are looking for a /users/:id/custom route instead of a /users/custom one. If you are looking for the latter, do this:
resources :users do
get :custom, on: :collection
end
Another way to do it is like this, which I prefer:
resources :users do
get 'custom', on: :collection
end
That gives you a route of /users/custom. If you were do use on: :member, then it would give you a route of /users/:id/custom.
You can also use a block for defining multiple custom actions for collections or members.
For example:
resources :users do
collection do
get 'custom'
post 'some_other_method'
end
member do
get 'some_action'
end
end

Rails nested resources and routing - how to break up controllers?

I have the following models:
Post
Tag
TaggedPost (from which Post and Tag derive their associations by has_many :through)
And I have the following routes.rb file:
resources :tags
resources :posts do
resources :tags
end
So when I navigate to, say, /posts/4/tags, that will shoot me into the index action for the Tag controller with the post_id value set in the parameters array. Cool.
My question is though, now that I'm accessing the nested tags resource under posts, should I be hitting the Tags controller still? Or should I setup some other controller to handle the nested nature of tags at this point? Otherwise I have to build additional logic into the Tags controller. This can be done of course, but is this the common way of handling nested routes and resources? The code I have in the index action for the Tags controller is as follows:
TagsController.rb
def index
if params[:post_id] && #post = Post.find_by_id(params[:post_id])
#tags = Post.find_by_id(params[:post_id]).tags
else
#tags = Tag.order(:name)
end
respond_to do |format|
format.html
format.json {render json: #tags.tokens(params[:q]) }
end
end
I can see the code in this controller growing increasingly large, as I plan for many additional resources to be associated with tag resources. Thoughts on how to break this out?
Summary of questions:
If a resource is nested, should the nested resource be going through a different controller representing the nested nature of the resource? This is opposed to going through the normal controller as I am in the code example that I provided.
If so, how should these controllers be named and setup?
Let me know if you need more information.
I think the best solution is to split up controllers:
resources :tags
resources :posts do
resources :tags, controller: 'post_tags'
end
And then you have 3 controllers. Optionally, you can inherit
PostTagsController from TagsController to do something like:
class PostTagsController < TagsController
def index
#tags = Post.find(params[:post_id]).tags
super
end
end
If the difference is only the retrieval of tags, you can:
class TagsController < ApplicationController
def tags
Tag.all
end
def tag
tags.find params[:id]
end
def index
#tags = tags
# ...
end
# ...
end
class PostTagsController < TagsController
def tags
Product.find(params[:product_id]).tags
end
end
Use that methods and simply override tags in the inheriting controllers ;)
All you are doing with nested resources is changing the routing URL. Only thing you would have to do is make sure you are passing the proper id (in your case post)to the tag controller. Most common error is the Can't Find *** ID.
If you don't nest a profile route into a user route it would look like this
domain.com/user/1
domain.com/profile/2
When you nest the routes it would be
domain.com/user/1/profile/2
That is all that it is doing and nothing else. You don't need additional controllers. Doing nested routing is just for looks. allowing your user to follow the association. The most important thing about nesting routes is that you make sure you make the link_to's to the right path.
When not nested: it would be
user_path
and
profile_path
when it is nested you would need to use
user_profile_path
rake routes is your friend to find out how the routes have changed.
Hope it helps.

Passing :new to Rails url_for

Maybe I'm stupid but Rails provides this nifty syntax for generating URL's like so:
url_for([user, comment]) # => /users/1/comment/1
Passing :edit allows me to create something like this:
url_for([:edit, user, comment]) # => /users/1/comment/1/edit
but is there some way to do following?
url_for([:new, user, comments]) # => NoMethodError: undefined method `new_user_comments_url'
UPDATE: Added more information.
My routes.rb:
resources :users do
resources :comments
end
resources :posts do
resources :comments
end
My problem here is, that I can't use Rails auto-generated url helper (user_comments_url), because I'm sharing the views for both user comments and post comments.
There are two workarounds (but no one feels like the "Rails"-way) for my problem:
Adding logic to the view, e.g. some if conditions.
Defining my own url helpers like new_parent_comment(user_or_blog).
Ok, found a solution, but I'm not sure if this is the intended one:
url_for([:new, user, :comment]) # => '/users/1/comments/new'
url_for([:new, post, :comment]) # => '/posts/1/comments/new'
Stuck with the same problem, and found next solution (tested on Rails 5.2):
url_for([user, Comment, action: :new])
where Comment model class name.
By the way, action also could be :edit.
According to the Rails Docs url_for uses the class name of the object passed to generate the RESTful route. It also states that with nested routes it can not make this assumption correctly:
If you have a nested route, such as admin_workshop_path you’ll have to call that explicitly (it’s impossible for url_for to guess that route).
I would suggest using a named route here something like new_user_comment_path(). I am assuming you have set up your routes.rb something like:
resources :users do
resources :comments do
end
end
Additionally you can run rake routes to print out the proper names for all your routes.
Hope this helps,
/Salernost
Could this simply be a typo? I think the last line should read comment, not comments:
url_for([:new, user, comment])
(Assuming your comment variable has been defined.)

REST Routes and overriding :id with to_param

My controller is using the default RESTful routes for creating, adding, editing etc
I want to change the default :id to use :guuid. So what I did was:
# routes.rb
resources :posts
# Post Model
class Post < ActiveRecord::Base
def to_param # overridden
guuid
end
end
This works but my modifed REST controller code has something like this
def show
#post = Post.find_by_guuid(params[:id])
#title = "Review"
respond_to do |format|
format.html # show.html.erb
end
end
When I see this this code ..
Post.find_by_guuid(params[:id])
it would seem wrong but it works.
I don't understand why I can't write it out like this:
Post.find_by_guuid(params[:guuid])
Why do I still have to pass in the params[:id] when I'm not using it?
Looking for feedback on whether my approach is correct or anything else to consider.
Even though it works it doesn't always mean it's right.
Type rake routes in your console, and check the output of the routes. You'll see the fragment ':id' in some of them, that's where the params[:id] comes from. It's a rails convention : when you use resources in your routes, the parameter is named id. I don't know if you can change it (while keeping resources; otherwise you could just go with matching rules), but you shouldn't anyway : even if it seems not very logic, it actually has sense, once your understand how rails routing works.

Change devise route from ID to Username

I have tried using the post to change the routes to point at the username. I know this is simple, but for some reason I can't compute it at the moment. I have tried everything on the devise documentation page as well.
Drawing username routes
I just want to have the routes layout to use the username instead of id and not have the users prefix. Like:
http://example.com/username
instead of
http://example.com/users/1
class User < ActiveRecord::Base
def to_param
username
end
end
in your controller make
#user = User.find_by_username(params[:id])
instead of
#user = User.find(params[:id])
this will make your routes like http://example.com/users/username
to make what you want, you can do route like:
resources :users, :path => '' do
# nested resources...
end
so, user_path(#user) will make url http://example.com/username
but It's not a good practice, cause it's not a REST. I advise you to leave urls like http://example.com/users/username