Rails grab template file as string variable in controller - ruby-on-rails

I'm creating a custom ActionMailer which will also send Direct Messages on Twitter, and Text Message through Twilio. I need each to receive a parameter of the body of the message. I need to use the current template as a string variable.
For example:
# calling send_invite's mail method will load the send_invite template
def send_invite(to)
mail(to: to) # body parameter is automatically rendered from template
end
Now if I modify it to send through other services:
def send_invite(to, option)
if option == :email
mail(to: to) # body parameter is automatically rendered from template
elsif option == :twitter
twitter.create_direct_message(to, NEED_TEMPLATE_AS_STRING_HERE)
elsif option == :sms
twilio.sms(to, NEED_TEMPLATE_AS_STRING_HERE)
end
end
How might I grab the template for each of these service calls? Would there be something like:
message(to, render layout: false)
# or
message(to, IO.read template_path_helper("template") )
# or
message(to, template_helper.to_s)
How might I get the template as a string for my message body in these other methods? What's the Rails Way to do it? The templates are erb files and need to be able to render the appropriate variable as they would from a render.
I need to do it this way for translations of templates to be available.

I think you can create the template string from the controller, and send it to your mailer, like this:
#parsed_template = render_to_string(:partial => 'my_partial', :layout => false, :locals => {:my_object => my_value})
SomeMailer.send_invite(to, option, #parsed_template).deliver

Related

PDF from ERB template with PDFKit and Rails

In my Rails application I'd like to have a special route to download a custom PDF.
This PDF should be generated via PDFKit from an ERB template in my application. Instead of describing what I'd like to achieve, I better paste some non-executable but commented code:
class MyController < ApplicationController
def download_my_list_as_pdf
# The template uses the instance variables below
#user_id = params[:user_id]
#items = ['first_item', 'second_item']
# This line describes what I'd like to do but behaves not like I want ;)
# Render the ERB template and save the rendered html in a variable
# I'd also use another layout
rendered_html = render :download_my_list_as_pdf
kit = PDFKit.new(rendered_html, page_size: 'A4')
kit.to_pdf
pdf_file_path = "#{Rails.root}/public/my_list.pdf"
kit.to_file(pdf_file_path)
send_file pdf_file_path, type: 'application/pdf'
# This is the message I'd like to show at the end
# But using 'render' more than once is not allowed
render plain: 'Download complete'
end
end
I wasn't able to find an answer to this problem yet, any help would be greatly appreciated!
render_to_string(*args, &block)
Raw rendering of a template to a string.
It is similar to render, except that it does not set the response_body
and it should be guaranteed to always return a string.
render does not return a string it sets the response_body of the response.
class MyController < ApplicationController
def download_my_list_as_pdf
# The template uses the instance variables below
#user_id = params[:user_id]
#items = ['first_item', 'second_item']
# This line describes what I'd like to do but behaves not like I want ;)
# Render the ERB template and save the rendered html in a variable
# I'd also use another layout
rendered_html = render_string(:download_my_list_as_pdf)
kit = PDFKit.new(rendered_html, page_size: 'A4')
kit.to_pdf
pdf_file_path = "#{Rails.root}/public/my_list.pdf"
kit.to_file(pdf_file_path)
send_file pdf_file_path, type: 'application/pdf'
end
end
However if you are sending a file you cannot send text or html as well. This is not a limitation of Rails but rather how HTTP works. One request - one response.
Usually javascript is used to create notifications surrounding file downloads. But consider first if its really needed as its pretty annoying to users as the browser usually tells you that you downloaded a file anyways.

Rails and XML requests

In my Rails 4 app I am dealing with an API that only handles XML (yes I wish it was JSON too).
I have to make a POST request and the XML string should be stored in a parameter called xmlRequestString.
The XML structure for the POST data is:
<?xml version="1.0" encoding="UTF-8"?>
<GetProperties>
<Auth>
<VendorId>UserID</ VendorId>
<VendorPassword>Password</VendorPassword>
</Auth>
</GetProperties>
As I have never even touched XML before could someone show me how I would actually post this data.
Would something like this be a good way of going about it (borrowed from here: Submitting POST data from the controller in rails to another website)?
require "uri"
require "net/http"
xml = 'xml string can go here'
params = {'xmlRequestString' => xml}
Net::HTTP.post_form(URI.parse('urlendpoint'),params)
You can save this as a template, with instance variables like in a regular html.erb template. Or, you could have it as a method in a model. Either way, you're using something that takes some dynamic data and returns you a text string, that has the xml in it. Then, in your controller, render out the template, or call the method (if you put it in a model) and post it to the api.
#Template method of generating xml
#app/views/properties/get_properties.rxml
xml.instruct! :xml, :version=>"1.0", :encoding => "UTF-8"
xml.GetProperties do
xml.Auth do
xml.VendorId do
<%= #user_id %>
end
xml.VendorPassword do
<%= #password %>
end
end
end
Then, in a controller, call the api:
#user_id = "foo"
#password = "bar"
xml_string = render :template => "properties/get_properties.rxml", :layout => false
http = Net::HTTP.new("www.the-api-website.com")
response = http.post("/path/to/call/the/api", xml_string)

Action Mailer attachments.inline showing as normal attachment, not inline

def simple_mail(to, subject, body)
attachments.inline['Logo.png'] = File.read( Rails.root.join("public", "Logo.png"))
mail(:to => to, :subject => subject, :body => body)
end
I receive an attachment to the message, but it is not an "inline" image. How can I attach inline, and how can I choose where it goes?
I'm seeing examples that have views for their mailer.. looks like I may need to set those up. If so, how do I link Action Mailer views to a message?
As you see in guides.rubyonrails, you can link a view to your mailer the same way you link a view to a controller's action. if your mailer is called my_mailer, and your mailer action is called simple_mail, then create a view in app/views/my_mailer/ and call it "simple_mail.html.erb". As well, you can create a "simple_mail.text.erb" to make sure that if the recipient can't receive HTML e-mails, they get the plaintext version (see action_mailer_basics.html below).
As for inline attachments, a different question was raised previously regarding views (see this link) and scaney answered with a couple links. Check out this link and search the text for "inline" and you should find the answer you're looking for!
http://guides.rubyonrails.org/action_mailer_basics.html
api.rubyonrails.org/classes/ActionMailer/Base.html
1) To Setup action-mailer views for the mails, you can do it as follows:
Create a view file in app/views/mailer_name/ with the same name as your mailer action (which in this case is "simple_mail" )
Alternatively if you want to use a different view altogether you can also do it as :
def simple_mail(to, subject, body)
attachments.inline['Logo.png']=File.read( Rails.root.join("public", "Logo.png"))
mail(:to => to, :subject => subject, :body => body) do |format|
format.html { render :partial => "/path/to/partial"}
end
end
In the above case, the content of the partial will be rendered as the body of the mail.
2) To attach a inline image you will also need to mention the attachment in the mailer action view file as follows:
<%= image_tag attachments['Logo.png'].url %>

