Rails Controllers => params[] - ruby-on-rails

Can someone please help me understand params in nested attributes a little better?
I am using Apotomo. But for the example. We could just assume its in the ApplicationController
I have a simple controller show action.
if params[:id].present?
#menu = Menu.find(params[:id])
else
#menu = Menu.first
end
Which checks to see if a menu id is specified in the URL. If not, it shows the first menu.
This works well as long as I'm only on the /menus/ URL.
But I have nested attributes. So once we visit URL /menus/17/categories/
It finds params[:id] as that of the category, not the menu.
Once I'm nested, I can call :menu_id, and it works fine. But no longer works on the parent object.
How do I look for params[:id] of the menu object regardless of where I am in the URL?
And am I missing something completely?
Here is my routs config as well:
resources :menus, :only => [:show, :home] do
resources :categories, :only => [:index, :show]
end
Thanks for your patience.

I would check how routing is defined. Maybe there is a reason why this link is translated this way.

Related

Rails Nested Ressources and External Get

I have a nested ressources and I created an external get to an alternative "Show" view, like that :
resources :teams do
get "alternativeshow"
resources :teammates
end
That gives me this url which is perfect :
http://appname.com/teams/:team_id/alternativeshow
and call the controller teams#alternativeshow
Inside teams#alternativeshow I want to set #team and #teammates just like in my regular #show
def show
#team = current_crafter.teams.find(params[:id])
#teammates = #team.teammates
end
but I can't figure out how.
When I copy-paste show actions inside alternativeshow I got this error
Couldn't find Team without an ID
I noticed (without understanding why) in rake routes that :id becomes :team_id so I setted #team like this to make it work:
#team = current_crafter.teams.find(params[:team_id])
However I can't find how to have my teammates since the ressource is nested inside teams ?
Is "get" the right way to do it ?
Thank you very much for your help !
Try this,
resources :teams do
resources :alternatives, only: [:show]
resources :teammates
end
This should provide the team_id and id params in the alternatives#show
Edit:
instead of a show action for #teammates in your alternatives#show, you'd want to use an index action to be able to get a collection, so you could add an index to the only: [:index, :show] call
than
alternativescontroller ...
def index
#team = Team.find(params[:team_id]
#teammates = #team.teammates
end
end
All nested resource contollers methods follow this pattern

Rails Cancan load_resource with nested routes

I am having a problem that I thought would have been answered somewhere but I cannot find it So I have my route like this
resources :users, only: [:show, :edit, :update] do
get :resend_invitation
end
So of course the :resend_invitation route looks like this /users/:user_id/resend_invitation
It seems like Cancan only loads resources with the :id parameter. I cannot find in the docs how to specify to include the :user_id parameter as well. I just want to automatically load my resources for my nested routes as well.
If anyone has any insight I would be very grateful.
Thanks
EDIT FOR CLARITY:
My end goal is that I want #user to be populated in my nested routes
def resend_invitation
# #user = User.find(params[:user_id] done by cancan
#user.something
end
hav tim, check this in cancan's github:
https://github.com/ryanb/cancan/issues/178#issuecomment-486203
also this:
https://github.com/ryanb/cancan/wiki/Authorizing-Controller-Actions
there is your answer

How do I re-write this route to be shorter?

