Eric Luo Eric Luo - 6 months ago 9
Ruby Question

How to attach dynamic file content in email

I want to attach a dynamic file in my email. I have the following code:

attachments["#{@company}likelist.xls"] = File.read("#{Rails.root}/app/views/products/like_list.xls.erb")


like_list.xls.erb
is a dynamic HTML table created by iterating a variable in Ruby. I used
respond_to format.xls
to convert the
like_list.html.erb
table to XLS, however, the attachment sent is empty as no Ruby code is executed.
File.read
only reads static files.

like_list.xls.erb
looks like this:

<table border="1">
<thead>
<tr>
<th><%= t ('product_table.name')%></th>
<th><%= t ('product_table.code')%></th>
</tr>
</thead>
<tbody>
<% @like_list.each do |key, value| %>
<% value.each do |t| %>
<tr>
<td><%=t.code%></td>
<td><%=key%></td>
...


Is there any way I can attach a dynamic file in Rails 4?

Answer

I assume that's because you sending just a template. But you should construct a table first.

I don't know what data type is your @like_list is, but let's say it is a Hash.

In that case you should build your attachment. Here is just an example that shows the basic approach:

  # mailer
  def table_deliver
    to = "user@example.com"
    from = "no-reply@example.com"
    subject = "Foo"

    @company = "Ggl"
    @like_list = { foo: 1,
                   bar: 2,
                   baz: 3 }
    attachments["#{@company}likelist.xls"] = build_table(@like_list)
    @body = "Mail body"
    mail(to: to, from: from, subject: subject)
  end

  private

  def build_table(data)
    opts = OpenStruct.new(data)
    template = ERB.new(File.read("#{Rails.root}/app/views/products/like_list.xls.erb"))
    template.result(opts.instance_eval { binding })
  end

# template

<table>
  <thead>
    <th>foo</th>
    <th>bar</th>
  </thead>
  <tbody>
    <% data.each do |k, v| %>
      <tr>
        <td><%= k %></td>
        <td><%= v %></td>
      </tr>
    <% end %>
  </tbody>
</table>

And the result in letter_opener:

email

Attachment opened in libre office

office

Hope that helps and feel free to ask some additional questions.

UPDATE

First you create an OpenStruct from your Hash params. Then create a new ERB class instance.

result is a method of ERB class, that applies params to existing template.

binding is an object that provides access to the instance methods and variables that are owned by another object.

More info here

instance_eval is a Object method that evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj).

More info here

So, that code do the following

  • creates some struct with key-value pairs
  • creates new template
  • executes binding object in context of template and assigns key-value pairs to the template.

You could refer to this SO post for more details

Comments