trouble with this case statement Ruby - ruby

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.

Related

Check whether string match or not

I have a string and an array with some strings.
as below
hostname = TETDC3DBE01
Array = ['WEB','APP','STR','DBE']
I want to find whether that hostname match with any of the array element or not?
When I'm trying with below code getting output
no
no
no
no
Here is loop repeating each and every element on array. I want check that hostname with single check on array the produce the output either yes or no only.
Array.each do |x|
if hostname.match(x)
puts "yes"
else
puts "no"
end
end
Given this fixed Ruby code:
hostname = 'TETDC3DBE01'
array = ['WEB','APP','STR','DBE']
Where if you want to find all elements in array that match as a substring of hostname your code should work. The more minimal matching system is probably:
array.select { |x| hostname.match(x) }
# => ["DBE"]
Using a tool like puts to produce output isn't always very useful because that "yes" or "no" text can't be acted upon by more code. Try and think of Ruby programs as a chain of transformations, where this selects all matches, and later you can print them, like this:
puts array.select { |x| hostname.match(x) }.join(',')
# => DBE
Check out Array#any? method.
It passes each element of the collection to the given block. The method returns true if the block ever returns a value other than false or nil. If the block is not given, Ruby adds an implicit block of { |obj| obj } that will cause any? to return true if at least one of the collection members is not false or nil.
If instead a pattern is supplied, the method returns whether pattern === element for any collection member.
In your case:
hostname = 'TETDC3DBE01'
['WEB','APP','STR','DBE'].any? do |x|
hostname.match(x)
end
or even if you actually mean equal by match:
hostname = 'TETDC3DBE01'
['WEB','APP','STR','DBE'].any?(hostname)
Lets take your code to fix it.
hostname = "TETDC3DBE01"
arr = ['WEB','APP','STR','DBE']
arr.each do |x|
if hostname.match?(x)
puts "yes"
else
puts "no"
end
end
match gives array of result and
match? gives you true or false value
I wouldn't use regexp in this case. A simple String#include? is probably faster. Furthermore any? will return true if any of the elements in the array leads is matching.
hostname = 'TETDC3DBE01'
array = ['WEB','APP','STR','DBE']
array.any? { |x| hostname.include?(x) }
#=> true
Regular expression made real easy:
hostname = "TETDC3DBE01"
array = ['WEB','APP','STR','DBE']
re = Regexp.union(array)
hostname.match?(re) # => true

Not understanding the Boolean Logic to my method

I write the method capitalized(word) to be funneled into the .each function.
i use bang! to make the function work correctly but i don't understand why its working correctly.
The part that is really throwing me off is the !capitalized word line if statement. I don't understand the logic and how the method returns the answers that it does.
!false returns false in the method... why? And !true it returns true.
Whats the best way to understand this concept.
I have tried taking out the bang and putting it back in to see its effect.
And it just confuses me. Thank you for your help.
# A name is valid is if satisfies all of the following:
# - contains at least a first name and last name, separated by spaces
# - each part of the name should be capitalized
#
# Hint: use str.upcase or str.downcase
# "a".upcase # => "A"
def is_valid_name(str)
name = str.split(' ')
if name.length < 2
return false
end
name.each do |word|
if !capitalized(word)
return false
end
end
return true
end
def capitalized(word)
if word[0] == word[0].upcase && word[1..-1]== word[1..-1].downcase
return true
else
return false
end
end
puts is_valid_name("Kush Patel") # => true
puts is_valid_name("Daniel") # => false
puts is_valid_name("Robert Downey Jr") # => true
puts is_valid_name("ROBERT DOWNEY JR") # => false
The purpose of the part of the code you struggling with, is to check whether all of the elements of the name are capitalised. There are two ways you can approach that problem:
Find any element that is not capitalised.
If such element exists, that means name is not valid, so method is_valid_name should return false. This is the approach used in you code:
def is_valid_name(str)
#...
name.each do |word| #1
if !capitalized(word) #2
return false #3
end
end
return true #4
end
Iterate over every part of the name
Check if word is capitalized. We want to break only when we find invalid (not capitalized) word. That occurs when capitalized(word) returns false: so condition if !capitalized(word) is actually if !false, which is equal to if true.
We entered, that means our word is not valid, so we return false from is_valid_name method.
Check if all elements are capitalized
This is more straightforward solution. We just want to check, if method capitalized(word) returns true for all of the elements of the name.
To achieve that, we can use method all?, which returns true if condition in the block returns true for every element; otherwise false. So we can replace all the code above with single line:
name.all?{|word| capitalized(word) }
Final implementation of the validation method can look like that:
def is_valid_name(str)
name = str.split(' ')
name.length < 2 && name.all?{|word| capitalized(word)}
end
Hope it helps you!
!false returns false in the method... why?
I'm assuming you mean this one:
if !capitalized(word)
return false
end
"If capitalize returns false, why do we return false", this question? Because if a word is not capitalized, the name is invalid. That's the given business logic.

Understanding Ruby code from Puppet module openstack/nova

