1

How can I use erb within a Module in Sinatra? Here is an example of a complete app, where erb fails within a Module.

require 'sinatra'

get '/yes' do
  erb "<%= Time.now %>" #works
end

get '/no' do 
  MyMod.foo #fails
end

module MyMod
  extend self

  def foo
    erb "<%= Time.now %>" #fails: undefined method `erb' for MyMod:Module
  end
end
sellarafaeli
  • 1,167
  • 2
  • 9
  • 24

4 Answers4

3

The erb method is defined in Sinatra::Templates. So one would think he'd be able to just use that

module MyMod
  extend Sinatra::Templates
  extend self

  def foo
    erb Time.now.to_s
  end
end

However, it's not that simple. Now erb method is found, but still doesn't work

NameError at /no

undefined local variable or method `settings' for MyMod:Module

If you follow that rabbit hole, you'll need to make your module a full sinatra app (don't know if that's even possible).

Why don't you use a modular sinatra app style (create a class which inherits from Sinatra::Base) and include your module into the app? This way I'm quite confident it'll "just work".

Community
  • 1
  • 1
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • Yes, that's the rabbit hole I was going down into... I'll try your suggestion. But isn't there a way to use Sinatra's 'erb' with regular Sinatra modules? – sellarafaeli May 13 '15 at 08:45
  • There's no such thing as "regular sinatra module" :) – Sergio Tulentsev May 13 '15 at 08:45
  • :) I mean, a regular Ruby module. – sellarafaeli May 13 '15 at 08:47
  • `erb` would happily work if you satisfied all its dependencies (like the `settings` method here) and **all their dependencies**. The easiest way is to include said module in a real sinatra app class (then you don't need to include `Sinatra::Templates`, obviously.) – Sergio Tulentsev May 13 '15 at 08:49
  • Why do you even *use* a module here? I see no purpose. – Sergio Tulentsev May 13 '15 at 08:54
  • To encapsulate behavior. I want an 'Emails' module to render HTML emails. Other relevant solutions for this are also appreciated. :) BTW, have been unsuccessful with using a Sinatra app class. – sellarafaeli May 13 '15 at 09:04
2

This may not be the answer you are looking for. Here is my another version. Probably this is close to what you wanted to achieve.

module MyMod  
  extend self

  def foo
    ERB.new("<%= Time.now %>").result   
  end
end

get '/' do
   MyMod.foo
end

or with helpers??

module Sinatra
    module MyMod  
        def foo
            erb "<%= Time.now %>"
        end
    end
    helpers MyMod
end

get '/' do
    foo
end
Bala
  • 11,068
  • 19
  • 67
  • 120
  • you don't need string interpolation for this. – Sergio Tulentsev May 13 '15 at 08:27
  • Yes, I am specifically asking how to render *within the module* rather than returning to the main app before rendering. – sellarafaeli May 13 '15 at 08:31
  • I see you changed it a bit :) - this is indeed closer. I'd rather use Sinatra's own 'erb' (for compatibility); at the very least I'll need to figure out how to pass ERB.new parameters and a layout file (which is basically the main use-case for Sinatra's 'erb'..) – sellarafaeli May 13 '15 at 09:07
  • @sellarafaeli re the parameters, do you mean `ERB.new("#{params['aaa']}").result` ? – barlop Apr 08 '19 at 01:47
  • @sellarafaeli also, that ERB.new line is not sinatra's own ERB. If you start irb, and type require 'erb', then you see the ERB.new line works. And there is even a command line erb. if I do which erb, I see a /usr/local/bin/erb – barlop Apr 08 '19 at 01:59
  • @sellarafaeli and as for another way of passing to the erb, really passing to the erb, see asfer's answer https://stackoverflow.com/questions/1338960/ruby-templates-how-to-pass-variables-into-inlined-erb – barlop Apr 08 '19 at 02:39
1

Extending answer of @Sergio Tulentsev: To imitate rendering behavior of Sinatra controller you can create module like this:

module ErbRender

  include Sinatra::Templates
  include Sinatra::Helpers
  include Sinatra::ContentFor

  def settings
    @settings ||= begin
      settings = Sinatra::Application.settings
      settings.root = "#{ROOT}/app"
      settings
    end
  end

  def template_cache
    @template_cache ||= Tilt::Cache.new
  end

end

Here you may need to tune settings.root

Usage example:

class ArticleIndexingPostBody

  include ErbRender

  def get_body
    erb :'amp/articles/show', layout: :'amp/layout'
  end

end

This will properly render templates with layouts including content_for

Daniel Garmoshka
  • 5,849
  • 39
  • 40
0

Solution - call erb on a Sinatra::Application instance, as below (two ways of calling it, second one is preferable).

require 'sinatra'

get '/yes' do
  erb "<%= Time.now %>" #works
end

get '/foo' do 
  MyMod.foo #works
end

get '/foo2' do
  MyMod.foo2 #works 
end

module MyMod 
  extend self

  def foo
    ObjectSpace.each_object(Sinatra::Application).first.erb "<%= Time.now %>"
  end

  def foo2
    @app ||= Sinatra::Application.new.instance_variable_get :@instance
    @app.erb "<%= Time.now %>"
  end 
end
sellarafaeli
  • 1,167
  • 2
  • 9
  • 24