I have resources :tags in my routes.rb.
So when I go to /tags/ios, it shows me the correct Tag#Show view.
What I would like to happen is when the user goes to /tags/iosit shows it as /ios and likewise I want that generated path to always be /ios (and not have the leading /tags).
Here is an example of how I am rendering that link, within an each block:
<%= link_to "#{tag.name}", url_for(tag) %>
In your routes.rb
resources :tags
match '/:tag_name' => 'tags#show'
Then in your tags_controller#show action you have access to the tag name via:
params[:tag_name]
Be sure to put that at the end of your routes file, as it will catch everything. Also, since it will catch everything you should render a 404 if it's not a valid tag name:
def show
unless #tag = Tag.where(name: params[:tag_name]).first
raise ActionController::RoutingError.new('Not Found')
end
end
To redirect /tags/ruby to /ruby:
match "/tags/:tag_name" => redirect("/%{tag_name}")
http://guides.rubyonrails.org/routing.html
well the problem here is namespace
if you define a route like
get "/(:slug)" => "Tags#show"
that will basically match anything where its a valid tag or not. The way I would do it would be
in your routes file:
begin
Tag.all.each do |t|
begin
get "#{t.slug}" => "Tags#show"
rescue
end
end
rescue
end
Then in your tags controller you can
def show
slug = request.env['PATH_INFO']
#tag = Tag.find_by_slug(slug)
end
My answer comes from another answer I wrote that you may be interested How to create app-wide slug routing for Rails app?
So even though I got lots of nice suggestions from these guys, I found what I think is the best solution for me.
All I am trying to do is basically make /tags/ruby be rendered as /ruby both in the actual URL on the links of all the tags, and in the URL bar.
I don't want to do anything that will add load to my app.
This is the solution that works for me:
resources :tags, path: "", except: [:index, :new, :create]
Using path:, you can specify what you want the path to display as. i.e. what do you want to appear before your resource. So if you wanted your URLs to look like myhotness/tags/ruby, then you would simply do path: "myhotness".
I didn't want anything in my path, so I just left it blank.
For what it's worth, you can even add a constraint to that route, like so:
resources :tags, path: "", except: [:index, :new, :create], constraints: { :id => /.*/ }

Routing error when using nested resources

I have a Post and Comment models. Comment belongs to a Post, and it is nested under Post in routes. Comments are posted from Posts#show. My routes look like this:
resources :posts do
resources :comments, only: [:create, :edit, :update, :destroy]
end
If a user submits a comment that fails validation, the URL will look like this:
app.com/posts/:id/comments
If, for any reason, the user decides to hit enter in the address bar, there will be a routing error:
Routing Error
No route matches [GET] "/posts/21/comments"
Try running rake routes for more information on available routes.
This strikes me as some what odd. I understand why the error is happening, but it doesn't seem like it's a good idea for usability. Is there a way to prevent this from happening?
This becomes a bigger issue when doing friendly redirects. When a friendly redirect happens, Rails will redirect to that same URL using a GET request, again causing a routing error.
I think that the best way to avoid it is to create a route for this case and redirect to wherever make sense for your application. Something like the following:
match "/posts/:id/comments" => redirect {|params| "/posts/#{params[:id]}" }
Instead of that routing error, the user would be redirected to the post page.
If your routes are
resources :posts do
resources :comments, only: [:create, :edit, :update, :destroy]
end
then the URL for edit would be
app.com/posts/:post_id/comments/:id/edit
where :id is the comment. If validation fails, you should be redirecting back to this URL.
def update
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
if #comment.update_attributes(params[:comment])
redirect_to(edit_post_path(#post))
else
redirect_to(edit_post_comment_path(#post, #comment), :notice => "update failed")
end
end
Better, since you are already at the correct edit URL,
...
else
flash[:error] = "Error - could not update comment"
render :action => "edit"
end
Not the best but the other solution may be to add comments through nested attributes of post.

How to custom ActiveAdmin using find_by request instead of ID for all actions

I'm just adding ActiveAdmin to my app, I got a problem using show/edit/destroy action cause my link doesn't point to ID but to users name (in order to be more readable for user).
ActiveAdmin correctly create my link like:
edit link:
http://localhost:3000/admin/users/paul/edit (where paul is the user name)
in that case I get:
Couldn't find User with ID=paul
cause of course Paul is not the id but the user name.
How can I custom ActiveAdmin to use find_by_name(params[:id]) like in my application for all the action show/edit/delete?
In other model I got a so called "SID" which is a generated salted ID and I would like to use also the find_by_sid(params[:id]) as well for other models.
There is a cleaner way to do this:
ActiveAdmin.register User do
controller do
defaults :finder => :find_by_slug
end
end
This will do the job in the app/admin/user.rb :
ActiveAdmin.register User do
before_filter :only => [:show, :edit, :update, :destroy] do
#user = User.find_by_name(params[:id])
end
end
If you followed this railscast: http://railscasts.com/episodes/63-model-name-in-url-revised and have custom routes, you can fix the active_admin routes by placing this in the app/admin/user.rb:
before_filter :only => [:show, :edit, :update, :destroy] do
#user = User.find_by_slug!(params[:id])
end
It's really close to the one shown by afiah, just slightly different.

Resources