I do not know Ruby very well and was hoping someone could help me understand some of what this bit of code is doing.
newproperty(:value, :array_matching => :all) do
desc 'The value of the setting to be defined.'
def insync?(is)
return true if #should.empty?
return false unless is.is_a? Array
return false unless is.length == #should.length
return (
is & #should == is or
is & #should.map(&:to_s) == is
)
end
I am not sure at all what this line ...
newproperty(:value, :array_matching => :all) do
... is doing. Is defining a function that accepts two parameters: value and array_matching? It is a loop? And I do not understand what the :array_matching => :all is all about.
Next is ...
desc 'The value of the setting to be defined.'
... Is this some sort of built in documentation? Next is this bit:
def insync?(is)
return true if #should.empty?
return false unless is.is_a? Array
return false unless is.length == #should.length
return (
is & #should == is or
is & #should.map(&:to_s) == is
)
I guess that a function called "insync" is being defined. Not sure what the '?' means. Also I guess #should is some global variable declared in the parent scope.
Thanks
I'll try to make assumptions and answer this as best I can from the info provided.
You probably have a function newproperty(x, y) <- accepts 2 arguments, somewhere in the associated model or helper. It's taking those inputs from some user interaction that assigns the :value and the :array_matching => :all is based on that :value.
desc is not a native Ruby function. It must be defined somewhere. For instance, this code would run:
def desc(x)
puts x
end
desc 'The value of the setting to be defined.'
It's a little unconventional but it would work.
The ? in def insync?(is) is part of the function name. Ruby is intended to be a very English-like language and since many functions evaluate to true or false, it's easier to read sometimes when you just make your function a question in itself.

Ruby - Populate and Array with returned method values

So, pretend we have the following three methods that check a grid to determine if there is a winner, and will return true if there is.
def win_diagonal?
# Code here to check for diagonal win.
end
def win_horizontal?
# Code here to check for horizontal win.
end
def win_vertical?
# Code here to check for vertical win.
end
I would like to push the returned values of each method into an Array instead of literally using the method names. Is this possible?
def game_status
check_wins = [win_vertical?, win_diagonal?, win_horizontal?]
if check_wins.uniq.length != 1 # When we don't have only false returns from methods
return :game_over
end
end
What you are looking for will indeed work in ruby.
def hello_world?
"hello world!"
end
a = [hello_world?]
Prints out
=> ["hello world!"]
Hope that helps. IRB is your friend when you wonder if something is possible in Ruby :-)
Simpler way (and very readable) yet:
def game_status
win_vertical? || win_diagonal? || win_horizontal?
end
If, for example, win_vertical? returns true, the other algorithms won't even need to run. You return immediately.
Or, if you need to know in which way the user won, I mean, if you need to preserve the results of all methods after they ran, you can use a hash, like:
{:vertical => win_vertical?, :diagonal => win_diagonal?, :horizontal => win_horizontal?}
This solution, like the array one, is worse than the first one above for it runs all algorithms all the time. If they are complex, you may have a problem. =)
You can do something like this when you really want to store all return values in an array:
def game_status
check_wins = [win_vertical?, win_diagonal?, win_horizontal?]
return :game_over if check_wins.any?
end
For readability I would prefer:
def game_status
return :game_over if win_vertical? || win_diagonal? || win_horizontal?
end

Elegant Loop Elsing in Ruby

I have to write a Ruby method that:
Iterates through an array, doing Foo if one of the elements matches a certain condition.
If none of the array elements matched the condition, do Bar thing.
In any other language, I'd set a Boolean variable before entering the loop and toggle it if I did Foo. The value of that variable would tell me whether I needed to Bar. But that feels unRubyishly inelegant. Can anybody suggest a better way?
Edit Some really good answers, but they don't quite work because of a detail I should have mentioned. The something that Foo does is done to the array element that matches the condition. Also, it's guaranteed that at most one element will match the condition.
Do any of the items match? If yes, then do something, not involving the matching item.
if items.any? { |item| item.totally_awesome? }
foo "we're totally awesome!"
else
bar "not awesome :("
end
Grab the first matching item. If it exists, then do something, with the matching item.
awesome_item = items.find { |item| item.totally_awesome? }
if awesome_item
foo "#{awesome_item.name} is totally awesome!"
else
bar "no items are awesome :("
end
Grab all matching items. If the array has anything in it, then do something with all matching items.
awesome_items = items.find_all { |item| item.totally_awesome? }
if awesome_items.any?
foo "#{awesome_items.size} totally awesome items!"
else
bar "no items are awesome :("
end
You could do it like this:
if array.any? { |elem| elem.condition }
foo
else
bar
end
From the doc, Enumerable#any does the following:
Passes each element of the collection to the given block. The method returns true if the block ever returns a value other than false or nil.
What you want is Enumerable#find
Ex:
element = array.find { |x| x.passes_requirements? }
element ? element.foo! : bar
idx = the_array.index { |i| conditional(i) }
if idx
modify_object(the_array[idx])
else
no_matches
end
Edit: modified based on new question criteria.
found_index = nil
my_array.each_with_index.detect { |elem, i| elem.condition? && found_index = i }
if found_index.nil?
do_not_found_case
else
my_array[found_index] = some_conversion(elem)
end
This isn't as pretty, but it gets the job done and still short-circuits on the first match.
Thanks to everybody who tried to answer the question. None of you supplied an answer I found appropriate, but you all forced me to think about how you do things the Ruby way (which was the main point of this exercise!) and helped me come up with this answer:
I need to make use of the fact that iterators in Ruby are just methods. All methods return a value, and (oddly enough) each returns a useful value. If the iteration completes, it returns the collection you were iterating over; if you use break to terminate iteration early it returns nil (or an optional argument).
So, in a Boolean context, the whole loop is true if it completes and false if you break out. Thus
bar if array.each do |element|
if fooable(element) then
foo(element)
break
end
end

Resources