Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Render a partial view outside controller

Tags:

ruby

erb

Context

I've written an internal standalone gem that generates PDF with Gotenberg rendering an ERB HTML template dynamically. This permits to actually generate a PDF outside a controller context for any possible reason, like reports etc.

This is achieved with inline code. For example:

ERB.new(template).result(binding)

Problem

When using render inside a template, i get undefined method 'render', cause of course it's not in the current binding. My goal is to use the existing partial rendering process.

Consider having these three templates:

_header.html.erb

<h1>My Header</h1>

_footer.html.erb

<p>My Footer</p>

layout.html.erb

<body>
  <%= render 'header' %>
  <%= yield %>
  <%= render 'footer' %>
</body>

Is there any way to achieve this?

like image 862
sheva Avatar asked Oct 28 '25 19:10

sheva


1 Answers

Not completely sure about how you intend to use it but, this works for similar needs.
Assuming a folder structure like this:

.
├── app.rb
└── views
    ├── _footer.erb
    ├── _header.erb
    ├── _index.erb
    └── layout.erb

...You can build a small templating engine in ERB like this, inside app.rb

require 'erb'
require 'date'

class SimpleTemplate
  attr_reader :views
  
  def initialize(views: 'views')
    @views = views
  end

  def render(template_name, locals = {})
    path = File.join(views, "#{template_name}.erb")
    html = erb(path, locals)
    layout = File.join(views,"layout.erb")
    File.exist?(layout) ? erb(layout, locals.merge(content: html)) : html
  end

  private

  def erb(path, locals)
    return "" unless File.exist?(path)
    b = binding
    locals.transform_keys(&:to_sym).each { |k, v| b.local_variable_set(k, v) }
    ERB.new(File.read(path), trim_mode: '-').result(b)
  end

  def render_partial(name)
    path = "#{views}/#{name}"
    File.exist?(path) ? erb(path, {}) : ""
  end
end

Test it with puts SimpleTemplate.new.render('_index')

The content of views/_index.erb

<main>
  <section>
    <p>Contenuto principale della pagina.</p>
    <p>Oggi è <%= Date.today %>.</p>
  </section>
</main>

... views/layout.erb

<!DOCTYPE html>
<html lang="it">
<head>
  <meta charset="UTF-8">
  <title>Il mio sito</title>
</head>
<body>
  <%= render_partial('_header.erb') %>
  <%= content %>
  <%= render_partial('_footer.erb') %>
</body>
</html>
like image 193
microspino Avatar answered Oct 30 '25 09:10

microspino