class BootstrapEmail::Compiler

Public Class Methods

new(mail) click to toggle source
# File lib/bootstrap-email.rb, line 11
def initialize(mail)
  @mail = mail
  @source = mail.html_part || mail
  update_doc(@source.body.raw_source)
end

Public Instance Methods

compile_html!() click to toggle source
# File lib/bootstrap-email.rb, line 28
def compile_html!
  button
  badge
  alert
  card
  hr
  container
  grid
  align
  padding
  margin
  spacer
  table
  body
end
inject_head!() click to toggle source
# File lib/bootstrap-email.rb, line 51
def inject_head!
  @doc.at_css('head').add_child(bootstrap_email_head)
end
inline_css!() click to toggle source
# File lib/bootstrap-email.rb, line 44
def inline_css!
  @source.body = @doc.to_html
  @mail = Premailer::Rails::Hook.perform(@mail)
  @mail.header[:skip_premailer] = true
  update_doc(@mail.html_part.body.raw_source)
end
perform_full_compile() click to toggle source
# File lib/bootstrap-email.rb, line 21
def perform_full_compile
  compile_html!
  inline_css!
  inject_head!
  update_mailer!
end
update_doc(source) click to toggle source
# File lib/bootstrap-email.rb, line 17
def update_doc(source)
  @doc = Nokogiri::HTML(source)
end
update_mailer!() click to toggle source
# File lib/bootstrap-email.rb, line 55
def update_mailer!
  @mail.html_part.body = @doc.to_html
  @mail
end

Private Instance Methods

alert() click to toggle source
# File lib/bootstrap-email.rb, line 95
def alert
  each_node('.alert') do |node| # move all classes up and remove all classes from the element
    node.replace(template('table', classes: node['class'], contents: node.delete('class') && node.to_html))
  end
end
align() click to toggle source
# File lib/bootstrap-email.rb, line 101
def align
  each_node('.float-left') do |node|
    align_helper(node, /float-left/, 'left')
  end
  each_node('.mx-auto') do |node|
    align_helper(node, /mx-auto/, 'center')
  end
  each_node('.float-right') do |node|
    align_helper(node, /float-right/, 'right')
  end
end
align_helper(node, klass, template) click to toggle source
# File lib/bootstrap-email.rb, line 113
def align_helper(node, klass, template)
  if node.name != 'table' # if it is already on a table, set the proprieties on the current table
    node['class'] = node['class'].sub(klass, '')
    node.replace(template("align-#{template}", contents: node.to_html))
  else
    node['align'] = template
  end
end
badge() click to toggle source
# File lib/bootstrap-email.rb, line 89
def badge
  each_node('.badge') do |node| # move all classes up and remove all classes from the element
    node.replace(template('table-left', classes: node['class'], contents: node.delete('class') && node.to_html))
  end
end
body() click to toggle source
# File lib/bootstrap-email.rb, line 208
def body
  @doc.css('body').each do |node|
    node.replace('<body>' + preview_text.to_s + template('body', classes: "#{node['class']} body", contents: node.inner_html.to_s) + '</body>')
  end
end
bootstrap_email_head() click to toggle source
# File lib/bootstrap-email.rb, line 62
    def bootstrap_email_head
      engine = defined?(SassC::Engine).nil? ? Sass::Engine : SassC::Engine
      html_string = <<-HEREDOC
        <style type="text/css">
          #{engine.new(File.open(File.expand_path('../core/head.scss', __dir__)).read, syntax: :scss, style: :compressed, cache: false, read_cache: false).render}
        </style>
      HEREDOC
      html_string
    end
button() click to toggle source
# File lib/bootstrap-email.rb, line 83
def button
  each_node('.btn') do |node| # move all classes up and remove all classes from the element
    node.replace(template('table', classes: node['class'], contents: node.delete('class') && node.to_html))
  end
end
card() click to toggle source
# File lib/bootstrap-email.rb, line 122
def card
  each_node('.card') do |node| # move all classes up and remove all classes from element
    node.replace(template('table', classes: node['class'], contents: node.delete('class') && node.to_html))
  end
  each_node('.card-body') do |node| # move all classes up and remove all classes from element
    node.replace(template('table', classes: node['class'], contents: node.delete('class') && node.to_html))
  end
