Using Ruby to wait for a page to load an element - ruby

I currently have a working piece of Ruby that looks like this:
def error_message
browser.span(:id => 'ctl00_cphMainContent_lblMessage').wait_until_present(30) do
not errors.empty?
end
errors
end
However, I'd prefer something more like this:
span(:errors, :id => 'ctl00_cphMainContent_lblMessage')
##farther down##
def error_message
browser.errors.wait_until_present(30) do
etc...
I'm new to Ruby, but how can I do something like this, assuming it's possible?

Typically, you make use of the Watir::Wait.until or <element>.wait_until_present methods.
In this way, you could do something like:
# Earlier in code
#browser = Watir::Browser.start('http://mypage.com/')
# ...
errors = { my_first_error: "You forgot your keys in the car!" }
check_for_errors(error[:my_first_error])
# Wherever
def check_for_errors(error, expiry=30)
error_element = #browser.span(:id => 'ctl00_cphMainContent_lblMessage')
error_element(value: error).wait_until_present(expiry)
end
See the watir documentation for more information.

Related

ruby sinatra how to redirect with regex

I am trying to move stuff at root to /en/ directory to make my little service multi-lingual.
So, I want to redirect this url
mysite.com/?year=2018
to
mysite.com/en/?year=2018
My code is like
get %r{^/(\?year\=\d{4})$} do |c|
redirect "/en/#{c}"
end
but it seems like I never get #{c} part from the url.
Why is that? or are there just better ways to do this?
Thanks!
You can use the request.path variable to get the information you're looking for.
For example,
get "/something" do
puts request.path # => "/something"
redirect "/en#{request.path}"
end
However if you are using query parameters (i.e. ?yeah=2000) you'll have to manually pass those off to the redirect route.
Kind of non-intuitively, there's a helper method for this in ActiveRecord.
require 'active_record'
get "/something" do
puts params.to_param
# if params[:year] is 2000, you'll get "year=2000"
redirect "/en#{request.path}?#{params.to_param}"
end
You could alternatively write your own helper method pretty easily:
def hash_to_param_string(hash)
hash.reduce("") do |string, (key, val)|
string << "#{key}=#{val}&"
end
end
puts hash_to_param_string({key1: "val1", key2: "val2"})
# => "key1=val1&key2=val2"

ruby object to_s gives unexpected output