Render ERB Template in RABL Template

I have a scenario where I'd like to pass back a long message with my JSON. Instead of writing it out with string concatenation I'd rather put together an erb template that I can render into my JSON. Below is the code I'm currently trying:
object #invitation
node(:phone_message) do |invitation|
begin
old_formats = formats
self.formats = [:text] # hack so partials resolve with html not json format
view_renderer.render( self, {:template => "invitation_mailer/rsvp_sms", :object => #invitation})
ensure
self.formats = old_formats
end
end
Everything works as expected the first time this code is run, however, I run into problems the second time I run it because it says there is a missing instance variable (which I assume was generated and cached during the first run).
undefined method
_app_views_invitation_mailer_rsvp_sms_text_erb___2510743827238765954_2192068340
for # (ActionView::Template::Error)
Is there a better way to render erb templates into rabl?
You could try using ERB as standalone, and not going through the view renderer, like so:
object #invitation
node(:phone_message) do |invitation|
begin
template = ERB.new(File.read("path/to/template.erb"))
template.result(binding)
end
end
binding is a method on Object (through the Kernel module) and it returns the binding which holds the current context, which also includes instance variables (#invitation in this case)
Update:
Don't really know if this will help you get any further (and I also realised it's been more than a year since you posted this), but here's another way to render ERB templates in a standalone fashion:
view = ActionView::Base.new(ActionController::Base.view_paths, {})
class << view
include ApplicationHelper
include Rails.application.routes.url_helpers
end
Rails.application.routes.default_url_options = ActionMailer::Base.default_url_options
view.render(:file => "path/to/template.html.erb", :locals => {:local_var => 'content'})
When I have time I should actually try this with Rabl.

Why do I have to specify text format to render template with ActionMailer?

I'm using ActionMailer 3.0.7
According to the docs plain text emails are the default. So if I have an EnquiryNotifier mailer with a notify method then I expect that app/views/enquiry_notifier/notify.text.plain.erb will be rendered.
If I simply use mail(someparams) within the notify method then the body of the email is empty.
I read that ActionMailer is meant to scan the view directory to look for all types of templates.
However, if I specify the format within a block and do
mail(:to => 'somebody', :subject => 'something') do |format|
format.text
end
then my template notify.text.plain.erb does get rendered.
Maybe unrelated: If I don't specify the format but rename the template to notify.erb then it works but the email is sent as text/html.
Here's what seems to work for me in rails 3.0.6:
I don't specify a format at all in my mailer class, I just let it find the view automatically.
I name my view "notify.text.erb"

Resources