I would like to convert the following erb code into slim.
<% begin %>
<%= some_function %>
<% rescue Exception %>
<%= some_other_function %>
<% end%>
My approach is:
- begin
= some_function
- rescue Exception
= some_other_function
But that gives an error:
index.slim:34: syntax error, unexpected keyword_ensure, expecting $end
How do I rescue exceptions properly using slim?
You need to make a Helper.
It's in that helper that you should put begin/rescue logic.
# my_helper.rb
class MyHelper
def my_func
begin
some_function
rescue
some_other_func
end
end
end
# slim view
= my_func
This was actually a bug in slim and is fixed in slim 1.3.7 upwards (https://github.com/slim-template/slim/commit/e4df090c2c82c3563bcc4e625cbd6ab55a60caf8)
The syntax now works exactly as expected. No helper method nor indenting is necessary.
Related
Note: I'm not using rails, sinatra, or tilt, just ruby's built in ERB.
Suppose I have two erb files:
file1.erb:
Start
<%= yield if block_given? -%>
End
and file2.erb:
<% render('file1.erb') do %>
Some text
<% end %>
I'd like to get output that looks like:
Start
Some text
End
However, with the following ruby code:
require 'erb'
def render(file)
content = File.read(file)
b = binding
ERB.new(content, trim_mode: '-').result b
end
res = render('file2.erb')
I only get "Some Text". And if I change the first line of file2.erb to use <%= instead of <% then I get a syntax error:
Traceback (most recent call last):
3: from test.rb:9:in `<main>'
2: from test.rb:6:in `render'
1: from /usr/lib/ruby/2.7.0/erb.rb:905:in `result'
/usr/lib/ruby/2.7.0/erb.rb:905:in `eval': (erb):1: syntax error, unexpected ')' (SyntaxError)
...t.<<(( render('file1.erb') do ).to_s); _erbout.<< "\\n Some T...
... ^
(erb):3: syntax error, unexpected `end', expecting ')'
; end ; _erbout.<< "\\n".freeze
^~~
(erb):4: syntax error, unexpected end-of-input, expecting ')'
Is there some way to get this to work with the 'erb' module?
I can get the yielding to work if I use a block that returns an expression directly, but that won't work for my case.
Another answer has a solution where the calling code can render a template inside a different layout. But that doesn't really work for me, because I need to define the layout to use inside the template itself (there isn't really an equivalent of a controller in my code).
I thought of maybe doing something like, changing .result to .run, and running it like:
$stdout = outstream
render('file2.erb')
But for some reason the results in the output:
Start
Some Text
End
Some Text
Note the extra "Some Text" at the end.
As you already poined, since we are not outputing the render method return value with <%= the in first case we are only getting Some text in output the rest is just used in processing.
In second when you used run with template which is causing the result to run twice, first time for template which is called in first script file and then second time for template which is called inside file2.erb render.
To solve the problem you can capture the output in a variable and then print the output from the captured variable as below:
file2.erb
<% #result = render('file1.erb') do -%>
<% "Some text\n" -%>
<% end -%>
<%= #result -%>
Output
Start
Some Text
End
Note:
You can use HEREDOC to put large amount of text between <% -%>
<% #result = render('file1.erb') do -%>
<%
<<~EOF
Some Text
EOF
-%>
<% end -%>
<%= #result -%>
Ruby newbie here who just started using Ruby with .erb templates and I'm having a problem with the code. I have a hangman game that's passing variables from the .rb file to the .erb file and everything was working fine until I tried to check it on initial load (no variables present) and it threw errors. So I figured I'd use defined? with an if statement to check if the variable exists and then execute the code if it does and ignore if doesn't. It works fine when I use:
<% if defined?bad_guesses %>
<%= bad_guesses %>
<% end %>
But the information I need is an array and when I try to use an .each or .times statement like this:
<% if defined?bad_guesses %>
<% bad_guesses.each do |i| %>
<%= i %>
<% end %>
<% end %>
I get:
NoMethodError at /
undefined method `each' for nil:NilClass
C:/Projects/hangman/views/index.erb in block in singleton class
<% bad_guesses.each do |i| %> hangman.rb in block in
erb :index, :locals => {:game_status => game_status, :bad_guesses => bad_guesses, :good_guesses => good_guesses, :word => word}
Any suggestions appreciated.
Also, is this even the proper way to do this? When you make an .erb template that uses variables passed in from a class in your .rb file, how do you ignore it until it exists to the template?
Passing variables using:
get '/' do
if params['make'] != nil
make = params['make'].to_i
game_status, bad_guesses, good_guesses, word = Hangman.get_word(make)
elsif params['guess'] != nil
guess = params['guess'].to_s
game_status, bad_guesses, good_guesses, word = Hangman.check_guess(guess)
end
erb :index, :locals => {:game_status => game_status, :bad_guesses => bad_guesses, :good_guesses => good_guesses, :word => word}
end
Looking at this:
<% if defined?bad_guesses %>
<% bad_guesses.each do |i| %>
<%= i %>
<% end %>
<% end %>
a few points:
defined?a is bad style; use defined?(bad_guesses) or defined? bad_guesses instead.
defined? checks if it's defined, so if you say foo = nil; defined? foo it will be true.
You could alternatively use this:
defined?(bad_guesses) && bad_guesses
On the other hand, undefined instance variables are nil by default:
# it shows undefined
defined? #non_existing_var
# but you can still check if it's truthy:
puts "found" if #non_existing_var
# it won't print anything
Similar to instance variables in this regard are hashes. The default value of an unknown key is nil.
The problem with instance variables is they are not scoped to the partial. Instead, I recommend sending your local variables as a nested hash:
locals: { data: { foo: "bar" } }
Then you can safely check for values which may not exist:
if data[:foo]
# this runs
elsif data[:non_existent]
# this doesnt run
end
For my purposes, the following syntax worked:
<% if some_var %>
<%= some_var %>
<% end %>
This block is rendered if some_var is not nil.
I would like to extend ERB so every output tag - <%= %> - content is pre-processed before the result is rendered.
For example,
<%= 'test' %>
should now render
!test!
instead of
test
How can I do this ?
Something like this? (untested)
require 'erb'
template = File.read(template_file)
template.gsub!(/<%=(.*?)%>/, '!\1!')
erb = ERB.new(template)
result = erb.result
There is no straightforward way to do that. Perhaps you can define:
class String; def bang; "!#{self}!" end end
and do
<%= "test".bang %>
I have the following .erb view in a Sinatra app:
<% sessions.each do |session| %>
<%= session.balance_beginning %>
<%= session.balance_ending %>
<% end %>
It works as expected, displaying the beginning and ending balances recorded for each session. I would like to calculate the net balances from within the .erb file, but I can't figure out how to do it. I have tried variations of this:
<% sessions.each do |session| %>
<%= session.balance_ending - session.balance_beginning %>
<% end %>
That doesn't work. I receive the following error in Sinatra:
undefined method `-' for nil:NilClass
How do I do what I'm trying to do?
Right #Zabba, in this case I think you would add a method to your Session model so you could call session.net_balance.
Then in your balance_ending and balance_beginning methods you would want to handle nil, either raise an error or return zero if that is valid.
If I don't handle view correctly, Production environment show 500.
<%= image_tag post.user.image_url %>
This could be
<%= image_tag post.user.image_url if post.user && post.user.image_url %>
but I am little careless and forgot this issue several times.
How can I prevent this? How can I use <%= image_tag nil %> in production environment without raising 500?
image_tag must have a source, Rails can do nothing with it, but raise an exception.
You can write a helper like this:
module ApplicationHelper
def safe_image_tag(source, options = {})
source ||= "default.jpg"
image_tag(source, options)
end
end
or simply check for nil directly in a view. Anyway you have to do something to prevent an error.