Overriden route id field is being reset - ruby-on-rails

In one of my models i have a field called token, that is created with before_create. It is never modified again.
In routes.rb i am using it instead of id, like: resources :model, param: :token
So for example the edit route is now model/:token/edit.
In my actions i am doing find_by(:token, params[:token]).
I have a partial form created with simple_form which is being loaded in the new and edit actions.
This works fine, the routes are generated properly, all showing :token: instead of :id.
The show action works fine. In the show page i have a link_to which links to the edit view. This works fine also.
Both are using :token in the route, the edit view loads the models fields, everything is normal.
However when you look at the source code for the edit view, it shows the action as /model/<id>, instead of /model/<token>. For example /model/5.
When you submit the edit form, it tries to go to /model/5/.
In addition, when i checked the params being sent, it shows token set to the value of id. So somehow, the token field has been reset.
So
1) The token field is somehow being reset to the value of id. I have no idea how this is happening.
2) simple_form seems to be generating the action based off of id instead of token. However i have realized that since token is being reset, it could be that simple_form isnt doing anything wrong and is using the value of token.
The only fix i could come up with, was setting the url field in simple_form_for manually, but if i do that, it then breaks the new action.
For simple_form I'm just doing:
<%= simple_form_for #model do |f| %>
In routes.rb I'm doing:
resources :model, param: :token
In the controller, the edit action is:
#model = Model.find_by(token: params[:token])
The update action is:
#model = Model.find_by(token: params[:token])
if #mode.update model_params
redirect_to model_path #model.token
else
render 'edit'
end
Nothing fancy in the least.

The simple_form uses the default behavior of Rails when generating the route string for you. Since the config/routes.rb just provide the named pattern matching for the Outside In URL, so it is not too much meaning for the application from inside. For ex, with a route like this
model/:token/edit
just means any string between model/ and /edit will be assigned to params[:token]. It doesn't mean that string has to be the value of YourModel#token. Of course, you can assign that pattern to params[:foobar] by the same way without breaking anything model/:foobar/edit
For fully replace id key by token key, you have to override the method YourModel#to_param
# app/models/your_model.rb
class YourModel < ActiveRecord::Base
def to_param
token
end
end
So that, the ActiveSupport will know to use token as the value when generating routing pattern from inside out.
You can read more about it at the Rails's Guides

Related

Can I have several New methods in a rails controller on Rails 5.2?