end
container() click to toggle source
# File lib/bootstrap-email.rb, line 137
def container
  each_node('.container') do |node|
    node.replace(template('container', classes: node['class'], contents: node.inner_html))
  end
  each_node('.container-fluid') do |node|
    node.replace(template('table', classes: node['class'], contents: node.inner_html))
  end
end
each_node(css_lookup, &blk) click to toggle source
# File lib/bootstrap-email.rb, line 78
def each_node(css_lookup, &blk)
  # sort by youngest child and traverse backwards up the tree
  @doc.css(css_lookup).sort_by { |n| n.ancestors.size }.reverse!.each(&blk)
end
grid() click to toggle source
# File lib/bootstrap-email.rb, line 146
def grid
  each_node('.row') do |node|
    node.replace(template('row', classes: node['class'], contents: node.inner_html))
  end
  each_node('*[class*=col]') do |node|
    node.replace(template('col', classes: node['class'], contents: node.inner_html))
  end
end
hr() click to toggle source
# File lib/bootstrap-email.rb, line 131
def hr
  each_node('hr') do |node| # drop hr in place of current
    node.replace(template('hr', classes: "hr #{node['class']}"))
  end
end
margin() click to toggle source
# File lib/bootstrap-email.rb, line 166
def margin
  each_node('*[class*=my-], *[class*=mt-], *[class*=mb-]') do |node|
    top_class = node['class'][/m[ty]{1}-(lg-)?(\d)/]
    bottom_class = node['class'][/m[by]{1}-(lg-)?(\d)/]
    node['class'] = node['class'].gsub(/(m[tby]{1}-(lg-)?\d)/, '')
    html = ''
    if top_class
      html += template('div', classes: "s-#{top_class.gsub(/m[ty]{1}-/, '')}", contents: nil)
    end
    html += node.to_html
    if bottom_class
      html += template('div', classes: "s-#{bottom_class.gsub(/m[by]{1}-/, '')}", contents: nil)
    end
    node.replace(html)
  end
end
padding() click to toggle source
# File lib/bootstrap-email.rb, line 155
def padding
  each_node('*[class*=p-], *[class*=pt-], *[class*=pr-], *[class*=pb-], *[class*=pl-], *[class*=px-], *[class*=py-]') do |node|
    next unless node.name != 'table' # if it is already on a table, set the padding on the table, else wrap the content in a table

    padding_regex = /(p[trblxy]?-\d)/
    classes = node['class'].scan(padding_regex).join(' ')
    node['class'] = node['class'].gsub(padding_regex, '')
    node.replace(template('table', classes: classes, contents: node.to_html))
  end
end
preview_text() click to toggle source
# File lib/bootstrap-email.rb, line 214
def preview_text
  preview_node = @doc.at_css('preview')
  return if preview_node.blank?

  # apply spacing after the text max of 100 characters so it doesn't show body text
  preview_node.content += '&nbsp;' * [(100 - preview_node.content.length.to_i), 0].max
  node = template('div', classes: 'preview', contents: preview_node.content)
  preview_node.remove
  node
end
spacer() click to toggle source
# File lib/bootstrap-email.rb, line 183
def spacer
  spacers = {
    '0' => 0,
    '1' => (16 * 0.25),
    '2' => (16 * 0.5),
    '3' => 16,
    '4' => (16 * 1.5),
    '5' => (16 * 3)
  }
  each_node('*[class*=s-]') do |node|
    temp = Nokogiri::HTML::DocumentFragment.parse(template('table', classes: node['class'] + ' w-100', contents: '&nbsp;'))
    temp.at_css('td')['height'] = spacers[node['class'].gsub(/s-/, '')].to_i
    node.replace(temp)
  end
end
table() click to toggle source
# File lib/bootstrap-email.rb, line 199
def table
  @doc.css('table').each do |node|
    # border="0" cellpadding="0" cellspacing="0"
    node['border'] = 0
    node['cellpadding'] = 0
    node['cellspacing'] = 0
  end
end
template(file, locals_hash = {}) click to toggle source
# File lib/bootstrap-email.rb, line 72
def template(file, locals_hash = {})
  namespace = OpenStruct.new(locals_hash)
  template_html = File.open(File.expand_path("../core/templates/#{file}.html.erb", __dir__)).read
  ERB.new(template_html).result(namespace.instance_eval { binding })
end