I'm calling a ruby function in a post method and I'm trying to output the contents from the function to the web page but it prints the output in my console instead. How do I get it to print to the page?
I've tried
<%=rsg(params[:grammar_file])%> inside an erb file
and
rsg(params[:grammar_file])
inside of the post method and both just print to the console
require 'sinatra'
require 'sinatra/reloader' if development? #gem install sinatra-contrib
require './rsg.rb'
enable :sessions
get '/' do
erb :index
end
post '/' do
rsg(params[:grammar_file])
erb :index
end
<% title = "RANDOM SENTENCE GENERATOR" %>
<!doctype html>
<html lang="en">
<head>
<title><%= #title || "RSG" %></title>
<meta charset="UTF8">
</head>
<body>
<h1>RubyRSG Demo</h1>
<p>Select grammar file to create randomly generated sentence</p>
<form action="/" method="post">
<select name="grammar_file">
<option value="Select" hidden>Select</option>
<option value="Poem">Poem</option>
<option value="Insult">Insult</option>
<option value="Extension-request">Extension-request</option>
<option value="Bond-movie">Bond-movie</option>
</select>
<br><br>
<input type="submit" value="submit">
</form>
<section>
<p>Here</p>
<p><%= rsg(params[:grammar_file])%></p>
</section>
</body>
</html>
You need to tell your template what to do with the params.
This is what is happening:
post '/' do
rsg(params[:grammar_file])
# your rsg method produces some output. I guess you have a line the `puts` your params to stdout somewhere. Instead you should redirect the output into the template.
erb :index
end
Like this:
post '/' do
erb :index, :locals => {:rsg => rsg(params[:grammar_file])}
end
Then, in your :index template you have a line like:
<%=rsg%>
To output the generated String.
The problem might also be that you're tryng to return a puts statement instead of the plain string:
def rsg(p)
puts "I love my daily #{p}. Good luck to you"
end
This will just print to the console and nothing else (true to be precise)
Better:
def rsg(p)
"I love my daily #{p}. Good luck to you"
end
Here you will just return the String from your method and calling rsg("sandwich") will return:
# => "I love my daily sandwich. Good luck to you"
Related
In my project, I have a few files:
1. main.rb
require 'sinatra'
set :public_folder, 'public'
set :views, 'views'
set :erb, :layout => :base
get '/' do
erb :layout
end
get '/about' do
erb :about
end
get '/contact' do
erb :contact
end
2. layout.erb
<% title="Songs By Sinatra" %>
<!doctype html>
<html lang="en">
<head>
<title>
<%=title %>
</title>
<meta charset="utf-8">
</head>
<body>
<header>
<h1><%= title %></h1>
<nav>
<ul>
<li>Home
</li>
<li>About
</li>
<li>Contact
</li>
</ul>
</nav>
</header>
<section>
<%=yield %>
</section>
</body>
</html>
<p>Welcome to this website that's all about the songs of the great Frank Sinatra.</p>
<img src="/images/sinatra.jpg" alt="Frank Sinatra">
3. about.erb
<p>
This site is a demonstration of how to build a website using Sinatra.
</p>
When I go to http://localhost:4567/about everything is OK, page loads, but when I go to http://localhost:4567/, I get an error: no block given (yield).
Can anyone explain what is the problem, and possible solution.
Thank you very much in advance.
You need to pass a block in
erb :layout
For <%=yield %> in layout.erb to work, you need to pass a block whose output will be placed at the location of `yield. In its simplest form, you can do something like this:
erb :layout { "This is what I want in output" }
Typically, you render another template:
erb :layout do
erb :about
end
More details in documentation.
TLDR; what #wandmaker says - you need to pass a block to :layout.
Understanding yield is essential for understanding Ruby. Basically, yield passes control from one scope to another. All methods in Ruby accept blocks but they ignore them unless they explicitly yield or call them:
"A RUBY STRING".downcase { p "I'm in a block!" }
# => "a ruby string"
But if the method calls yield, control is passed to the block and then passed back to the calling scope:
def yield_me
yield
end
yield_me { p "I'm in a block!" }
# => "I'm in a block!"
Alternatively, you can catch blocks as arguments:
def catch_me(&block)
block.call
end
catch_me { p "I'm in a block" }
# => "I'm in a block!"
So with that in mind, it should be clearer what your Sinatra template is doing - it renders layout.erb but when it hits yield, it tries to yield control to a block - in this case a non-existent one.
So all your responses should supply a block or sub-template like:
erb :layout { "<p>This is the about page</p>" }
or
erb :index
Im building a simple practice Sinatra app which allows users to enter urls via a form, and once submitted the app will open each of these urls in a new table (similar to urlopener.com)
My app.rb file
require 'sinatra'
get '/' do
erb :'index.html'
end
post '/' do
urls = params[:urls]
end
My View file
<h1>Enter URLs Below </h1>
<form action="/" method="post">
<textarea rows="40" cols="50" id="urls" name="urls" ></textarea>
<br/>
<input type= "submit" value="Open 'em up!">
</form>
I am able to print the urls to the console in the post action, but am unsure how to redirect back to the index, and display each of the urls before opening them in new tabs (which I plan on using JS to do).
You don't have to redirect back to the original page (in fact, the URL hasn't changed, so redirecting doesn't make sense). Instead, you render the same template. Simply insert erb :'index.html' in the second block (post '/') as well, and put the URLs in a class variable, so that they will be available to the template:
#urls=params[:urls].split
(The split is there so you get an array of strings, rather than one long string with linebreaks.)
Finally, you add some logic to the template to check whether there are any URLs to display, and if so render them as a list:
<% if #urls && !#urls.empty? %>
<h1>URLs</h1>
<ul>
<% for #url in #urls %>
<li>
<%= #url %>
</li>
<% end %>
</ul>
<% end %>
<h1>Enter URLs Below </h1>
...etc...
I'm using sequel.
In my app.rb, I wrote
get '/search' do
#post = Post.find(:Title => "%#{params[:query]}%")
erb :'layout'
end
Layout.erb
<form action="/search" method="get">
<input type="text" name="query"/><br />
<input type="submit" />
</form>
<% if #results %>
<table>
<%#results.each do |r|%>
<tr valign="top">
<td><%=r.title%></td>
</tr>
<%end%>
</table>
<% end %>
And to the blog_model.rb in post class this:
def self.search(query)
#where(:title, query) -> This would return an exact match of the query
where("title like ?", "%#{query}%")
end
And I'm getting this :LocalJumpError at /search
no block given (yield).
So what to do or have I done this code correctly ? Thanks in advance.
I guess the problem is the name of erb file, layout.erb.
Sinatra always search for a layout.erb, if you not explicit indicate other layout file, that will handle the page template. This file has the form:
<!doctype html>
<html>
<head>
...
<body>
...
<%= yield %> insert the content here
...
</html>
There are two solutions:
Rename the layout.erb file.
Replace the erb call by: erb :layout, layout: false
Right now I have my ActionView::Base.field_error_proc as
Proc.new do |html_tag, instance|
if html_tag =~ /^<label/ or instance.respond_to?(:object_name)
%{<div class="field_with_errors">#{html_tag}</div>}.html_safe
else
%{<div class="field_with_errors">#{html_tag}<br /><label for="#{instance.send(:tag_id)}" class="message">#{instance.error_message.first}</label></div>}.html_safe
end
I had modified this to accomodate a few of my needs when i did use the client_side_validations gems.
Right now, the 'fields_with_error' div wraps around the html_tag(i.e the error field to be specific). What i would like to know though is if its possible to modify the position of the div class="fields_with_error" and place it right after the div that contains the error_field.
For eg,
<div class="main_div">
<%= password_field_tag 'secret', 'Your secret here' %>
</div>
Then the div class="fields_with_error" should be postioned as
<div class="main_div">
<%= password_field_tag 'secret', 'Your secret here' %>
</div>
<div class="fields_with_error"> <label class="message"> The error message </label> </div>
Any help would be of great value.
So when I work with twitter bootstrap I usually make an initializer with something like this
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
html = %(<div class="field_with_errors">#{html_tag}</div>).html_safe
# add nokogiri gem to Gemfile
form_fields = [
'textarea',
'input',
'select'
]
elements = Nokogiri::HTML::DocumentFragment.parse(html_tag).css "label, " + form_fields.join(', ')
elements.each do |e|
if e.node_name.eql? 'label'
html = %(<div class="control-group error">#{e}</div>).html_safe
elsif form_fields.include? e.node_name
if instance.error_message.kind_of?(Array)
html = %(<div class="control-group error">#{html_tag}<span class="help-inline"> #{instance.error_message.uniq.join(', ')}</span></div>).html_safe
else
html = %(<div class="control-group error">#{html_tag}<span class="help-inline"> #{instance.error_message}</span></div>).html_safe
end
end
end
html
end
This just adjust regular errors to twitter errors in forms :> so makes everything very nice to look at ;>. You can use same approach.
Cheers if this helps
This was my first Sinatra project - link shortener but I am stuck with some errors and to be honest sinatra's built-in debugger tells me literally nothing. I would like you to give me a clue or suggest a solution to problem.
http://min.us/mkBIVTh7p - screenshot, this happen when I submit my form with url: http://google.com and word google
require 'sinatra'
require 'shotgun'
require 'data_mapper'
require 'dm-migrations'
require 'dm-sqlite-adapter'
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/form.db")
class Url
include DataMapper::Resource
property :id, Serial
property :url, String
property :suggestion, String
end
get '/' do
erb :index
end
post '/' do
Url.create(:url => params[:url], :suggestion=> params[:suggestion])
end
get '/Url.suggestion' do
query = request_path.slice!(0)
redirection = Url.first(:suggestion => query)
redirect redirection.url
end
index.rb
<!doctype html>
<html>
<head>
<title>Skracanie linków</title>
</head>
<body>
<form name="form" method="post" action="#">
<fieldset>
<legend>Wpisz co trzeba</legend>
<p><label> <input type="text" name="post[url]"/>Url:</label></p>
<p><label> <input type="text" name="post[suggestion]"/>Suggested name:</label></p>
</fieldset>
<p class="center">
<input type="reset" value="Wyczyść formularz"/>
<input type="submit" value="Wyślij"/>
</p>
</form>
</body>
</html>
This is because you need to finalize your models. See http://datamapper.org/getting-started.html under the heading "Finalize Models".
Add the finalize command after defining your models:
class Url
include DataMapper::Resource
property :id, Serial
property :url, String
property :suggestion, String
end
# add this line
DataMapper.finalize