Possible to put a conditional statement inside a here document? - ruby

I know we can do things like this:
puts <<START
----Some documents
#{if true
"yesyesyesyesyesyesyesyesyesyes"
else
"nonononononononononononononono"
end}
----Some documents
START
But is it possible to do like this:
puts <<START
----Some documents
#{if true}
yesyesyesyesyesyesyesyesyesyes
#{else}
nonononononononononononononono
#{end}
----Some documents
START
Why I want this is because I hate single/double-quotes in here document, avoiding them will make the document clearer
anyone can help?
thanks!

Maybe you actually want to use ERB if the intention is to perform templating. ERB will support splitting the if/else fine:
require 'erb'
template = ERB.new <<-DOC
----Some documents
<% if true %>
yesyesyesyesyesyesyesyesyesyes
<% else %>
nonononononononononononononono
<% end %>
----Some documents
DOC
string = template.result(binding)

I'll give the alternative I would favour, which is to use heredocs assigned to variables that are then inserted in a master heredoc, as it gets the conditional outside of the heredoc, thus giving the better clarity you're looking for (especially when things start getting more complicated than a contrived example):
cond = if true
<<TRUE
yesyesyesyesyesyesyesyesyesyes
TRUE
else
<<NOTTRUE
nonononononononononononononono
NOTTRUE
end.strip
puts <<START
----Some documents
#{cond}
----Some documents
START
If you're looking for a template then there are plenty out there, and plenty better than ERB in my opinion (start with looking at Haml).

You could consider nested heredocs:
puts <<EOF
---- Some documents
#{if true; <<WHENTRUE
yesyesyes
WHENTRUE
else <<WHENFALSE
nonono
WHENFALSE
end
}---- Some documents
EOF
Note that you need to place the closing } on the beginning of the line or you will have an extra empty line.
Edit: You could avoid that and perhaps get a bit nicer syntax by using a little helper function:
def if_text(condition, whentrue, whenfalse)
(condition ? whentrue : whenfalse).chomp
end
puts <<EOF
---- Some documents
#{if_text(true, <<ELSE, <<ENDIF)
yesyesyes
ELSE
nonono
ENDIF
}
---- Some documents
EOF

You could use ERB if you really wanted something like that:
str = <<-ERB
----Some documents
<% if true %>
yesyesyesyesyesyesyesyesyesyes
<% else %>
nonononononononononononononono
<% end %>
----Some documents
ERB
erb = ERB.new(str, nil, '<>');
puts erb.result(binding)

Related

Middleman Frontmatter YAML list

I just want to make a simple each loop in my Middleman helper, datas are stored in my page'Frontmatter like this :
dir:
- test
- test2
So in my helper, I try to write my loop :
def translate_directory
current_page.data.dir.each do |dir|
dir
end
end
call my method in my page
<%= translate_directory %>
and this is what's display :
["test", "test2"]
But now, if I make the same loop in my page, write with ERB syntax :
<% current_page.data.dir.each do |x| %>
<%= x %>
<% end %>
the exit is the following
test test2
separated in two strings, so exactly what I want.
EDIT : when I puts the helper'method, it display the two strings in two lines, so in two separated strings. Don't understand why it appear as an array on my browser.
EDIT 2 : a little thing I forgot, I want to translate each word with I18n.translate, like this :
def path_translate
current_page.data.dir.each { |dir| t("paths.#{dir}", locale: lang) }
end
but i can't because the each method doesn't work so I18n can't translate each word.
Because your helper is returning an array not a interpolated string like the ERB template is doing. Try the following for your helper:
def translate_directory
current_page.data.dir.join(' ')
end
My bad. Using .map instead of .each fix the problem, then use .join makes the array a big string.

If Statement inside Sinatra template

