I'm making a URL shortener application in Sinatra. It works as follows:
The front page is a form with one field, to enter a long url into:
<form action="" method="post">
<input type='url' name='url' placeholder='Enter URL to be shortened'>
<input type="submit">
</form>
The form posts to the same front page and the code for posting to '/' is this:
post '/' do
#Makes variable of POSTed url.
#long = params[:url]
loop do
#makes random six letter hash
#rand = (0...6).map{(65+rand(26)).chr}.join.downcase
#don't generate another one if it isn't found in the database
break if Short.first(id: "#{#rand}").nil?
end
#saves url and hash to database
#input = Short.create(url: #long, id: #rand)
#displays link with hash to be copied into browser address bar
"http://192.168.1.3:999/"+#rand
end
The problem is that when I submit the form, it doesn't return the http://192.168.1.3:999/... or anything I put after the #input=Short.create(... line. It doesn't return any errors, even when raise_on_save_failure is true. If I comment that line out, it works fine (except when trying to use the shortened url).
EDIT: When I change the code to allow non-urls, it functions perfectly normally. It only breaks with the exact url format.
Related
I want to save the html after filling a form. lets say:
page.form.field.value = 'testing'
page.save 'test.html'
the generated test.html file don't have the modified value attribute
<input name='something' value=''>
I'm expecting:
<input name='something' value='testing'>
You want to use dom functions for that:
page.at('[name=something]')['value'] = 'testing'
In other words there's no reason to expect that changes to the Form fields will update the dom.
How do I interact with a file_field thats hidden by its parent?
<span class="btn button-large fileinput-button">
Select files...
<input accept="image/png, image/gif, image/jpeg" id="gallery_files" multiple="multiple" name="gallery_files" type="file">
</span>
The button overlays the input, therefore it's not visible.
Edit
For the record, here's my code:
data[:photos].each do |photo|
$browser.file_field.set photo
end
and the error: Watir::Wait::TimeoutError: timed out after 20 seconds, waiting for {:tag_name=>"input", :type=>"file"} to become present
Workable example in a Gist
I was a bit suprised, but I was able to set the file field in the sample HTML without any issue using:
browser.file_field.set('path/to/file.txt')
From the code, it looks like setting the file field only requires the input to exist. It does not require it to be visible (or present).
Given that you are getting a Watir::Wait::TimeoutError exception, I would guess that your code is actually failing before the file_field.set. As it looks like the page has the input in a dialog, I am guessing your code actually looks more like:
$browser.file_field.wait_until_present
data[:photos].each do |photo|
$browser.file_field.set photo
end
It would be the wait_until_present method that is actually throwing the exception.
Solution 1
Assuming that an explicit wait method is being called for the file field, you should be able to just remove the wait.
If you have the wait because the dialog is being loaded by Ajax, you could try waiting for a different element instead - eg the parent span.
Solution 2
If for some reason you need the file field to be present, you will need to change its CSS. In this case, the opacity:
p $browser.file_field.present?
#=> false
$browser.execute_script('arguments[0].style.opacity = "1.0";', browser.file_field)
p $browser.file_field.present?
#=> true
For my situation, this worked:
$browser.execute_script("jQuery(function($) {
$('.fileinput-button').css('visibility', 'hidden')
$('#gallery_files').css('visibility', 'visible').css('opacity', '1').css('width', '100').css('height', '50')
})")
I had to hide the parent span, then show, resize, and change the opacity of the input
I have been developing an application using web2py. I have a page with a callback link which sends information to another function, this loads the results via ajax in the right sidebar of the page. When the link is clicked, the link itself disappears but the results are loaded correctly. I have to refresh the page for the links to come back. I have been looking at this for ages but can't figure it out, I am relatively new to web2py so probably missed something obvious.
Here is some code:
The view (With the link):
{{if hosts == "Please select a category":}}
{{response.write("Please select a category")}}
{{else:}}
<ol>
{{for i in hosts:}}
<li>{{=A(i['HostName'], callback=URL('selection','getResults.load', vars=dict(clicked=i['HostName'])), target="monitoredDevice")}}</li>
{{pass}}
</ol>
{{pass}}
{{end}}
{{block right_sidebar}}
<div id="monitoredDevice">
</div>
{{end}}
The controller action:
def getResults():
hostName = request.vars.clicked
# hostName = request.vars.hostNameSelected
print(hostName)
try:
task_status = scheduler.task_status(db.scheduler_task.task_name==hostName, output=True)
result = task_status.result
# create_compare_graph()
except(AttributeError): # Error is because there is no scheduler and therefore no status, need to set the values to 0
result = emptyTaskStatusResultsToZero()
try:
return dict(result)
except(TypeError): # There is a task scheduled however there is no run data as it has not been run before. Causes an error.
return emptyTaskStatusResultsToZero()
I think there is something wrong with the =A link in the view?
Thank you!
The method just prevents the view from getting an error if nothing is returned so I set all the values to zero so the view has something to render.
def emptyTaskStatusResultsToZero():
return {'percent_diff_in': 0, 'percent_diff_out':0, 'now_avg_30mins_in': 0
, 'now_avg_30mins_out':0, 'prev_week_avg_30mins_in':0, 'prev_week_avg_30mins_out':0,
'percent_more':0,'percent_less':0, 'count':0}
I have a watir-webserver Ruby script I am developing, and I am having it first prompt the user for a username and password (with Ruby-Tk).
In any case, I can open the page with this code:
b = Watir::Browser.start 'https://connect.mypage.com/Home'
After that, I call the following:
t1=b.text_field(:id => 'ctl00_cphBody_txtUserName').when_present.set #entry1.textvariable
Nothing is filled in.
I have tried hard coding a name in for ctl00_cphBody_txtUsername, but that did not work either.
Using the Inspector in Firefox, the field is constructed as following:
<input name="ctl00$cphBody$txtUsername" id="ctl00_cphBody_txtUsername" class="firstFocus" maxlength="50" type="text">
The class of firstFocus is:
<script type="text/javascript">
$(document).ready(function () {
jQuery('.firstFocus').focus();
});
</script>
The problem is that the locator is case-sensitive.
Notice that the id attribute value is "ctl00_cphBody_txtUsername" but the locator being used is "ctl00_cphBody_txtUserName". Due to the "N" not matching the "n", the element is never found, which is why the when_present times out.
Correcting the id in the locator will fix the issue:
t1=b.text_field(:id => 'ctl00_cphBody_txtUsername').when_present.set #entry1.textvariable
If I start the browser and open the page like below, I have no problems locating those two text_field's:
browser = Watir::Browser.new
browser.goto 'https://connect.mypage.com/Home'
#Then, the first item actually has a class, so I use that
t1=browser.text_field :class => 'firstFocus'
t1.set #entry1.textvariable
This is in Sinatra. In my 'get', I create an instance variable which is a nokogiri object, created from an external xml file. I go to an erb file and parse through that nokogiri object in order to do the page layout. In my post method, I need access to that same nokogiri object (I may return to post numerous times and may modify the nokogiri object). The way I've been doing this is to set a hidden variable in the erb page, like this:
<input type="hidden" name="test" value= '<%= #test %>' >
Then in my post, I create a nokogiri object from that variable like this:
#test = Nokogiri::XML(params["test"])
This seemed clunky, but I'm not experienced in this stuff. Anyway, everything worked fine, except that somewhere along the line, my embedded quotes in the xml get mangled. For example, node in my file starts like this:
<property name="blah" value='{"name:foo"}'> </property>
And when I do a puts in my post of params["test"], I get this:
<property name="blah" value="{"name:foo"}"> </property>
(single quotes became double quotes), and finally, after converting it back into a nokogiri object, with the following code:
#test = Nokogiri::XML(params["test"])
I get this:
<property name="blah" value="{"/>name:foo"}"> </root>
Is there a better way to retain access to the object? If not, is there a way to retain my embedded quotes ( I think setting the hidden variable in the erb file is where it gets mangeled)
Summary
Cache the Nokogiri documents in a constant (e.g. a hash or module), which live across requests (within the same server run; see below).
Send only the key to the hash in your form.
Use the key to get the document back out of the constant later on.
Example
package_32.xml
<packages><kittens in_box="true">32</kittens></packages>
cache_nokodocs.rb
require 'sinatra'
require 'nokogiri'
module NokoDocs
#docs_by_file = {}
def self.[](file)
#docs_by_file[file] ||= Nokogiri::XML(IO.read(file))
end
end
get "/xml/:doc" do
#doc = params['doc']
#xml = NokoDocs[#doc]
<<-ENDHTML
The XML starts with '#{#xml.root.name}'
<form method="post" action="/">
<input type="hidden" name="xml" value="#{#doc}">
<button type="submit">Go</button>
</form>
ENDHTML
end
post "/" do
#xml = NokoDocs[params['xml']]
#xml.to_s
end
Using
C:\>curl http://localhost:4567/xml/package_32.xml
The XML starts with 'packages'
<form method="post" action="/">
<input type="hidden" name="xml" value="package_32.xml">
<button type="submit">Go</button>
</form>
# simulate post that the web browser does from the command line
C:\>curl -d xml="package_32.xml" http://localhost:4567/
<?xml version="1.0"?>
<packages>
<kittens in_box="true">32</kittens>
</packages>
The first time any user requests a particular XML file, it will be loaded into the hash; subsequent requests for that file will fetch it from the hash directly, pre-parsed.
Beware!
The documents will not be cached across multiple instances of the server (e.g. if you're behind a reverse proxy). They will also not be cached across server restarts. However, if these are static files on disk, the worst that will happen is that the particular server session will just have to re-create the Nokogiri document once before caching it.
Using the file name on disk and then letting the user post it back to you is probably a really, really dangerous thing to do. Instead, you might create a custom or random key when you load the document and use that. For example:
require 'digest'
module NokoDocs
#docs_by_file = {}
def self.from_file(file)
key = Digest::SHA1( file + rand(100) )
[
#docs_by_file[key] ||= Nokogiri::XML(IO.read(file)),
key
]
end
def self.from_key(key)
#docs_by_file[key]
end
end
get "/xml/:doc" do
#xml, #key = NokoDocs.from_file params['doc']
...
"<input type="hidden" name="key" value="#{#key}">"
...
end
post "/" do
#xml = NokoDocs.from_key params['key']
end
This is a potential memory leak. Each unique document your users request is parsed as a big Nokogiri document and preserved forever (until you restart the server). You might want a system that records the last access time and have a timed job that periodically sweeps out items that haven't been accessed in a while.