Rails 5 render set instance variables in locals - ruby-on-rails

I have some complicated PDF generation logic that requires rendering a view outside of a controller and then passing the HTML into WickedPDF:
ActionView::Base.send(:define_method, :protect_against_forgery?) { false }
av = ActionView::Base.new
av.view_paths = ActionController::Base.view_paths
income_statement_html = av.render :template => "reports/income_statement.pdf.erb", :layout => 'layouts/report.html.erb',
locals: {:#revenue_accounts => revenue_accounts,
:#expense_accounts => expense_accounts,
:#start_date => start_date,
:#end_date => end_date,
:#business => business}
This all works fine on Rails 4 but has stopped working when we upgraded to Rails 5.
All the instance variables we are setting here end up as nil inside the view. Is there still a way to set instance variables from the render call like this?

Rails 5 introduced ActionController::Base.render, which allows you to do this instead:
rendered_html = ApplicationController.render(
template: 'reports/income_statement',
layout: 'report',
assigns: {
revenue_accounts: revenue_accounts,
expense_accounts: expense_accounts,
start_date: start_date,
end_date: end_date,
business: business
}
)
Which you can then pass to WickedPDF:
WickedPdf.new.pdf_from_string(rendered_html)
You can read more about .render and using it with WickedPDF, as well get some examples of how to extract this functionality into reusable objects on this blog post.

ActionView::Base has a method assign which can be called to set the instance variables.
av.assign({revenue_accounts: revenue_accounts,
expense_accounts: expense_accounts,
start_date: start_date,
end_date: end_date,
business: business})
income_statement_html = av.render :template => "reports/income_statement.pdf.erb", :layout => 'layouts/report.html.erb'

Related

Undefined local variables in Rails 3 partials after upgrade to Ruby 1.9.3

I know there are several posts on this issue but none of the solutions I've read help here.
I've just upgraded from Ruby 1.8.7 to 1.9.3p429 and now, I get undefined local variable in my partials.
I am calling a partial (from a partial in case that's relevant) thusly:
= render :partial => 'user_name', :locals => {:user => item_user, :extra_class => "active"}
In the partial, I access these locals thusly:
- if(current_user and user.silhouette_user_id.to_i == current_user.silhouette_user_id)
= notranslate(current_user.full_name)
- else
- if !local_assigns[:extra_class].nil?
= notranslate(link_to( h(user.full_name), stream_url(:user_id => user.silhouette_user_id), :class => extra_class )) rescue "Anonymous"
- else
= notranslate(link_to( h(user.full_name), stream_url(:user_id => user.silhouette_user_id) )) rescue "Anonymous"
= notranslate(link_to "Pro", "#", :class => "badge badge-pro", :title => "#{user.part_name} is pro") if SSO.is_pro? user
I can access the locals via the local_assigns hash, so if I add these lines to the top, I have access:
user = local_assigns[:user]
extra_class = local_assigns[:extra_class]
I could live with that. However, it gets worse. user is an ActiveRecord model object. I cannot, however, access the attributes of this object with user.attribute notation. Instead I have to user user[:attribute].
I could still get by with this except that some of the attributes of user are actually methods, which I cannot access using user[:method].
I thought it might be the Rails, which was at 3.1.12 so I upgraded to 3.2.13 with no change.
Any ideas? Could it be the HAML processor? All other posts that were solved used ERB.
Try using this way for rendering partials (it's correct way for Rails 3):
= render 'user_name', user: item_user, extra_class: "active"
And access to objects using user and extra_class

Rails - Paperclip url in JSON along with other attributes

I've followed Ryan Bates' screencast on using jQuery Tokeninput for an auto-completing list for a many-to-many association. Now I want to pull in a photo for each result. I'm using Paperclip and get the url's passed into a JSON file by doing this in the controller:
format.json { render :json => #users.map(&:photo_url) }
Ryan's code for passing the attributes into a JSON file is this:
format.json { render :json => #users.map(&:attributes) }
But how can I combine the two to display both the :attributes and :photo_url methods in the JSON file?
I've tried different things, including the below code, but nothing seems to work. It seems as if there can only be one method called on .map?
// Doesn't work
format.json { render :json => #users.map(&:attributes, &:photo_url) }
// Doesn't work
format.json { render :json => #users.map(&:attributes).map(&:photo_url) }
Does this help? (Note - I'm just returning from a night out and am not 100%, so I might be misunderstanding your question entirely.)
This creates an array of arrays: The first element in the array contains the user's attributes, and the second contains the photo URL:
#users.map {|u| [u.attributes, u.photo_url]}
This creates a hash - just like the above array. But the first element is named "attributes" and the second is named "photo_url".
#users.map {|u| {:attributes => u.attributes, :photo_url => u.photo_url}}
Try plugging one or both of those in. They should work for you.
(E.g. format.json { render :json => #users.map {|u| [u.attributes, u.photo_url]} }).
Edit:
Just had another thought.
You can merge the two into one collection (so that you'll have it all in one hash instead of separate elements in an array):
#users.map {|u| u.attributes.merge(:photo_url => u.photo_url)}
That'll add photo_url as a key to the attributes hash. It might work more easily for whatever code you've written to read the JSON.
In case of this being helpful to anyone, i find out a nice way to do this:
class MyModel < ActiveRecord::Base
has_attached_file :avatar, :styles => { :large => "500x500#", :medium => "300x300#", :small => "100x100#", :thumb => "50x50#" }
def as_json(options)
json = super
self.avatar.styles.each do | format |
json = json.merge({"avatar_"+format[0].to_s => self.avatar(format[0])})
end
json
end
end
You can then simply call
render :json => #my_model
Also working while rendering collections.
It is then possible to do some conditional rendering with as_json(options), with something like:
model_to_json = #my_model.to_json(:nested => true)
render :json => model_json

RAILS3: to_JSON with multiple objects and includes

def list
#rings = Ring.order("RAND()")
#JSON RENDERING
render :json => #rings.to_json(:include => [:variations, :stones]), :callback => params[:callback]
end
def show
#showring = Ring.includes(:stones, :variations).find(params[:id])
#other_rings = Ring.select([:id, :stone_count]).where(:style_number => #showring.style_number).reject{ |ring| ring == #showring}
#JSON RENDERING
render :json => {#showring.to_json(:include =>[:variations, :stones]), :other_rings => #other_rings}, :callback => params[:callback]
end
My list view rendering works fine, but when i want to do a show view, with two objects, and showring with includes won't render proper JSON. It is quoting everything in the object with the includes...
JSON output looks like this:
showring => "{"available":"yes","eng...9","stone_y":"149.4"}]}"
other_rings => properly rendered object
On a seperate note, if i have already added the includes to #rings object, why do i then again have to add the association in the "to_json" method?
When you do
render :json => {:show_ring => #showring.to_json(:include =>[:variations, :stones]), :other_rings => #other_rings}
Rails is converting #showring to json (ie getting back a string representation), i.e. the value is the string literal. Instead do
render :json => {:show_ring => #showring.as_json(:include =>[:variations, :stones]), :other_rings => #other_rings}
as_json does all the work of turning the object into a hash but without the final step of turning into a string
if you are going to invest more time in building more JSON objects, you should look into a gem called rabl. It makes building JSON very simple, good for customization which then is good for building API.

ruby on rails and XML

I am trying to create a ruby on rails app to capture data from form and create a corresponding XML.I have created a dummy model class which is not extending active record
Do i have to do that .Below is the code and the error i m facing plz help
class RamOne
attr_accessor :name,:city
end
Controller
def start
#ramone = RamOne.new
end
def load_new
#ramone = RamOne.new(params[:ramone])
if #ramone.save
redirect_to :action => ‘gen_xml’
end
end
def gen_xml
#xml = Builder::XmlMarkup.new
#ramones = RamOne.find(:all)
render :layout => false
end
View captures name,city and has a submit action attached with load_new
error : wrong num of args(1 for 0 ) in load_new
what is wrong?
You can't call RamOne.new with an argument because your RamOne class does not override the initialize method. Also, #ramone.save and RamOne.find are all ActiveRecord methods, so I think you need to extend ActiveRecord::Base in your RamOne class.
Check out the Hpricot gem http://hpricot.com/ if you are doing some heavy duty XML.
Check out REST to clean up your controller. http://guides.rubyonrails.org/routing.html#restful-routing-the-rails-default
Can i get rid of the model class
and store my captured data in a normal ruby class and fetch from it to create the xml .Are there any helper methods available
similar to form_for() helper
<% form_for :ramone, #ramone, :url => { :action => "load_new" }, :html => { :method => :get } do |f| %>
can i find any other non ActiveRecordHelper methods
I dont want the Model class unnecesarily.

How to pass variables to render_to_string?

Trying to do the following
#message = render_to_string ( :sender => sender, :template => "template" )
But when accessing #sender in template it turns out to be nil:NilClass. Double checked if I pass the right variable and it's totally fine. Maybe there are other way to pass variables to render_to_string?
It might be the syntax you're using. Try using the :locals argument:
#m = render_to_string :template => "template", :locals => {:sender => sender}
Then you just need to access sender (without an #) as a local variable inside the template.
Here's Jason Kim's solution he wrote in a comment which worked for me:
ActionController::Base.new.render_to_string(
"user_mailer/welcome_email.html.erb", locals: { :#user => user}
)
Please mind the :#user => value bit.
In Rails 5 (atm in beta):
ApplicationController.render(
file: 'path',
assigns: { foo: 'bar' }
)
More here
Try this:
ac = ActionController::Base.new()
ac.render_to_string(:partial => 'path to your partial',:locals => {:varable => your variables})
In rails 4.0.2 this worked:
render_to_string(partial: 'path/to/partial', locals: { argument: 'value'}
I was trying to render a different format of partial in render_to_string. The thing which really worked for me was:
render_to_string(:partial => 'partial_file.html', :locals => {:variable => variable}, :format => :html)
where the name of the file was _partial_file.html.erb.

Resources