Rails rendering file with locals using instance variable - ruby-on-rails

I've just updated to Rails 2.3.11. In an earlier version I could write the following
render :file=>'console/users/show.html.erb', :locals => {:#user => #users.first}
which no longer works, instead I need to write
render :file=>'console/users/show.html.erb', :locals => {:user => #users.first}
which means to access the user in the file I would use 'user' but in the file I would like to use the instance variable #user as this same show file is called from the controller and passes #user
Any tips?
Thanks

Set #user before calling render:
#user = #users.first
render :file=>'console/users/show.html.erb'
The :locals option should only be used if you are passing local variables through.

Related

Rails Instance Variable Accessibility Inside Layout

This may be a silly question, but I'm curious to know how it works. Here is the scenario:
Controller
def some_method
#abc = true
render :template => "show", :layout => "lightbox"
end
Now, when I try to access the instance variable #abc inside layout, it comes out to be nil. But, for following scenario:
Controller
def some_method
render :template => "show", :layout => "lightbox"
end
View (show.html.haml)
- #abc = true
The variable is accessible inside layout.
My question is:
Why is it different that I define an instance variable inside view, it's accessible in layout, but if the same variable is defined in controller instead of view, it's not accessible and come as nil?
In the first case: When you use render in the controller action, it does not inherit the controller action scope automatically. You would have to pass the variable to the render call. Something like:
render :template => "show", :layout => "lightbox", :locals => {abc: #abc}
In the view template you would be able to access the variable abc (without the #), containing the controller #abc variable contents
In the second case: when processing the template "show", rails is creating the #abc variable inside the view scope. So you can access it.
The answers provided in this other question can help you understand what is happening. And the Rails API docs are also a good reference for you

render a partial inside a model rails 3.2

I understand advice available on the net to never, ever, ever render in a model, for this case I need do it
class Order
def canceled_order_message(order)
admin = User.find_or_create_by(email: "myemail")
message = Message.new(subject: I18n.t(".buyer_has_canceled_the_order"),
body: "#{render_to_string :partial => 'users/messages/templates/sent_by_admin/canceled_order', :locals => {:order => order}}")
message.send_message
message.save
end
end
I need render inside message body attribute:
render_to_string :partial => 'users/messages/templates/sent_by_admin/canceled_order
How can I do it?
Thank you!
Please check my answer here:
undefined method `fragment_for' for nil:NilClass on render partial with cache
I think that could be useful.
P.S. Horrible idea, please rethink your architecture.

ActionMailer pass local variables to the erb template

I know I could define instance variables e.g:
def user_register(username, email)
#username = username
#email = email
mail(:to => email, :subject => "Welcome!", :template_name => "reg_#{I18n.locale}")
end
But, is there a way to use local variables instead, just like passing :locals to partials?
As ronalchn pointed out, it's the render that has :locals, not the mail method. So, you need a direct access to the render method in order to pass the locals.
You can give a block to the mail and that way gain access to the render method, something like this:
mail(to: "your_mail#example.com", subject: "Test passing locals to view from mailer") do |format|
format.html {
render locals: { recipient_name: "John D." }
}
end
And now you should be able to use "Hello <%= recipient_name %>"
All options available in the mail method can be found at http://api.rubyonrails.org/classes/ActionMailer/Base.html#method-i-mail.
We know that render has a :locals option. However we can see that there is no :locals option available for mail. Therefore, no, there is no better way than to use instance variables (unless you want to use something hideous like globals or persistent database objects - don't do this).
Instance variables are what you are meant to use.
In Rails 5, you simply have to define instance variables using # in your method. You no longer have access to the locals property for this purpose.
class UserMailer < ApplicationMailer
def welcome_email(user_id:, to_email:, user_full_name:, token:)
# Mail template variables
#user = User.find_by(id: user_id)
#token = token
mail(:to => to_email,
:subject => MAILER_SUBJECTS_WELCOME,
:template_path => "user_mailer",
:template_name => "welcome_email")
end
end
Then you can just access them in your email template using <%= #user %> and <%= #token %>
You can actually use the locals option with mail, it's just a bit confusing and inconsistent as to how.
Once you use :locals you can then access these locals in the mail template using instance variables, e.g.
:locals => { :name => 'Jane' }
and then in the template
Dear <%= #name %>,

Rails: how do I use a template from somewhere other than the file system?

I have an application that needs to support a small set of trusted users uploading new templates. I'll store them in the database or in S3. My question is: how do I tell the controller to render a given template? Of course, I could do it with a manual ERB call:
class MyController < ApplicationController
def foo
template_source = find_template(params[:name])
template = Erubis::Eruby.new(template_source)
render :text => template.result({ :some => #data })
end
end
But then I lose things like helpers and the automatic copying of instance variables.
You could do it using render :inline
render :inline => find_template(params[:name])

HAML partials - partial does not recognized object which I queried for in the controller

I have this controller:
def index
#disclosures = Disclosure.where(:user_id => current_user.id)
respond_to do |format|
format.html{}
format.js{}
end
end
and with the help of the good folks at StackOverflow I am now able to get my HAML to point to the partial like this:
= render :partial => "/influencers/disclosures/shared/list"
but this partial throws and exception:
-if disclosures.empty?
.alert.alert-info
%p=(no_disclosures_message || (t "influencers.influencer_dashboard.disclosures.no_disclosures"))
%table.table.influencer-disclosures
%tbody
-disclosures.each do |disclosure|
=render "influencers/disclosures/shared/row", :disclosure => disclosure
saying that:
undefined local variable or method `disclosures' for #<#<Class:0x133ca8a58>:0x133ca25e0>
But how can this be? I just queried for that disclosures object in my controller. Any idea why this is happening and how to fix it?
Thanks!!
You need to put an # in front of disclosures. This is how the controller passes variables to the view.
-if #disclosures.empty?
and
-#disclosures.each do |disclosure|
Update
Another way to fix this is the change your render call. This will make it backwards compatible with other call sites of the same partial.
render :partial => "/influencers/disclosures/shared/list", :locals => {:disclosures => #disclosures}

Resources