How does Rails 3 actionmailer decide which format to use? - ruby-on-rails-3

In documentation it says, that mailer actions behave in very similar fashion as controller actions.
In rails guide, to send mail:
UserMailer.welcome_email(#user).deliver
and welcome_email action looks like this:
def welcome_email(user)
#user = user
#url = "http://example.com/login"
mail(:to => user.email, :subject => "Welcome to My Awesome Site") do |format|
format.html { render 'another_template' }
format.text { render 'another_template' }
end
end
what I don't get is, how welcome_email action decides which format to use (html or text)?
Thanks!

I believe it will create a multipart email that includes both html and text parts. This will allow text only clients to render it using that part and html based clients to render it properly too.
Rails 3: http://guides.rubyonrails.org/action_mailer_basics.html
Rails 2: http://guides.rubyonrails.org/v2.3.8/action_mailer_basics.html

Related

rails 3 load different partials in mailer action

So I have one layout for 2 mail actions: signup_email and change_password
def signup_email(user, url)
#user = user
#url = url
mail(to:user.username, subject:"Welcome to Clubicity!", template_path: "mail_templates", template_name: "system")
end
def change_password(username,url)
#url = url
mail(to:username,subject:"Clubicity - password recovery", template_path: "mail_templates", template_name: "system")
end
I managed to get the one layout for this 2 actions, but now in this layout I need to render 2 different partials depending on what action is called, signup or change_pwd..
I've looked in RailsGuides and api.rubyonrails.org and they say only about templates.
Please, need help with this.
got it using mail layouts like this
def change_password(username,url)
#url = url
mail(to:username,subject:"Clubicity - password recovery") do |format|
format.html { render :layout => 'mail_templates/system'}
end
end
def signup_email(user, url)
#user = user
#url = url
mail(to:user.username, subject:"Welcome to Clubicity!") do |format|
format.html { render :layout => 'mail_templates/system'}
end
end
and in the layout just put <%= yield %>
and rock'n'roll!
So, you want to use one file for different actions. Of course you have to define actions in templates.
I propose to make one view file for each action.
For using one layout just add <%= render 'email_header' %> to the top and <%= render 'email_footer' %> to the bottom of each view. Also make _email_header.html.erb and _email_footer.html.erb with appropriate markup.

How to construct form_tag URL that PUTs to an external API

I'm using the Shopify API http://api.shopify.com/
And the Shopify Gem: https://github.com/Shopify/shopify_api that does most of the heavy lifting- just can't quite figure out how to make it work.
To update an #variant object I need to PUT here: PUT /admin/variants/#{id}.json
In config/routes.rb I made default resource routes with resources :variants and now I'm trying to make a form that updates a variant resource but can't configure the form to have the proper action.
Basically I'm constructing form_tag with a text field input that takes an integer and updates variant.inventory_quantity
Rake Routes give me this:
rake routes:
variants GET /variants(.:format) variants#index
POST /variants(.:format) variants#create
new_variant GET /variants/new(.:format) variants#new
edit_variant GET /variants/:id/edit(.:format) variants#edit
variant GET /variants/:id(.:format) variants#show
PUT /variants/:id(.:format) variants#update
DELETE /variants/:id(.:format) variants#destroy
You need to declare variants resource under admin namespace like this:
config/routes.rb
namespace :admin do
resources :variants
end
EDIT:
You don't have to do anything special for Rails to accept JSON. Rails will convert the JSON you passed in PUT into params and make it available to update method.
Here is the standard implementation of 'update' method:
app/controllers/admin/variants_controller.rb
def update
#variant = Variant.find(params[:id])
respond_to do |format|
if #variant.update_attributes(params[:variant])
format.html { redirect_to(#variant,
:notice => 'Variant was successfully updated.') }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => #variant.errors,
:status => :unprocessable_entity }
end
end
end
Refer to Rails guide and layout and rendering for details.

How can I toggle a boolean field in Rails 3 using AJAX?

