Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

Kian Chakamian
Kian Chakamian
15,410 Points

Sinatra: HTML form not appearing when after my embedded ruby code.

I am trying to have a page display the content of a file and take input via an HTML form adding onto the file. Both features work when separated but when on the same erb template, the HTML form always disappears even if it is before the embedded ruby code that is displaying the file. Please help.

play.rb:

require "sinatra"

get "/" do
  erb :enter
end

post "/create" do
  File.open("test.txt", "a+") do |file|
    file.write "#{params["content"]}"
  end
  redirect "/"
end

views/enter.erb:

<%= send_file "test.txt"%>

<form method="post" action="/create">
    <input type="text" name="content" id="content">
    <input type="submit">
</form>

test.txt:

Hello
Hi

Output: Just the text from test.txt

1 Answer

Oliver Duncan
Oliver Duncan
16,642 Points

If I had to guess, I'd say that the culprit is #send_file being used from within your view. From the docs:

Use the contents of the file at path as the response body.

That says to me that when your browser encounters #send_file from within a view, it might be sending another request for the contents of that file, the response of which doesn't include your form. You could confirm this from the "network" tab in your js console.

If I'm right, a fix would be to read the file from within your controller, assign its contents to an instance variable, and pass it to the view that way.

Kian Chakamian
Kian Chakamian
15,410 Points

I just edited a copy of my project to include your fix and the output was the same as before. The updated files are:

play.rb:

require "sinatra"

get "/" do
  @content = send_file "test.txt"
  erb :enter
end

post "/create" do
  File.open("test.txt", "a+") do |file|
    file.write "#{params["content"]}"
  end
  redirect "/"
end

views/enter.erb:

<%= @content %>

<form method="post" action="/create">
    <input type="text" name="content" id="content">
    <input type="submit">
</form>

Thanks for the suggestion anyway

Oliver Duncan
Oliver Duncan
16,642 Points

Sorry, I wasn't clear. #send_file is a method that sends the contents of a file as a response to the request, in your revised case a GET request to "/". But I think it's also hijacking your controller response, and sending only the contents of the file. This is what the #send_file method is for. If you want to display the contents of the file along with your template, I think a different approach is needed.

What I meant by reading the file in the controller was something like this:

get "/" do
  @content = File.read("test.txt")
  erb :enter
end

This way @content contains the file contents, and you avoid using #send_file. Not sure that this is the best solution, but hopefully it'll work.

EDIT: To account for new line characters in the text file (which I'm not sure are read by html), a better solution might look something like this:

  get "/" do
    @content = ""
    File.foreach("test.txt") { |line| @content += line + "<br />" }
    erb :enter
  end
Kian Chakamian
Kian Chakamian
15,410 Points

I put in your second solution and worked just as I wanted it to. I used #send_file because I couldn't figure out how to make the new line characters work. Thank you so much, Oliver!