Multiple file uploads in Sinatra - ruby

I'm writing a simple Sinatra app but having issues having <input type="file" multiple /> not making Rack throw a NoMethodError: undefined method 'bytesize' for (Hash) while reading the files.
The form is written like so:
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="file" name="images[]" multiple />
</form>
But the receiving end throws the mentioned error, before any of my code executes, that is, Rack is not parsing correctly the input[name=images]. Am I sending the form incorrectly? If I drop the brackets [], then only the last file (of many) is sent, but I feel like I might be missing something...
Just to clarify: this is Sinatra v1.4.3 and Rack v1.5.2, the latter being the one throwing the exception. Full backtrace here.

The only thing that puts me off here is that you don't use the POST method – maybe your issue has to do with that. Anyway, the following code works perfectly for me. I hope this will give you a hint how to fix your code.
require 'sinatra'
get '/' do
<<-HTML
<html>
<head><title>Multi file upload</title></head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="images[]" multiple />
<input type="submit" />
</form>
</body>
</html>
HTML
end
post '/upload' do
content_type :text
res = "I received the following files:\n"
res << params['images'].map{|f| f[:filename] }.join("\n")
res
end

Related

Uploading to exist-db (controller issue?)

All of the files are available at http://gist.github.com/tonyahowe -- page.html, app.xql, upload.html, and form1.html. The app:upload function in appxql is at line 378.
I am trying to add an upload feature to my application here (http://nic.cerosia.org) and have been able to get the upload to work without integrating it into the whole application. Once I integrate it, I get a 400 error message, included below. I think the problem might be a controller issue, but I'm not sure?
I have added a simple link from the index to upload.html page, as follows:
<div xmlns="http://www.w3.org/1999/xhtml" data-template="templates:surround" data-template-with="templates/page.html" data-template-at="main">
<form enctype="multipart/form-data" method="post" action="form1.html">
<fieldset>
<legend>Upload Document:</legend>
<input type="file" name="file"/>
<button id="f-btn-upload" name="f-btn-upload" value="true" type="submit" class="btn btn-danger">Upload</button>
</fieldset>
</form>
</div>
That sends the upload file to form1.html, which basically figures out what to do with different form actions (it works with the first two options):
<div xmlns="http://www.w3.org/1999/xhtml" data-template="templates:surround" data-template-with="templates/page.html" data-template-at="main">
<div/>
<!-- if a query search -->
<div data-template="templates:if-parameter-set" data-template-param="query">
<div data-template="templates:include" data-template-path="search.html" class="col-md-12"/>
</div>
<!-- if a coursepack search -->
<div data-template="templates:if-parameter-set" data-template-param="f-btn-coursepack">
<div data-template="templates:include" data-template-path="coursepack.html" class="col-md-12"/>
</div>
<!-- if an upload -->
<div data-template="templates:if-parameter-set" data-template-param="f-btn-upload">
<div data-template="templates:include" data-template-path="success.html" class="col-md-12"/>
<div data-template="app:upload"/>
</div>
</div>
The app:upload is here:
declare function app:upload($node as node(), $model as map(*), $type as xs:string?) {
let $collection := '/db/apps/NiC/inReview/'
let $filename := request:get-uploaded-file-name('file')
(: make sure you use the right user permissions that has write access to this collection :)
let $login := xmldb:login($collection, 'public', 'public')
let $store := xmldb:store($collection, $filename, request:get-uploaded-file-data('file'))
return
<results>
<message>File {$filename} has been stored at collection={$collection}.</message>
</results>
};
I keep getting an error that I think has something to do with the controller, but I'm not sure. Can anyone help me figure out where the problem is? It may also be a templating issue?
Here's the error:
HTTP ERROR 400
Problem accessing /exist/apps/NiC/works/form1.html. Reason:
SAX exception while parsing request: Content is not allowed in prolog.
I don't know for sure, but I wonder if it is an issue with the templating framework reading the data from the request body and then it not being available in your app:upload function.
I wonder if we could test this, perhaps instead of checking for f-btn-upload which will be in the body of the HTTP request, you could instead check for a query-string parameter. If you change your form in the upload.html page to be like this:
<form enctype="multipart/form-data" method="post" action="form1.html?do-upload=true">
and the switch in your form1.html to be like this:
<div data-template="templates:if-parameter-set" data-template-param="do-upload">
Does that help you at all?

patch method in ruby sinatra

I'm writing a web app using sinatra and activerecord but I can't figure it out what's wrong into my code...if you could just check it and guide me through it would be awesome !!All the models are linked but I can't update information about some books into the database..
RUBY SINATRA CODE :
get '/info/:isbn/edit' do
#book = Book.find_by(isbn: params[:isbn])
erb :edit
end
patch '/info/:isbn' do
book = Book.find_by(isbn: params[:isbn])
book.title = params[:title]
book.page_count = params[:number_pages]
book.category = params[:category]
book.save
redirect to "/info/#{ params[:isbn] }"
end
ERB PAGE
<form action="/info/<%= #book_isbn%>/edit" method="post">
<input type="hidden" value="patch" name="_method">
<label for="">Title</label>
<input type="text" name="title" value="<%= #book_title %>">
</form>
and this is the error page...
Check to make sure that your controller sets :method_override to true.
set :method_override, true
Without it Sinatra won't know what do with input type="hidden" value="patch" name="_method"
You are sending your form to the wrong URL:
<form action="/info/<%= #book_isbn%>/edit" method="post">
URL should be /info/<%= #book_isbn%>.

Watir-Webdriver text_field deprecation - use textarea (but method does not seem to work)

The new version of watir-webdriver (0.8.0) has been released. I have installed it and have been running my suites to check for changes. During this process I have noticed the text_field methods I am using to set text in <input id="some_id" type="text"> tags has been deprecated from the message Locating textareas with '#text_field' is deprecated. Please, use '#textarea' method instead. I changed the text_field method to textarea, but once I do this the element can no longer be located.
Am I using the method wrong? I tried to use it as browser.textarea(:id=> 'some_id').when_present.set "Text". Once the method times out, I get the message saying the element with the tagname textarea could not be located. Was wondering if there is something else I need the do to use the textarea method to set text in a <input id="some_id" type="text"> tag.
EDITED => html I am trying to automate on:
<form action="some_href" method="post">
<p class="align">
<label for="user">Username</label>
<input id="login" type="text" value="">
</p>
<p class="align">
<label for="pass">Password</label>
<input id="password" type="password" value="">
</p>
</form>
EDITED Watir code used to automate page
#RUBY CODE
class Login
def open
#browser = Watir::Browser.new :firefox
#browser.goto some_website_url #URL leading to page with form HTML above
end
def login_as
user_field.when_present(10).set username
password_field.when_present(10).set password
login_link.when_present(10).click
end
def user_field
#browser.text_field(:id=> "login")
end
def password_field
#browser.text_field(:id=> 'password')
end
def username
"user123"
end
def password
"p#ssw0rd"
end
def login_link
#browser.link(:text=> 'Sign-in')
end
end#Login
#STEP DEFINITION
Given(/^login to website$/) do
login = Login.new
login.open
login.login_as
end
The previous versions I have been using without the deprecation message is:
watir-webdriver (0.6.11)
selenium-webdriver (2.44.0)
Any answers would be appreciated if someone has some insight on the problem.

How do I pass an existing Variable that changes at refresh to a param in a post method using sinatra

I'm using sinatra as my web framework and right now I have
<p><%= #sentence %></p>
<form action='/' method='POST'>
<button type='submit'>Save Story</button>
</form>
in my erb file. The variable at #sentence changes at refresh. When I hit the save Story button I want it to create a param in the post method that is equal to #sentence so that I can save #sentence to the main page. Is there anyway to do this without javascript?
ANSWERED
I used
`<div class="row">
<form action='/' method='POST'>
<input id="sentence" type="hidden" name="sentence" value= "<%= #sentence %>" >
<button type='submit'>Save Story</button>
</form>
</div>`
its still only taking the first word on one of the 4 pages but there must be something else going on there.
You need to create a hidden input field with the value set to w.e #sentence is
<p><%= #sentence %></p>
<form action='/' method='POST'>
<input type="hidden" name="sentence" value="<%= #sentence %>" />
<button type='submit'>Save Story</button>
</form>
This will give the form something to pass that you can grab with post elsewhere, hope that helps, just put your variable where the ... is and be sure to tell it the language, here is a php example on how to add a varaible to a value.
value="<?php echo $state; ?>"
Here I'm basically telling the browser to echo(print) the state variable between the " and " using php start and end to initiate the language and end it, The hidden type input field is invisible to users and they cannot edit it, its a background trick you can use to pass information, it acts as a text field.
Information on hidden fields:
http://www.blooberry.com/indexdot/html/tagpages/i/inputhidden.htm
When you select an answer, please edit your main post to display ANSWERED and the updated code so users can see what you decide to use.
In sinatra you can do this:
<p><%= #sentence %></p>
<form action='/' method='POST'>
<input type="hidden" name="sentence" value="<%= #sentence %>" />
<button type='submit'>Save Story</button>
</form>

Sinatra HTTP 'PUT' method

Something's wrong with the PUT action here, the form gets processed but the updated field is not being saved.
I've did what Sinatra users are doing, by adding in "_method" for Sinatra to recognise that's its a HTTP PUT action. Could anyone spot any mistake here?
# edit
get '/entries/*/:id/edit' do
#entry = Entries.get(params[:id])
#title = "edit"
erb :edit, :layout => :edit_layout
end
# update
put '/entries/:id' do
#entry = Entries.get(params[:id])
if #entry.save
redirect "/entries/id=#{#entry.id}"
else
redirect "/enewsletters"
end
end
<!-- Edit form -->
<form action="/enewsletters/edit/<%= #entry.id %>" method="post">
<input name="_method" value="put" type="hidden"/>
<p>
<label>Content</label><br/>
<input type="text" name="entry[title]" value="<%= #enew.title %>">
</p>
<p>
<input type="submit" name="commit" value="update">
</p>
</form>
You don't seem to be doing any update to the #entry, you're just fetching the specific entry with the id from params. Are you using ActiveRecord? If so, instead of #entry.save, try #entry.update_attributes(params[:entry]).
Edit: I'm guessing you're not using AR since I just noticed the .get call. Whatever ORM you are using must have an easy way to update the attributes and then save the record.

Resources