I'd like to to show a message only if on a specific route/page. Essentially, if on /route display a message.
I tried going through the Sinatra Docs, but I can't find a specific way to do it. Is there a Ruby method that will make this work?
EDIT: Here's an example of what I'd like to do.
get '/' do
erb :index
end
get '/page1' do
erb :page1
end
get '/page2' do
erb :page2
end
*******************
<!-- Layout File -->
<html>
<head>
<title></title>
</head>
<body>
<% if this page is 'page1' do something %>
<% else do something else %>
<% end %>
<%= yield %>
</body>
</html>
No idea what how to target the current page using Ruby/Sinatra and structure it into an if statement.
There are several ways to approach this (and BTW, I'm going to use Haml even though you've used ERB because it's less typing for me and plainly an improvement). Most of them rely on the request helper, most often it will be request.path_info.
Conditional within a view.
Within any view, not just a layout:
%p
- if request.path_info == "/page1"
= "You are on page1"
- else
= "You are not on page1, but on #{request.path_info[1..]}"
%p= request.path_info == "/page1" ? "PAGE1!!!" : "NOT PAGE1!!!"
A conditional with a route.
get "/page1" do
# you are on page1
message = "This is page 1"
# you can use an instance variable if you want,
# but reducing scope is a best practice and very easy.
erb :page1, :locals => { message: message }
end
get "/page2" do
message = nil # not needed, but this is a silly example
erb :page2, :locals => { message: message }
end
get %r{/page(\d+)} do |digits|
# you'd never reach this with a 1 as the digit, but again, this is an example
message = "Page 1" if digits == "1"
erb :page_any, :locals => { message: message }
end
# page1.erb
%p= message unless message.nil?
A before block.
before do
#message = "Page1" if request.path_info == "/page1"
end
# page1.erb
%p= #message unless #message.nil?
or even better
before "/page1" do
#message = "Hello, this is page 1"
end
or better again
before do
#message = request.path_info == "/page1" ? "PAGE 1!" : "NOT PAGE 1!!"
end
# page1.erb
%p= #message
I would also suggest you take a look at Sinatra Partial if you're looking to do this, as it's a lot easier to handle splitting up views when you have a helper ready made for the job.
Sinatra has no "controller#action" Rail's like concept, so you wont find a way to instantiate the current route. In any case, you can check request.path.split('/').last to get a relative idea of what is the current route.
However, if you want something so be shown only if request.path == "x", a much better way is to put that content on the template, unless that content has to be rendered in a different place within your layout. In that case you can use something like Rail's content_for. Check sinatra-content-for.

Ruby, better way to implement conditional iteration than this?

I have an array #cities = ["Vienna", "Barcelona", "Paris"];
and I am trying to display the individual items with a spacer in between. However it is possible that there is only 1 element in the array, in which case I do not want to display the spacer. And also the array could be empty, in which case I want to display nothing.
For the above array I want the following output:
Vienna
-----
Barcelona
-----
Paris
I use an erb template cityview to apply formatting, css, etc before actually printing the city names. Simplified, it looks like this:
<p><%= #cities[#city_id] %></p>
I have implemented it as follows...
unless #array.empty?
#city_id = 0;
erb :cityview
end
unless #array[1..-1].nil?
#array[1..-1].each_index do |i|
#city_id = i+1;
puts "<p>-------</p>";
erb :cityview
end
end
Is there a better way?
#cities.join("<p>--------</p>")
Edit to address the template
Here I'm assuming that there's an erbs method that returns the rendered template without doing a puts. Returning the string allows easier manipulation and reuse.
#cities.map { |c| #city = c; erb :cityview }.join("<p>--------</p>")
I'd prefer:
erb:
<p><%= #city %></p>
and loop
#array.each_with_index do |e, i|
#city = e
erb :cityview
puts "<p>-------</p>" if i < #array.length - 1
end
I assume you have split the erb, bit because you want to customize it.
If you want to mix HTML with your city names then you'll need to worry about HTML encoding things before you mix in your HTML. Using just the standard library:
require 'cgi'
html = #cities.map { |c| CGI.escapeHTML(c) }.join('<p>-----</p>')
If you're in Rails, then you can use html_escape from ERB::Util and mark the result as safe-for-HTML with html_safe to avoid having to worry about the encoding in your view:
include ERB::Util
html = #cities.map { |c| html_escape(c) }.join('<p>-----</p>').html_safe
The simpler solution would be to use a spacer template.
http://guides.rubyonrails.org/layouts_and_rendering.html#spacer-templates

Array of Ruby objects returning strings on each method. Why?

Useful additional info: I am using the decent_exposure gem so this might be the issue - correcting the code below:
expose(:get_filter_tags) do
if params[:filter_tag_names]
filter_tag_names = Array(params[:filter_tag_names].split(" "))
filter_tags = Array.new
filter_tag_names.each do |f|
t = Tag.find_by_name(f)
filter_tags << t
end
end
end
So, something funny happens when I call this in the view:
query string ?utf8=✓&filter_tag_names=test
<% get_filter_tags.each do |ft| %>
<%= ft.name %>
<% end %>
Error message: undefined method `name' for "test":String
Why is this trying to call name on a string not a Tag object? If I put the following in the view, and have jut one filter_tag_names item
def getfiltertag
Tag.find_by_name(params[:filter_tag_names])
end
#view
<%= getfiltertag.name %>
query string: ?utf8=✓&filter=test
like above then I can call name just fine, so obviously I am doing something wrong to get an array of strings instead of objects. I just don't know what. Any suggestions?
Your problem is that each returns self — so if you write filter_tag_names.each, it returns filter_tag_names. You could fix this by explicitly returning filter_tags, but more idiomatically, you could just rewrite it as:
expose(:get_filter_tags) do
if params[:filter_tag_names]
filter_tag_names = Array(params[:filter_tag_names].split(" "))
filter_tag_names.map {|f| Tag.find_by_name(f) }
end
end
Just as an aside, this method will return nil if there aren't any filter tag names. You may want to do that, or you might want to return an empty collection to avoid exceptions in the calling code.

Rails 3 refactoring issue

The following view code generates a series of links with totals (as expected):
<% #jobs.group_by(&:employer_name).sort.each do |employer, jobs| %>
<%= link_to employer, jobs_path() %> <%= "(#{jobs.length})" %>
<% end %>
However, when I refactor the view's code and move the logic to a helper, the code doesn't work as expect.
view:
<%= employer_filter(#jobs_clone) %>
helper:
def employer_filter(jobs)
jobs.group_by(&:employer_name).sort.each do |employer,jobs|
link_to employer, jobs_path()
end
end
The following output is generated:
<Job:0x10342e628>#<Job:0x10342e588>#<Job:0x10342e2e0>Employer A#<Job:0x10342e1c8>Employer B#<Job:0x10342e0d8>Employer C#<Job:0x10342ded0>Employer D#
What am I not understanding? At first blush, the code seems to be equivalent.
In the first example, it is directly outputting to erb, in the second example it is returning the result of that method.
Try this:
def employer_filter(jobs)
employer_filter = ""
jobs.group_by(&:employer_name).sort.each do |employer,jobs|
employer_filter += link_to(employer, jobs_path())
end
employer_filter
end
Then call it like this in the view:
raw(employer_filter(jobs))
Also note the use of "raw". Once you move generation of a string out of the template you need to tell rails that you don't want it html escaped.
For extra credit, you could use the "inject" command instead of explicitly building the string, but I am lazy and wanted to give you what I know would work w/o testing.
This syntax worked as I hoped it would:
def employer_filter(jobs_clone)
jobs_clone.group_by(&:employer_name).sort.collect { |group,items|
link_to( group, jobs_path() ) + " (#{items.length})"
}.join(' | ').html_safe
end

Resources