I have a column in a table called Complete that is a boolean.
How can I (using the Rails 3 / JQuery way) provide a link that will toggle that via AJAX?
Historically, I've simply created my own jquery file, grabbed the click event, and done it by hand. But it really seems like I'm missing something by not doing it the "rails" way. If that makes sense.
I guess I still don't understand how the responds_to JS works, JS with ERB, etc.
Is there a good, up-to-date tutorial on how to do this?
Thanks.
see this post,
Rails 3, Custom Actions, and HTML request methods
and use UJS on top of it.
so you have a fallback, if javascript is disabled
a possible method in the controller looks like that:
# GET /surveys/1/close
def close
#survey.close!
flash[:success] = "Survey successfully closed!"
respond_to do |format|
format.html { redirect_to(surveys_url) }
format.xml { head :ok }
format.js { render :partial => 'list_item', :locals => { :survey => #survey } }
end
end
you could also use a state machine to change the state of your object.

Rails 3 ActionMailer and Wicked_PDF

I'm trying to generate emails with rendered PDF attachements using ActionMailer and wicked_pdf.
On my site, I'm using already both wicked_pdf and actionmailer separately. I can use wicked_pdf to serve up a pdf in the web app, and can use ActionMailer to send mail, but I'm having trouble attaching rendered pdf content to an ActionMailer (edited for content):
class UserMailer < ActionMailer::Base
default :from => "webadmin#mydomain.com"
def generate_pdf(invoice)
render :pdf => "test.pdf",
:template => 'invoices/show.pdf.erb',
:layout => 'pdf.html'
end
def email_invoice(invoice)
#invoice = invoice
attachments["invoice.pdf"] = {:mime_type => 'application/pdf',
:encoding => 'Base64',
:content => generate_pdf(#invoice)}
mail :subject => "Your Invoice", :to => invoice.customer.email
end
end
Using Railscasts 206 (Action Mailer in Rails 3) as a guide, I can send email with my desired rich content, only if I don't try to add my rendered attachment.
If I try to add the attachment (as shown above), I get an attachement of what looks to be the right size, only the name of the attachment doesn't come across as expected, nor is it readable as a pdf. In addition to that, the content of my email is missing...
Does anyone have any experience using ActionMailer while rendering the PDF on the fly in Rails 3.0?
Thanks in advance!
--Dan
WickedPDF can render to a file just fine to attach to an email or save to the filesystem.
Your method above won't work for you because generate_pdf is a method on the mailer, that returns a mail object (not the PDF you wanted)
Also, there is a bug in ActionMailer that causes the message to be malformed if you try to call render in the method itself
http://chopmode.wordpress.com/2011/03/25/render_to_string-causes-subsequent-mail-rendering-to-fail/
https://rails.lighthouseapp.com/projects/8994/tickets/6623-render_to_string-in-mailer-causes-subsequent-render-to-fail
There are 2 ways you can make this work,
The first is to use the hack described in the first article above:
def email_invoice(invoice)
#invoice = invoice
attachments["invoice.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "invoice",:template => 'documents/show.pdf.erb')
)
self.instance_variable_set(:#lookup_context, nil)
mail :subject => "Your Invoice", :to => invoice.customer.email
end
Or, you can set the attachment in a block like so:
def email_invoice(invoice)
#invoice = invoice
mail(:subject => 'Your Invoice', :to => invoice.customer.email) do |format|
format.text
format.pdf do
attachments['invoice.pdf'] = WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "invoice",:template => 'documents/show.pdf.erb')
)
end
end
end
I used of Unixmonkey's solutions above, but then when I upgraded to rails 3.1.rc4 setting the #lookup_context instance variable no longer worked. Perhaps there's another way to achieve the same clearing of the lookup context, but for now, setting the attachment in the mail block works fine like so:
def results_email(participant, program)
mail(:to => participant.email,
:subject => "my subject") do |format|
format.text
format.html
format.pdf do
attachments['trust_quotient_results.pdf'] = WickedPdf.new.pdf_from_string(
render_to_string :pdf => "results",
:template => '/test_sessions/results.pdf.erb',
:layout => 'pdf.html')
end
end
end
Heres' how I fixed this issue:
Removed wicked_pdf
Installed prawn (https://github.com/sandal/prawn/wiki/Using-Prawn-in-Rails)
While Prawn is/was a bit more cumbersome in laying out a document, it can easily sling around mail attachments...
Better to use PDFKit, for example:
class ReportMailer < ApplicationMailer
def report(users:, amounts:)
#users = users
#amounts = amounts
attachments["proveedores.pdf"] = PDFKit.new(
render_to_string(
pdf: 'bcra',
template: 'reports/users.html.haml',
layout: false,
locals: { users: #users }
)
).to_pdf
mail subject: "Report - #{Date.today}"
end
end

Rails3 and Respond_with problem

I have an application, on which I have two user interfaces.
The first one is for normal users and the second one is for iphone users.
Everything was working fine until i refactored my code within controller to use the respond_with declarative instead of respond_to.
The application is still working for the html interface(:format => :html) but not on the iphone interface(:format => :iphone).
On the iphone, when I do the following action (:index, :new, :edit, :show) it works.
But when i do (:create, :update, :destroy), I get errors saying the template is not found(create.iphone.haml for example).
On my controller I have
respond_to :html, :iphone
And then for example, the edit and the update action
def edit
#refund = Refund.find(params[:id])
respond_with(#refund)
end
def update
#refund = Refund.find(params[:id])
if #refund.update_attributes(params[:refund])
flash[:notice] = 'Refund was successfully updated.'
end
respond_with(#refund, :location => project_refunds_path(#project))
end
In fact, I would like the :iphone format is handle as :html is ... and not by calling the to_format method as it is specified into the doc.
Solved it by myself.
Just need to add this to an initializer file :
ActionController::Responder.class_eval do
alias :to_iphone :to_html
end
What if you do:
respond_with(#refund, :location => project_refunds_path(#project)) do |format|
format.iphone { whatever you had here before refactoring }
end