What is the correct way to view the output of the puts statements below? My apologies for such a simple question.... Im a little rusty on ruby. github repo
require 'active_support'
require 'active_support/core_ext'
require 'indicators'
my_data = Indicators::Data.new(Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-08-25', :end_date => '2012-08-30').output)
puts my_data.to_s #expected to see Open,High,Low,Close for AAPL
temp=my_data.calc(:type => :sma, :params => 3)
puts temp.to_s #expected to see an RSI value for each data point from the data above
Maybe check out the awesome_print gem.
It provides the .ai method which can be called on anything.
An example:
my_obj = { a: "b" }
my_obj_as_string = my_obj.ai
puts my_obj_as_string
# ... this will print
# {
# :a => "b"
# }
# except the result is colored.
You can shorten all this into a single step with ap(my_obj).
There's also a way to return objects as HTML. It's the my_obj.ai(html: true) option.
Just use .inspect method instead of .to_s if you want to see internal properties of objects.

Structuring Nokogiri output without HTML tags

I got Ruby to travel to a web site, iterate through a list of campaigns and scrape the pages for specific data. The problem I have now is getting it from the structure Nokogiri gives me, and outputting it into a readable form.
campaign_list = Array.new
campaign_list.push(1042360, 1042386, 1042365, 992307)
browser = Watir::Browser.new :chrome
browser.goto '<redacted>'
browser.text_field(:id => 'email').set '<redacted>'
browser.text_field(:id => 'password').set '<redacted>'
browser.send_keys :enter
file = File.new('hourlysales.csv', 'w')
data = {}
campaign_list.each do |campaign|
browser.goto "<redacted>"
if browser.text.include? "Application Error"
puts "Error loading page, I recommend restarting script"
# Possibly automatic restart of script
else
hourly_data = Nokogiri::HTML.parse(browser.html).text
# file.write data
puts hourly_data
end
This is the output I get:
{"views":[[17,145],[18,165],[19,99],[20,71],[21,31],[22,26],[23,10],[0,15],[1,1], [2,18],[3,19],[4,35],[5,47],[6,44],[7,67],[8,179],[9,141],[10,112],[11,95],[12,46],[13,82],[14,79],[15,70],[16,103]],"orders":[[17,10],[18,9],[19,5],[20,1],[21,1],[22,0],[23,0],[0,1],[1,0],[2,1],[3,0],[4,1],[5,2],[6,1],[7,5],[8,11],[9,6],[10,5],[11,3],[12,1],[13,2],[14,4],[15,6],[16,7]],"conversion_rates":[0.06870229007633588,0.05442176870748299,0.050505050505050504,0.014084507042253521,0.03225806451612903,0.0,0.0,0.06666666666666667,0.0,0.05555555555555555,0.0,0.02857142857142857,0.0425531914893617,0.022727272727272728,0.07462686567164178,0.06134969325153374,0.0425531914893617,0.044642857142857144,0.031578947368421054,0.021739130434782608,0.024390243902439025,0.05063291139240506,0.08571428571428572,0.06741573033707865]}
The arrays stand for { views [[hour, # of views], [hour, # of views], etc. }. Same with orders. I don't need conversion rates.
I also need to add the values up for each key, so after doing this for 5 pages, I have one key for each hour of the day, and the total number of views for that hour. I tried a couple each loops, but couldn't make any progress.
I appreciate any help you guys can give me.
It looks like the output (which from your code I assume is the content of hourly_data) is JSON. In that case, it's easy to parse and add up the numbers. Something like this:
require "json" # at the top of your script
# ...
def sum_hours_values(data, hours_values=nil)
# Start with an empty hash that automatically initializes missing keys to `0`
hours_values ||= Hash.new {|hsh,hour| hsh[hour] = 0 }
# Iterate through the [hour, value] arrays, adding `value` to the running
# count for that `hour`, and return `hours_values`
data.each_with_object(hours_values) do |(hour, value), hsh|
hsh[hour] += value
end
end
# ... Watir/Nokogiri stuff here...
# Initialize these so they persist outside the loop
hours_views, orders_views = nil
campaign_list.each do |campaign|
browser.goto "<redacted>"
if browser.text.include? "Application Error"
# ...
else
# ...
hourly_data_parsed = JSON.parse(hourly_data)
hours_views = sum_hours_values(hourly_data_parsed["views"], hours_views)
hours_orders = sum_hours_values(hourly_data_parsed["orders"], orders_views)
end
end
puts "Views by hour:"
puts hours_views.sort.map {|hour_views| "%2i\t%4i" % hour_views }
puts "Orders by hour:"
puts hours_orders.sort.map {|hour_orders| "%2i\t%4i" % hour_orders }
P.S. There's a really nice recursive version of sum_hours_values I didn't include since the iterative version is clearer to most Ruby programmers. If you're into recursion I leave it as an exercise for you. ;)

Ruby Hash Fetch Issue with Sass

I'm at a loss. Hoping somebody with more Ruby experience will tell me what is going on.
I'm calling a custom Sass function from my stylesheet like so:
$color: user_var('color')
The custom function looks like this:
module Sass::Script::Functions
def user_var(param_name)
puts options[:custom]
puts options[:custom].fetch('color')
puts options[:custom].fetch(param_name)
end
end
The result of puts options[:custom] (as expected) is this:
{"color"=>#eeeeee, "header"=>20px}
The result of puts options[:custom].fetch('color') is #eeeeee
BUT... the result of puts options[:custom].fetch(param_name) results in a "key not found: 'color' (KeyError)".
Completely baffled here.
my guess is that param_name is a symbol and the color key in the options hash is defined as a string.
this should work.
options[:custom].fetch("#{param_name}")

Using Open-URI to fetch XML and the best practice in case of problems with a remote url not returning/timing out?

Current code works as long as there is no remote error:
def get_name_from_remote_url
cstr = "http://someurl.com"
getresult = open(cstr, "UserAgent" => "Ruby-OpenURI").read
doc = Nokogiri::XML(getresult)
my_data = doc.xpath("/session/name").text
# => 'Fred' or 'Sam' etc
return my_data
end
But, what if the remote URL times out or returns nothing? How I detect that and return nil, for example?
And, does Open-URI give a way to define how long to wait before giving up? This method is called while a user is waiting for a response, so how do we set a max timeoput time before we give up and tell the user "sorry the remote server we tried to access is not available right now"?
Open-URI is convenient, but that ease of use means they're removing the access to a lot of the configuration details the other HTTP clients like Net::HTTP allow.
It depends on what version of Ruby you're using. For 1.8.7 you can use the Timeout module. From the docs:
require 'timeout'
begin
status = Timeout::timeout(5) {
getresult = open(cstr, "UserAgent" => "Ruby-OpenURI").read
}
rescue Timeout::Error => e
puts e.to_s
end
Then check the length of getresult to see if you got any content:
if (getresult.empty?)
puts "got nothing from url"
end
If you are using Ruby 1.9.2 you can add a :read_timeout => 10 option to the open() method.
Also, your code could be tightened up and made a bit more flexible. This will let you pass in a URL or default to the currently used URL. Also read Nokogiri's NodeSet docs to understand the difference between xpath, /, css and at, %, at_css, at_xpath:
def get_name_from_remote_url(cstr = 'http://someurl.com')
doc = Nokogiri::XML(open(cstr, 'UserAgent' => 'Ruby-OpenURI'))
# xpath returns a nodeset which has to be iterated over
# my_data = doc.xpath('/session/name').text # => 'Fred' or 'Sam' etc
# at returns a single node
doc.at('/session/name').text
end

Resources