I am trying to access some text that is located in a DIV.
I need to check to see if the page holds the text so I can return a true or false.
The code I am using is below:
cancel = browser.text.include?("Current Cancelled")
if cancel == true
puts "Line item cancelled"
else
puts "****Line item not cancelled****"
end
But it returns false every time.
Here is a code snippet of what I am looking into:
I'd really recommend using Nokogiri to parse the content.
require 'nokogiri'
doc = Nokogiri::HTML('<div><span class="label">Current</span>Cancelled</div>')
doc.at('//div/span[#class="label"]/../text()').text # => "Cancelled"
(doc.at('//div/span[#class="label"]/../text()').text.downcase == 'cancelled') # => true
!!(doc.at('//div/span[#class="label"]/../text()').text.downcase['cancelled']) # => true
Something like one of the two bottom statements will get you a usable true/false.
The probable reason this isn't working is because the string you're testing for contains a newline character and a non breaking space.
This could work...
if browser.div(:text, /Current.*Cancelled/).exists?
puts "Line item cancelled"
else
puts "****Line item not cancelled****"
end
or
if browser.text =~ /Current.*Cancelled/
puts "Line item cancelled"
else
puts "****Line item not cancelled****"
end
etc.
Watir's Browser object has now the #elements_by_xpath method...
See Watir's API
Just pin-point your DIV and ask for its #text method. Pretty much like what the Tin Man suggests but without requiring nokogiri.
AFIK Watir uses internally exactly for the purpose of locating elements (it's a dependency gem that Watir installs) anyway.
I believe the fact that the text is inside a table is causing this problem.
You might consider drilling into the table doing:
cancel = browser.table(:class, 'basic-table').each { |row|
test = row.text.include?("Current Cancelled")
return test if test == true
}
Wow. That makes sense. I wonder how I
could split these up and get them to
combine for my check.
Okay, here's a really quick draft:
div = browser.table(:class, 'basic-table').div(:text, /Cancelled/)
cancel = div.exist? and div.span(:index, 1).text == 'Current'
if cancel
puts "Line item cancelled"
else
puts "****Line item not cancelled****"
end
You could also combine a few of the regular expression approaches below (mostly those from Kinofrost), with the idea of narrowing it down to looking just inside a single cell within the table. That should be faster, and less prone to a false alert should the words 'Current' and 'Cancelled' occur in that order with anything between them, elsewhere on the page.
if browser.table(:class, 'basic-table').cell(:text, /Current.*Cancelled/).exists?
puts "Line item cancelled"
else
puts "****Line item not cancelled****"
end
Related
I want to recall the case until user writes a or b. I do not want to use "case"
particularly.
I just want to get input from user but not geting something else. If he writes something else, he should need to write until he writes a or b.
str = gets.chomp.to_s
case str
when "a"
print "nice a"
when "b"
puts "nice b"
else
puts "please do it again"
end
class person
attr_accessor :name , :surname #and other attributes
end
#There will be a method here and it will run when the program is opened.
#The method will create the first object as soon as the program is opened.
#The new object that the user will enter will actually be the 2nd object.
puts "What do you want to do?
add
list
out"
process = gets.chomp.to_s
case process
when "add"
#in here user will add new objects of my class
when "list"
#in here user will show my objects
when "out"
puts "Have a nice day"
else
puts "please do it again"
end
In fact, if you look at it, many actions will be taken as a result of the user entering the correct input. what I want to tell is more detailed in this example. According to the input of the user, there will be actions such as calling methods, adding objects, etc.
I wrote most of the code on my computer. But still I couldn't solve my first problem.
Use Kernel#loop
There are a lot of ways to solve this problem, but let's start with a simple Kernel#loop wrapper around your existing code, as that's probably the easiest path forward for you.
loop do
str = gets.chomp.to_s
case str
when "a"
print "nice a"
when "b"
puts "nice b"
else
puts "please do it again"
# restart your loop when not "a" or "b"
next
end
# exit the loop if else clause wasn't triggered
break
end
Use until Control Expression
The loop construct above is pretty straightforward, but it requires you to think about where you need next and break statements for flow control. My own instinct would be to simply call a block until it's truthy. For example, the core logic could be shortened to:
str = nil; until str =~ /a|b/i do str = gets.chomp end; p str
This is a lot shorter, but it's not particularly user-friendly. To leverage this approach while making the solution more communicative and error-resistant, I'd refactor the original code this way:
# enable single-character input from console
require 'io/console'
# make sure you don't already have a value,
# especially in a REPL like irb
str = nil
until str =~ /a|b/ do
printf "\nLetter (a, b): "
str = STDIN.getch.downcase
end
puts "\nYou entered: #{str}"
While not much shorter than your original code, it handles more edge cases and avoids branching. It also seems less cluttered to me, but that's more a question of style. This approach and its semantic intent also seem more readable to me, but your mileage may legitimately vary.
See Also
IO::Console
Control Expressions
"I just want to do something until something else happens" is when you use some sort of while loop.
You can do this:
while true
str = gets.chomp
break unless str == 'a' || str == 'b'
puts "please do it again"
end
You can also use loop do:
loop do
str = gets.chomp
break unless ['a', 'b'].include?(str)
puts "please do it again"
end
puts "Nice #{str}."
Rubyists tend to prefer loop do over while true. They do pretty much the same thing.
One more thing. There's a simpler way to write out arrays of strings:
loop do
str = gets.chomp
break unless %w(a b).include?(str)
puts "please do it again"
end
puts "Nice #{str}."
It doesn't look a whole lot simpler, but if you have, say, 10 strings, it's definitely quicker to type in when you don't have to use all those quotation marks.
As your intuition was telling you, you don't need to use the case statement at all. Like trying to kill a flea with a sledgehammer. The most concise way to do your check is to check whether the input character is included in an array of the desired characters.
I am trying to make an app which if give the option to type, it types false then it skips the certain element from the list and it jumps to the next executing the same task.
That is the basic idea of the following code:
string["items"].each do |item|
p continue.to_s + "<- item"
begin
Anemone.crawl("http://" + item["displayLink"] + "/") do |anemone|
anemone.on_every_page do |page|
if continue.chomp.to_bool == false
raise "no more please"
end
request = Typhoeus::Request.new(page.url, followlocation: true)
response = request.run
email = /[-0-9a-zA-Z.+_]+#[-0-9a-zA-Z.+_]+\.[a-zA-Z]{2,4}/.match(response.body)
if email.nil?
else
p email
begin
continue = Timeout::timeout(2) do
p "insert now false/nothing"
gets
end
rescue Timeout::Error
continue = "true"
end
end
end
end
rescue
continue = true
next
end
p "---------------------------------------------------------"
end
As the code shows, if the user types false when prompted the app should skip the item and go to the next one. However what it does is: when the user types false the app skips the current item and then doesn't execute any of the code that should be executed for all of the other items except the printing ( the second line of code );
Here is how the output looks like:
$ruby main.rb
"1"
"true<- item"
#<MatchData "support#keycreative.com">
"insert now false/nothing"
false
"true<- item"
"true<- item"
"true<- item"
As I'm doing my best to show after false is entered the code does skip the certain item from the list but it also never ever executes code for the other items as it should since it is an each loop
First I thought that maybe the continue is false however as you can see from the output the continue is true which makes me wonder why does ruby skip my code?
UPDATE
Here is where the to_bool method comes from:
class String
def to_bool()
return true if self == "true"
return false if self == "false"
return nil
end
end
In your last rescue statement add:
rescue => e
puts e.message
continue = true
next
end
and inspect the output. Most likely your code is throwing an exception other than "no more please" (I expect undefined method to_bool for true:TrueClass). Note that using exception for skipping the loop element is a terrible idea. Why can't you just get rid of this rescue and do:
if continue.chomp.to_bool == false
continue = true
next
end
There are a lot of things in this code which makes it very un-ruby-like. If you want to improve it please paste it to StackExchange CodeReview page. (link in the comment).
UPDATE:
My bad, you are in nested loop, so the if statement won't work. You might look at sth similar to raise/rescue bit, namely throw/catch, see example here: How to break from nested loops in Ruby?. I still think you should post it to codereview though for refactoring advises.
As to your actual code (without refactoring). You are calling to_bool method on continue, and in your rescue block you assign true instead of 'true'. Hence your to_bool method raises exception which is then rescued same way as 'no more please' exception.
I am trying to write fast and concise code. I'd appreciate your thoughts on which is the best way to write the following code and why:
Option #1
def get_title
title = check_in_place_one
if title.empty?
title = check_in_place_two
if title.empty?
title = check_in_place_three
end
end
return title
end
Option #2
def get_title
title = check_in_place_one
title = check_in_place_two unless !title.empty?
title = check_in_place_three unless !title.empty?
return title
end
I think Option #1 is better since if the title is found by check_in_place_one, we test title.empty? once and then skip the rest of the code in the method and return. But, it looks too long. Option #2 appears better, but processes title.empty? one extra time, and unnecessary time before returning. Also, am I missing a third option?
From performance, there is no difference between the two versions of your code (besides very minor difference that may come from parsing, which should be ignorable). The control structures are the same.
From readability, if you can get away with nesting, doing so is better. Your second option is better.
It is usually better to get rid of any case that does not need further processing. That is done by return.
def get_title
title = check_in_place_one
return title unless title.empty?
title = check_in_place_two
return title unless title.empty?
title = check_in_place_three
return title
end
The last title = and return in the code above are redundant, but I put them there for consistency, which improves readability.
You can further compact the code using tap like this:
def get_title
check_in_place_one.tap{|s| return s unless s.empty?}
check_in_place_two.tap{|s| return s unless s.empty?}
check_in_place_three
end
tap is a pretty much fast method, and unlike instance_eval, its performance penalty is usually ignorable.
The following approach could be used for any number of sequential tests. Moreover, it is completely general. The return condition could be changed, arguments could easily be assigned to the test methods, etc.
tests = %w[check_in_place_one check_in_place_two check_in_place_three]
def do_tests(tests)
title = nil # Define title outside block
tests.each do |t|
title = send(t)
break unless title.empty?
end
title
end
Let's try it:
def check_in_place_one
puts "check 1"
[]
end
def check_in_place_two
puts "check 2"
''
end
def check_in_place_three
puts "check 3"
[3]
end
do_tests(tests) #=> [3]
check 1
check 2
check 3
#=> [3]
Now change one of the tests:
def check_in_place_two
puts "check 2"
'cat'
end
do_tests(tests) #=> 'cat'
check 1
check 2
#=> "cat"
If there were more tests, it might be convenient to put them in a module which would be included into a class. Mixed-in methods behave the same as those that you define for the class. For example, they have access to instance variables. I will demonstrate that with the definition of the first test method. We probably want to make the test methods private. We could do it like this:
module TestMethods
private
def check_in_place_one
puts "#pet => #{#pet}"
puts "check 1"
[]
end
def check_in_place_two
puts "check 2"
''
end
def check_in_place_three
puts "check 3"
[3]
end
end
class MyClass
##tests = TestMethods.private_instance_methods(false)
puts "##tests = #{##tests}"
def initialize
#pet = 'dog'
end
def do_tests
title = nil # Define title outside block
##tests.each do |t|
title = send(t)
break unless title.empty?
end
title
end
include TestMethods
end
The following is displayed when the code is parsed:
##tests = [:check_in_place_one, :check_in_place_two, :check_in_place_three]
Now we perform the tests:
MyClass.new.do_tests #=> [3]
#pet => dog
check 1
check 2
check 3
Confirm the test methods are private:
MyClass.new.check_in_place_one
#=> private method 'check_in_place_one' called for...'
The advantage of using a module is that you can add, delete, rearrange and rename the test methods without making any changes to the class.
Well, here's a few other alternatives.
Option 1: Return first non-empty check.
def get_title
return check_in_place_one unless check_in_place_one.empty?
return check_in_place_two unless check_in_place_two.empty?
return check_in_place_three
end
Option 2: Helper method with short-circuit evaluation.
def get_title
check_place("one") || check_place("two") || check_place("three")
end
private
def check_place(place)
result = send("check_in_place_#{place}")
result.empty? ? nil : result
end
Option 3: Check all places then find the first that's non-empty.
def get_title
[
check_in_place_one,
check_in_place_two,
check_in_place_three,
].find{|x| !x.empty? }
end
Option 2 looks good although you did a 360 degree turn with the unless !title.empty?. You can shorten that to if title.empty? since unless is equivalent to an if ! so doing an unless ! takes you back to just if.
If you're only ever going to have 3 places to look in then option 2 is the best. It's short, concise, and easy to read (easier once you fix the aforementioned whoopsie). If you might add on to the places you look for a title in you can get a bit more dynamic:
def get_title(num_places = 4)
title, cur_place = nil, 0
title = check_in_place(cur_place += 1) while title.nil? && cur_place < num_places
end
def check_in_place(place_num)
# some code to check in the place # given by place_num
end
The tricky line is that one with the while in it. What's happening is that the while will check the expression title.nil? && cur_place < num_places and return true because the title is still nil and 0 is less than 4.
Then we'll call the check_in_place method and pass in a value of 1 because the cur_place += 1 expression will increment its value to 1 and then return it, giving it to the method (assuming we want to start checking in place 1, of course).
This will repeat until either check_in_place returns a non nil value, or we run out of places to check.
Now the get_title method is shorter and will automatically support checking in num_places places given that your check_in_place method can also look in more places.
One more thing, you might like to give https://codereview.stackexchange.com/ a look, this question seems like it'd be a good fit for it.
I don't think there's any reason to get too clever:
def get_title
check_in_place_one || check_in_place_two || check_in_place_three
end
Edit: if the check_in_place_X methods are indeed returning an empty string on failure it would be better (and more idiomatic) to have them instead return nil. Not only does it allow for truthy comparisons like the above code, return "" results in the construction of a new and unnecessary String object.
Can someone help me understand how to write this case statement properly its not working and as a NOOB I have no idea how to fix it:
def hide_link?(link, mailing)
case link
when 'edit' && ['sent', 'sending', 'archived'].include?(mailing.status)
return true
when 'send_schedule' && ['sent', 'sending', 'archived'].include?(mailing.status)
return true
when 'archive' && ['archived'].include?(mailing.status)
puts "I should be in here"
return true
else 'dashboard' && ['sending', 'draft'].include?(mailing.status)
return true
end
end
Basically I want to return true when the link matches certain criteria.
I believe that if link doesn't match these criterias the method should return false. Thus:
def hide_link?(link, mailing)
case link
when 'edit'
['sent', 'sending', 'archived'].include?(mailing.status)
when 'send_schedule'
['sent', 'sending', 'archived'].include?(mailing.status)
when 'archive'
puts "I should be in here"
['archived'].include?(mailing.status)
when 'dashboard'
['sending', 'draft'].include?(mailing.status)
else
false
end
end
The construction [...].include?(mailing.status) has result true or false which will be returned as a result of hide_link? method.
Remove return.
link = "fred"
case link
when "fred"
true
else
false
end
case will return the value itself which will then be passed to the method.
Refactor of megas's version:
def hide_link?(link, mailing)
statuses_to_hide = case link
when 'edit', 'send_schedule'
%w{sent sending archived}
when 'archive'
%w{archived}
when 'dashboard'
%w{sending draft}
else
[]
end
statuses_to_hide.include?(mailing.status)
end
The conditions in the case statement all follow the same form, which suggest that there is an opportunity to eliminate some repetition, and to separate policy from implementation. The policy is the set of conditions under which the link should be hidden:
WHEN_TO_HIDE_LINK = [
['edit', %w(sent sending archived)],
['send_schedule', %w(sent sending archived)],
['archive', %w(archived)],
['dashboard', %w(sending draft)],
]
The implementation is the code that applies the policy:
def hide_link?(link, mailing)
WHEN_TO_HIDE_LINK.any? do |link_value, mailing_statuses|
link_value == link && mailing_statuses.include?(mailing.status)
end
end
Explanations below the fold.
%w
%w is a way to specify a list of strings without typing all those quotes and commas. This:
%w(sent sending archived)
is equivalent to this:
['sent', 'sending', 'archived']
any?
Enumerable#any? passes each element of the array to the block (the bit between the do and the end). If the block ever returns truthy, then the result of any? is true; otherwise, the value of any? is false.
array decomposition
Did you notice that although each element of WHEN_TO_HIDE_LINK is an array, the block passed to any? does not take an array? You might expect that you'd have to do this:
WHEN_TO_HIDE_LINK.any? do |when_to_hide|
link_value = when_to_hide[0]
mailing_statuses = when_to_hide[1]
...
but Ruby will decompose array into parts for you. Here's one way to do it:
WHEN_TO_HIDE_LINK.any? do |when_to_hide|
link_value, mailing_statuses = when_to_hide
...
When there is an array on the right side of the = and comma-separated variables on the left, Ruby decomposes the array into its elements and assigns them to the variables separately.
But Ruby can make things even easier:
WHEN_TO_HIDE_LINK.any? do |link_value, mailing_statuses|
...
This is equivalent to either of the preceding two fragments.
How would i go about this?
I tried
Net::HTTP.new(#site).head('/').kind_of? Net::HTTPOK
but I receive this:
error: in `kind_of?': class or module required (TypeError)
I think there is something wrong with my code, can you please take a look at it? I know its very messy and pretty bad, i'm a first year programming student and i'm learning. If you have any ideas for improvement, let me know!
Also, there is something messed up with the ends contained within. For some reason i get an unexpected end error when i try to end all the defs.
Here is my code in context:
def begindownload
require 'net/http'
puts "Enter the URL of the site that you want to rip images from (use www.*website*.com/folder/file.html or other extension format):"
while #site = gets.chomp
puts "Querying " + #site
if Net::HTTP.new(#site).head('/').kind_of? Net::HTTPOK == true
puts "Site is online!"
else
puts "Site is offline. Try again."
end
end
end
In your code you are using the line:
if Net::HTTP.new(#site).head('/').kind_of? Net::HTTPOK == true
The == is being evaluated first causing Net::HTTPOK == true to be evaluated to false. The rest of the statement then becomes:
if Net::HTTP.new(#site).head('/').kind_of? false
Causing your TypeError.
You don't need to check == true when you are doing an if statement. If the statement after the if evaluates to true then the if will be evaluated. Checking if a boolean == true is considered bad form anyway.
Have you tried .instance_of instead?