Passing user id of current user through a partial - ruby-on-rails

I'm trying to pass the id of the user currently signed in into a form rendered by the partial below
<%= render :partial=>'new_reservation', :locals=> {:reservation => Reservation.new(:table_id=>#table.id, :user_id=>#user.id)} %>
I'm using Devise and also tried to call current_user but it doesn't seem to be working. Whenever I look at the reservation that was created I don't see a user id.
Any idea what I am missing

Why are you sending a new ActiveRecord object to your partial?
You'd be better setting an instance variable & sending that as a local:
#controller
#reservation = Reservation.build(table_id: #table.id, user_id: current_user.id)
#view
<%= render partial: "new_reservation", locals: {reservation: #reservation} %>

Related

ActionCable + Devise + Pundit + ApplicationController.render

I'm trying to render a template in a ActionJob to be broadcast via ActionCable.
ApplicationController.render(partial: "messages/message", locals: { message: message }, assigns: { current_user: user}).squish
In most instances, this works fine, however some of my templates use Punit for authorization in the view.
<% if policy(message).show? %>
<%= message.body %>
<% end %>
This raises an error when the job is ran.
ActionView::Template::Error: Devise could not find the `Warden::Proxy` instance on your request environment.
A quick Google search reveals this issue: https://github.com/plataformatec/devise/issues/4271
The mentioned in the ticket and links, there is no env['warden'] available because no middleware has executed to add it.
How can I work around this?
As a workaround, this is what I've done:
class ActiveJobController < ActionController::Base
end
In my partial, instead of using the policy helper, I'm doing this
<% if Pundit::PolicyFinder.new(message).policy.new(current_user, message).show? %>
<%= message.body %>
<% end %>
and from my ActiveJob
ActiveJobController.render(partial: "messages/message", locals: { message: message, current_user: user }).squish
This avoids any of the stock Devise and Pundit helpers which references env["warden"]. It isn't ideal but works for now both when rendered in a request and in a job.
Another scalable/maintainable approach is to use a helper or a view library like ViewComponents/cells. This way, you can extract your existing view into a component and parameterize any devise/warden methods called in the view. The advantage of this approach is
The view is easier to test
You can call it from anywhere in your code
There are no coupled dependencies
Example using view helpers
Say you have in app/views/messages/show.html.erb the following
<%= current_user.first_name %>
<%= #message %>
Calling MessagesController.render :show outside the controller will cause an error since access to the request object is not available. Using ViewComponents, we extract the view into its own component
in app/components/message_component.rb
class MessageComponent < ViewComponent::Base
def initialize(user:, message:)
#user = user
#message = message
end
end
in app/components/message_component.html.erb
<%= #user.first_name %>
<%= #message %>
Usage
In app/views/messages/show.html.erb just call
<%= render(MessageComponent.new(message: #message, user: current_user) %>
In ActionCable, call it like so
ApplicationController.render(MessageComponent.new(message: #message, user: current_user)
Since you have access to ActiveRecord models from anywhere in your code, you should be able to fetch #message

rails 4 strong params session

I am using a custom passwordless login for my app. A user simply needs to enter their username or email, and then a unique login link with a token is sent to them. They enter their username or email into a simple form :
<%= form_tag request_token_path, id:'login-form' do %>
<%= text_field_tag :user_id %>
<% end %>
This posts to a sessions#request_token method which verifies whether that user exists and then sends along the login link.
def request_token
lookup = session_params[:user_id]
if lookup.include? '#'
#user = User.find_by(email: lookup)
else
#user = User.cached_find(lookup)
end
if #user
#user.send_login_link
redirect_to login_path, notice: "#{#user.username.capitalize} your email was sent!"
else
redirect_to login_path, notice: "Whoops! Looks like #{lookup} is not registered on this site. Please check spelling or signup!"
end
end
My question is that in my SessionsController file I defined the sessions_params
private
def session_params
params.require(:session).permit(:user_id,:auth_token)
end
I know that means that I have to use a session object or in order to pass along the :user_id from the form since I defined :user_id as a param that is on valid as an attribute of a session. I am wondering the correct way to do this. Making a new session object doesn't make sense since that isn't even a model I have but is it safe to just take it from the params?
and instead make lookup = params[:user_id] ?
If you have a session object that responds to user_id attribute, you need to create the form for that object specifically:
<%= form_for #session do |f| %>
<%= f.text_field :user_id %>
<% end %>
If that's not the case, and you need to stick to form_tag, try making the attribute name something that would come up in the controller as a session hash:
<%= text_field_tag "session[user_id]" %>
When you do
params.require(:session)
it means you're requiring your params hash to have a session key, which in turn should have the permitted user_id attribute:
{params: {session: {user_id: "something"}}
And thats why you'd need form_for #session OR the textfield with the suggested "session[user_id]" name

rails3 show not getting :user_id in one case

I have a strange problem. I've been coding in Rails for, off and on, a year. I created a new project recently and used scaffolding. Things were going fine, yesturday I started implementing some favoriting features. Now I have a strange problem. I rolled back the stuff I did last night but still have the problem. First
Entry belongs to user
User has many entries
My Entry show method in my controller is very standard and simple
def show
#user = User.find(params[:user_id])
#entry = #user.entries.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #entry }
end
end
When I view the entry from a normal link in the entries index
<%= link_to 'Show', user_entry_path(#user, entry) %>
I takes me to where it should go:
/users/4/entries/11
When I create new things still look good
/users/4/entries/new
Until I click "create entry" or the submit button
<div class="actions">
<%= f.submit %>
</div>
Then it goes to
/entries/20 ...with the error:
ActiveRecord::RecordNotFound in EntriesController#show
Couldn't find User without an ID
If I go back to the entries index however, the file new entry is there and the show link takes me to the right place. Thoughts? Your help is appreciated!
The error message tells you that User.find(params[:user_id]) couldn't find a user with that ID. Try checking the structure of the GET parameters in the server logs.
If your GET path is /entries/20, then the path only has an entry ID and is missing a user ID. You might be able to fix this in your Controller#create by having it redirect to user_entry_path instead of entry_path.
How does your form look like?
I think you have nested routes? Your form should look like following:
<%= form_for [#user, #entry] do |f| %>
<% # your fields %>
<% end %>
Your form seems to point to resources entry, instead of the nested ressource..

How to Check the URL or path in Rails?

I have a Rails app where I have a show action with a form. This show action uses a view that checks if user_type == "admin" and displays a partial accordingly. For this show view, only if user_type = admin can access it.
Once the form is submitted, it triggers another action in the same controller (store). The issue I'm having is that when there is an error with the submission, store renders show without reference to the user, so the partial doesn't appear since user_type won't equal admin (since it can't find the user). However, since access to 'show' is protected, I'm wondering if there's a way I can check the url.
Example:
the form is on:
http://foo.com/bars/4/users/3
however, when it fails, the render url becomes:
http://foo.com/bars/4/users/store
the show.html.erb view has this:
<%= form_for([:bar,#user], :url => store_bar_users_path(params[:bar_id]), :html => {:name=>"users_form",:multipart => true,:class=> "standardForm"}) do |user| %>
<%= render :partial => "users/admin_options" if #user.user_type == "admin" %>
in the store action in the controller:
if !#user.save
render :action => "show" ,:layout => 'application'
else
So what I'm wondering is if I can change the conditional in:
<%= render :partial => "users/admin_options" if #user.user_type == "admin" %>
to somehow also also check the url for store_bar_users_path, or something like that - or perhaps the render in the store action is incorrect.
Store the user type in a session variable, set when the user logs in. Then you don't have to keep loading it, and it is visible to all controllers, views, and helpers. Use this variable to determine if you want to show the partial.

What to render on error in a Rails controller

In my rails application, I've got a partial view with an entry form on it. The form gets included on multiple pages across my app. The form in the partial posts to a RidesController to save with a create method like this:
RidesController.rb
def create
#ride = current_user.rides.build(params[:ride])
if #ride.save
flash[:success] = "Ride created!"
redirect_to root_path
else
#rides = current_user.rides.paginate(:page => params[:page])
render 'pages/home' # <---- WHAT GOES HERE?
end
end
I've commented the line where my question is. When we have an error, I need to present the same view that the user is presently on. But because this controller is being invoked from a partial instead of a full view, I don't know how to tell what context it's coming from.
Right now if there's an error on /rides/new, the user ends up redirected to the homepage which also has the form.
One way you could do this is pass the template path in with the form.
Add this to each main view that includes the form partial (e.g. pages/home, rides/new, etc):
<% #current_page_template = __FILE__ %>
In your form partial:
<%= form_for ... do |f| %>
<%= hidden_field_tag 'current_page_template',
#current_page_template.sub(File.join(Rails.root, 'app', 'views'), '') %>
In your controller:
def create
...
if #ride.save
...
else
...
render params[:current_page_template]
end
end

Resources