My application has several ways to create notifications:
A user can create a notification using the standard "new" method
A link in a view can create a notification from an action using a dedicated "new_action"
etc.
So I created additional route and views for the new_action_notification_path:
resources :notifications do
member do
get :new_action
end
collection do
get :index_all
end
end
In the controller
# GET /notifications/new_action
def new_action
#playground = Playground.find(current_playground)
#notification = #playground.notifications.build( playground_id: params[:playground_id], …
And in the view:
<%= link_to t('Reject'), new_action_notification_path(
playground_id: current_playground,
description: t("#{this_object.class.name}#{'Rejected'}"),
But this does not behave as expected:
If I write new_action_notification_path in the view, as above, the generated URL looks like /notification/729/new_action?code=QWSTZ ...
If I write new_notification_path in the same place, the generated URL looks like /notification/new?code=QWSTZ ...
Why is it different, and how can I remove the notification id from the first URL?
Thanks a lot!
The reason your url's are different is because you have a nested route for the new_action.
resources :notifications do
member do
get :new_action
end
end
With a nested route, you are going to get an id number between your resources. The 729 is the id you are passing in to the link_to helper. The other route helper, new_notification_path, is creating a new notification, so it doesn't need an id. If you look at your routes, with rails routes in your console or localhost:3000/rails/info/routes in your browser, you'll see that the new_action needs an id.
new_action_notification_path GET /notifications/:id/new_action
new_notification_path GET /notifications
The :id part of the route is a placeholder for an id number (although it can be anything you pass in, really) to look up the resource before it. In this case, the id is use to look up the notification.

How does edit_password_reset_url(some_id_here) resolve to https://example.com/password_resets/some_id_here/edit?

I've been looking at Learn RoR tutorials and we have this route:
edit_password_reset GET /password_resets/:id/edit(.:format) password_resets#edit
I do not understand how this line:
<%= edit_password_reset_url(#user.reset_token, email: #user.email) %>
Will translate into:
https://example.com/password_resets/<some token here>/edit?email=<user email here>
More specifically, how does rails know that the first param (#user.reset_token) is suppose to go into the :id portion of the url?
The controller PasswordResetsController's edit function is defined but blank.
The first argument passed to edit_password_reset_url replaces the :id parameter you see when running rails routes (/password_resets/:id/edit(.:format)). The docs on this can be found here.
You can actually update this to use a more relevant parameter, such as :token in your case, by using:
resources :password_resets, param: :token
The url helper also takes a hash of arguments where you can provide additional params, as you're doing with email.
Slightly unnecessary additional explanation
Were you using a nested route, say, edit_user_password_reset_url, this would product something akin to users/:id/password_resets/:token/edit(.:format). The same still applies - the first arg of the helper fills in the variable parameters, though would now take an array, i.e. edit_user_password_reset_url([#user.id, #token])
/end slightly unnecessary additional explanation
The edit action in your controller can be blank, though typically assigns a resource to be used in the edit view (/password_resets/edit.html.erb or similar). This generally contains a form that, when submitted, will hit the update action of the same controller.
So, in this case, you may want the edit action to contain the following:
def edit
#user = User.find_by_reset_token(params[:id]) # or params[:token] if you update as above
end
Then, in your edit view, you can include a form allowing the user to reset their password.

Get the actual id of current user without deleting the session? rails will_paginate dilema

I want to get the real id of a User due to a problem in the url for will_paginate in that I need to set it manually, so I want to get the target link for will paginate by a custom renderer method in the view like this,
<%= will_paginate #friends, :renderer => WillPaginateHelper::MyLinkRenderer %>
And the helper is something like,
module WillPaginateHelper
class MyLinkRenderer < WillPaginate::ActionView::LinkRenderer
include SessionsHelper
protected
def link(text, target, attributes = {})
if target.is_a?(Integer)
attributes[:rel] = rel_value(target)
target = "/users" + current_user.id + "/friends?page=#{target}"
end
attributes[:href] = target
tag(:a, text, attributes)
end
end
end
The line target = "/users" + current_user.id + "/friends?page=#{target}" is the important one where I need to set the url for will_paginate anchor links with the current user id.
Problem is that when the helper I am using is ran in the view to set the url, I get a error undefined local variable or method session..., you cannot use sessions hash in helpers so how to get the real id of the current_user to insert into variable. Do I delete/destroy the session get the id and create a new one? The problem is once I delete the session how to get the id once I delete the session and the user reference is deleted.
Reason
I have a friends#index action that is rendered in a div and upon initial call to url the pagination attaches correctly being the url users/:id/friends so each pagination request goes to the correct user and action. But this index view has "Unfriend" form attached to each displayed friend so you can destroy the friendship which goes to the destroy action of the friends controller so markup is eg <form... action="friends/177"...> and on full view reload of the index action from destroy action will paginate attaches to the last known link unless overriden. So when index action is fully rendered again pagination links are friends/177 which give a server error anyway and makes no sense as that record just got destroyed.
I have the current_user variables and id in my destroy action but I can not find a way to get them to my helper method, or simply get the current user id from the session.
Ok, so thanks to max and everyone for helping me. found this post and this answer here which you can do something like,
In your routes
get 'users/:cu_id/friends', to: 'users#index', as: :my_friends
And for the view
<%= will_paginate #friends, params: { controller: :users, :cu_id => current_user.id } %>
This will produce users/:cu_id/friends?page=2... of course :cu_id will be the actual id number, for pagination.
EDIT: Order is important here to if you have any other routes going to the same url eg, users/:user_id/friends when you nest resources, you need to put get 'users/:cu_id/friends',... below this, as rails will build the correct route for you for will paginate but come back in through the friends#index action when paginated and match eg users/1/friends through users/:user_id/friends as it is first in order in routes.rb. here in rails guide it explains how it works.

How to setup routes when the controller only has edit and update?

I can't seem to figure out how to get my routes setup properly.
In my app, I have a view that lets site owners update their address information. The new and create actions are part of the signup process and are located in the signups_controller. The edit and update actions are in the settings_controller.
When the user goes into the settings area, he/she sees only the edit form. When filled out, the user is then returned to the same form with a flash message, or error message. Here is what the controller looks like:
class SettingsController < ApplicationController
def edit
#account = current_account
#account.companies.first
#account.companies.first.addresses.first
#account.companies.first.phones.first
end
def update
#account = current_account
if #account.update_attributes(params[:account])
redirect_to edit_setting_path
flash[:notice] = "Success!"
else
render :edit
end
end
end
In my routes, I simply have:
resources :settings
The link to this area of the site is a basic RESTful named linke, with the parameter options:
edit_setting_path(:id => current_account.id)
When the user arrives to this page, they see the following URL:
http://domainname.com/settings/1/edit
When they submit the form and get errors, the URL changes to:
http://domainname.com/settings/1
Why is the URL changing -- I'd rather it not? Is there a way to make it stay the same as the initial edit view? I've tried doing a redirect on a failed update, but then I don't get the error messages.
Any ideas?
To answer your "why" question: The URL is changing because it's reflecting the URL of the failed request - which in this case is a PUT request to that URL (/settings/1). You've submitted the form and the submission of that form (correctly) points to that URL. This is a result of the RESTful routes that the helper gives you. Since the logic in your action, falls through to the render :action, there is no redirect and the form simply re-renders on the page using the same data available in this action (which is why you can see the errors).
If you want to redirect back to the edit page, yes, you will lose the errors that have been set in the #account instance variable since the redirect will reset (re-query for) the account.
You could add a route that matches a PUT to /settings/1/edit and point it to your update action and change your form etc. In short, I wouldn't recommend this, but it should work.
completely untested but attemptable:
routes.rb
put "/settings/:id/edit", :to=>"settings#update", :as=>"update_setting"
resources :settings, :except=>:update
your form would also have to submit to the update_setting_path (which also means it's not reusable for a new object... ew)
First you should read up on The Rails Guides for Routing. They will help a lot to understand why its working like that.
Secondly, to accomplish what you are trying to do, you will need to add manual routes via the match call. You'll need something like this.
match '/settings/:id/edit' => "settings#edit"

I want to pass the name of a calling web page in a form in Ruby on Rails

I am quite new to Ruby. I have a landing page controller and index page that has a button on it that pops up a user input form for email addresses, etc. One of the things I want to capture and write into the database is the name of the originating landing page.
For example:
www,mydomain.com/landngpage/campaign1
Another landing page could be:
www,mydomain.com/landngpage/campaign2
The above form calls a ppc_user controller
www,mydomain.com/lppc_user/new
Can anyone help me on this? I have seen a few examples of passing data using the flash option, but I can't get this to work.
I guess you're looking for request.referer.
It tells you from which page the user comes from.
You could use a hidden field and in fill it with an instance variable created in the controller...
so in your controller index:
def index
#campaign = params[:campaign] # this is whatever parameter you have named that is "campaign1", "campaign2", etc..
end
then in your form:
hidden_field :campaign, #campaign
or with the answer given by apneadiving:
hidden_field :campaign, request.referer
and then whatever controller you are posting your message to will have a param called :campaign containing the URI that it came from or the name of the campaign parameter depending on which one you choose to use.

Resources