If Statement inside Sinatra template - ruby

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.

Related

passing return (string) value into erb input value

Problem:
I want it to be able to pass the return value into an HTML ERB page(see result form below). I have tried many of the solutions on here and other sites and have yet to find one that fixes my problem. I included the full code in case I missed something.
NOTE: I get the return value and can bring up the results form but the return value is not passing. I already posted this but solution given did NOT help. This includes the change recommended to me. It appears adding #{output} so the redirect in the main code causes a "The connection was reset" error which makes no sense what-so-ever.
Main Code:
file name: */projects/webhosted_custom_fibonacci_calculator.rb
require "rubygems"
require "sinatra"
require_relative 'fibonacci_calculator.rb'
require "erb"
include Calculator
get '/' do
redirect ("/calculate")
end
get '/calculate' do
erb :calculator_form, :locals => {:calculator => session[:calculator]}
end
post '/calculate' do
num1 = params['firstnum'].to_i
num2 = params['secondnum'].to_i
output = Calculator.run(num1, num2)
redirect ("/results_form?results=#{output}")
end
get '/results_form' do
erb :results_form, :locals => {:results => params[:results]}
end
Result form:
File name: */projects/views/results_form.erb
<html>
<head>
<title>Fibonacci Calculator</title>
</head>
<body>
<h1>Results</h1>
Result: <%= results %>
</body>
</html>

Array for errors in sinatra

To manage errors in my address book app i initialize an array like this
err = Array.new
and then when i post something it checks if there are empty fields. If yes, for each empty field it adds a record in the array, and then redirect to /add page, like this
post '/' do
if params[:fname] == ""
err.push "Insert a valid first name"
end
if params[:lname] == ""
err.push "insert a valid last name"
end
if params[:phone] == ""
err.push "insert a valid phone number"
end
if params[:mail] == ""
err.push "insert a valid e-mail address"
end
if err.empty?
c = Contatto.new
c.fname = params[:fname]
c.lname = params[:lname]
c.phone = params[:phone]
c.mail = params[:mail]
c.save
redirect '/'
else
redirect '/add'
end
end
then the add page reads if the array has any record and if yes, cycles it to print each message
get '/add' do
#err = err
#title = 'Aggiungi'
erb :aggiungi
end
<% if #err.any? %>
<div class="error">
<% #err.each do |err| %>
<%= err %><br>
<% end %>
</div>
<% end %>
i think the error is that it re-initialize the array every time it changes from post '/' to get '/add' and so the result is an empty array...
How can i solve? thank you everyone!
If you want data for a specific visitor to persist between requests you need to be storing the error array in either a session or a cookie (session probably makes the most sense).
Luckily sessions in Sinatra are pretty easy: http://www.sinatrarb.com/intro#Using%20Sessions . Once enabled you can put pretty much anything you want into the session hash, so initializing with session[:errors] = [] and pushing with session[:errors] << "An error" should give you the persistence you are looking for.
You're creating a local variable and expecting it to persist between requests. How is this supposed to happen? Sinatra is not psychic, it will only remember what you tell it to remember, and that's usually done through some kind of database or a client-side cookie.
Generally you should render a response page on failure, making use of the errors you've collected, or redirect on success, where the empty errors array is not relevant.
As a matter of style, the more Ruby way to do things like this is:
err = [ ] # Equivalent to Array.new
err << "Example error" # Equivalent to err.push

Possible to put a conditional statement inside a here document?

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)

Is sprintf incompatible with sinatra?

Say I have this:
class Account
...
property :charge, Decimal, :precision => 7, :scale => 2
...
classy stuff
...
def self.balance(prefix)
x = Account.get(prefix.to_sym).order(:fields => [:charge]).sum(:charge)
sprintf("%5.2f", x)
end
end
(Edit: The value of all :charge fields is 0.13E2 (0.1E2 + 0.3E1). This is correctly returned. Only in a View does it seem to get borked from sprintf)
In IRB Account.balance(:AAA) returns => "13.00"
if I call Account.balance(:AAA) from a view I get TypeError at /accounts
can't convert nil into Float
Account.balance(:AAA) works anywhere I call it except in a view. If I remove sprintf("%5.2f", x) I get 0.13E2 in my view. (using Account.balance(:AAA).to_f in a view gives me 13.0)
Is sinatra incompatible with sprintf? or am I not understanding how to use sprintf?
(Edit: This is the offending view:)
<section>
<% #accounts.each do |account| %>
<article>
<h2><%= account.prefix %></h2>
<span><p>This account belongs to <%= account.name %> & has a balance of $<%= Account.balance(account.prefix) %>.</p></span>
</article>
<% end %>
</section>
Wouldn't it make more sense to define balance as an instance method rather than a class method? It looks from your example like you're calling balance in an account-specific way anyway, so why not make it:
# the model
class Account
#...
def balance
amount = self.order(:fields => [:charge]).sum(:charge)
sprintf "%5.2f", amount
# or the infix version:
"%5.2f" % amount
end
end
,
# the view
...balance of $<%= account.balance %>...
I know that this doesn't address sprintf per se, but the problem is more likely to be coming from the slightly convoluted lookup than from a built-in method. Even if my specific code doesn't suit your application, it might be worth simplifying the lookup step, even if that involves a few more lines of code.
The advantage of this approach is that there is no doubt that you'll be getting the right Account record.
tested it with a little sinatra app and it worked for me
app.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
get '/' do
#x = 10.23
erb :index
end
views/index.erb
<%= sprintf("%5.2f", #x) %>
output:
10.23
ruby 1.9.2 / sinatra 1.3.1
I think there is another error before the sprintf because of your error message:
can't convert nil into Float
seems like your x is nil. try to be sure that x is not nil there, then sprintf should work as expected.

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

Resources