Ruby - Next if vs Next unless - ruby-on-rails

What is the difference between next if vs next unless.
My understanding of next if was that next only if variable == value but in the following code, the second next statement isn't working. next if datastore.id == 5 it is bypassing this and proceeding even if the datastore.id == 2 or datastore.id == 3 etc.,
$evm.vmdb(:ManageIQ_Providers_Vmware_InfraManager_Storage).all.each do |datastore|
next if datastore.ems_id == provider.id.to_s
next if datastore.id == 5
dialog_hash[datastore[:id]] = "#{datastore.name} on #{datastore.ext_management_system.name}"
end
But if I change the next if to next unless then it works fine.
$evm.vmdb(:ManageIQ_Providers_Vmware_InfraManager_Storage).all.each do |datastore|
next if datastore.ems_id == provider.id.to_s # Next if works here
next unless datastore.id == 5 # Next unless works here instead of next if
dialog_hash[datastore[:id]] = "#{datastore.name} on #{datastore.ext_management_system.name}"
end

I'll quickly explain next and the difference between if and unless in ruby, and proceed to suggest some things you could try to get your code to work.
if
Code would execute if the condition is true.
unless
Code would execute if the condition is false. It would work the same as:
if !(expression) { ... }
next
The program would skip the rest of the code inside the loop block, and skip to the next iteration.
In your case, if there is a next called in the first line, then the rest of the code wouldn't run.
$evm.vmdb(:ManageIQ_Providers_Vmware_InfraManager_Storage).all.each do |datastore|
next if datastore.ems_id == provider.id.to_s # if this condition is true, don't run rest of the code in this loop, and go to the next datastore
next if datastore.id == 5
dialog_hash[datastore[:id]] = "#{datastore.name} on #{datastore.ext_management_system.name}"
end
Suggestion
It's unclear from your question what you mean by if doesn't work but unless does.
From the first code snippet in the question, the last line of the code. i.e,
dialog_hash[datastore[:id]] = "#{datastore.name} on #{datastore.ext_management_system.name}"
would only run if both of the conditions above are false.
You can check where the data is unexpected, or if your initial conditions are wrong, by debugging the datastore with either a debugger or some puts statements inside the loop.
All the best.

The difference is that
next if condition calls next when the condition is true, but
next unless condition calls next when the condition is false.
When calling next with a condition like datastore.id == 5 doesn't work as expected, then the problem is not the usage of if or unless because they work the opposite way.
Instead, you need to debug why you expect datastore.id == 5 to be true and why it is not. Obviously, the condition datastore.id == 5 can only be true when datastore.id returns the integer 5. If it returns false then datastore.id might return a string with the digit "5".
I suggest adding debug output to your code to dig deeper, like this:
p datastore.id
p datastore.id.class

Additionally to answers, most popular Ruby style guide recommends to use positive instead of negative conditions
# bad
do_something if !some_condition
# good
do_something unless some_condition
Depending on this, you can choose if or unless

Related

How to wait for all Concurrent::Promise in an array to finish/resolve

#some_instance_var = Concurrent::Hash.new
(0...some.length).each do |idx|
fetch_requests[idx] = Concurrent::Promise.execute do
response = HTTP.get(EXTDATA_URL)
if response.status.success?
... # update #some_instance_var
end
# We're going to disregard GET failures here.
puts "I'm here"
end
end
Concurrent::Promise.all?(fetch_requests).execute.wait # let threads finish gathering all of the unique posts first
puts "how am i out already"
When I run this, the bottom line prints first, so it's not doing what I want of waiting for all the threads in the array to finish its work first, hence I keep getting an empty #some_instance_var to work with below this code. What am I writing wrong?
Never mind, I fixed this. That setup is correct, I just had to use the splat operator * for my fetch_requests array inside the all?().
Concurrent::Promise.all?(*fetch_requests).execute.wait
I guess it wanted multiple args instead of one array.

Is there a way to add time to a Wait class inside an If statement?

I started learning LUA a few days ago, started my own project inside Tabletop Simulator game, but I've hit a brick wall. I can't add time to a Wait class.
This is an example of what I tried:
function state_check()
--This function checks if the state of obj_1 and obj_2 is 0 or 1
end
function press_button()
--This function activates other functions based on the state of obj_1 and obj_2
i = 1 --Function starts with a wait of 1 second
if obj_1_state == 1 then --If state is 1, then the function is triggered and 1 second is added to i
Wait.time(func_1, i)
i = i + 1
end
if obj_2_state == 1 then
Wait.time(func_2, i)
end
end
I need for the function to check the first part and if true, do the second part 1 second later. If not, do the second part normally and skip the "i = i + 1".
My problem is that the function does everything at the same time. I know I'm doing something wrong, but I can't figure out what. Is there a way to create some for of gate to do everything in order or anything similar?
Your code seems correct.
I don't know what is the problem.
But I know that one of possible solutions is to follow the "callback hell" style of programming:
local function second_part(obj_2_state)
if obj_2_state == 1 then
Wait.time(func_2, 1)
end
end
local function first_part(obj_1_state, obj_2_state)
if obj_1_state == 1 then
Wait.time(
function()
func_1()
second_part(obj_2_state)
end, 1)
else
second_part(obj_2_state)
end
end
function press_button()
local obj_1_state, obj_2_state = --calculate states of obj_1 and obj_2 here
first_part(obj_1_state, obj_2_state)
end

vlc.playlist.loop automation

Basically I have this .lua code in my vlc extension, now I am having problems in setting the VLC repeat automatically to loop all in a playlist.
I have tried setting it into "all", 0, 1, 2, true, "TRUE" but it does just not set the playlist's loop value into "all".
I also cannot get the playlist loop value. I tried object.playlist().loop, vlc.playlist.loop.
I read the VLC's Lua Script and Extensions page but I still cannot get what is supposed to be that
<status>
value or any of its acceptable strings.
function trigger()
vlc.playlist.stop()
vlc.playlist.sort("random")
vlc.playlist.goto(0)
--vlc.playlist.repeat_(<status>)
--vlc.playlist.loop(<status>)
vlc.playlist.play()
end
Solved it, thanks to Vyacheslav and Piglet
this would now set the vlc playlist loop to loop all, but I cannot print or vlc.msg.info the value of playlist.loop though. But it works in the end.
function trigger()
vlc.playlist.stop()
vlc.playlist.sort("random")
vlc.playlist.goto(0)
playlist = vlc.object.playlist();
if vlc.var.get(playlist,"loop") == false then vlc.playlist.loop() end
vlc.playlist.play()
end
I have not tried it but following the documentations logic status should be either nil, true or false. If nil (or no argument) it will toggle the current state. true will enable, false disable loop or repeat.
Not sure why you expect loop to be "all".
Did you remove the -- in front of the respective line?
I assume setting both doesn't make sense. And they will toggle each other internally?
In common case, if you do not know how it properly works, it is possible to search such code in github.com.
In your case, you can use this:
https://github.com/search?utf8=%E2%9C%93&q=vlc.playlist.loop+language%3ALua&type=Code&ref=searchresults
As you can see,
you can use such code:
elseif command == "pl_loop" then
vlc.playlist.loop()
elseif command == "pl_repeat" then
vlc.playlist.repeat_()
or
elseif c == 'loop' then
if vlc.playlist.loop(v) then
vlc.playlist.repeat_('off')
end
elseif c == 'repeat' then
if vlc.playlist.repeat_(v) then
vlc.playlist.loop('off')
end

Computercraft lua change value later

ok, Im almost completely new to lua and computercraft but I have alot of creativity. I'm trying to write code that will reprint a variable every second. here is what I have so far:
display = "Loading..."
While true do
sleep(1)
term.clear()
term.setCursorPos(1,1)
print (display)
end
sleep(3)
display = "hello"
I want to use it to render a 2d game, the "display" variable would change often and thus why i want it to be updated every second.
It does indeed refresh every second when I run the code but for some reason I cant seem to get the "display" variable to change after 3 seconds to test it.
What am I doing wrong?
while true is an infinite loop. The script never reaches sleep(3).
Perhaps you want to replace while true with for i=1,3.
I am not experienced in Lua, but this might be a solution: answer on SO
In the UI thread, run:
while ((status=lua_resume(L_coroutine, 0)) == LUA_YIELD) {
semaphore_wait(); /* whatever the appropriate C# call is */
}
"Wait for response" should look something like:
while not results[my_result] do
coroutine.yield()
end
The "incoming message" function should look like the following in Lua:
results[cur_result]=parsed_message

What is syntactically wrong with this Ruby case statement?

For some reason, the when #orgs is not working :
#orgs = Organization.all.select{|a|a.active}.count
case collection.size
when 0; "No #{entry_name.pluralize} found"
when #orgs; "#{#orgs} Businesses Returned!"
else; "#{collection.total_entries} of #{#orgs} Businesses Returned!"
end
Is this syntactically accurate? It will always return the last else statement. It never catches on the second when statement, even if #orgs == #orgs.
The actual number of #orgs = 1211.
So if I make the when statement when 1211; , it still doesn't catch. Is there a syntactical mistake here?
Did you try the following syntax ?
case collection.size
when 0 then "No #{entry_name.pluralize} found"
when #orgs then "#{#orgs} Businesses Returned!"
else "#{collection.total_entries} of #{#orgs} Businesses Returned!"
end
Your syntax isn't the issue. The logic in your conditions, I think, is all messed up.
So, #orgs does not = 1211, #orgs is a collection whose size is 1211. Big difference.
This case statement will always fail because the case is collection.size, when it should probably be #orgs.size
Let's say we changed it to be:
case #orgs.size
...
Then:
Your first when statement would start working when #orgs.size == 0
Your second when statement would still fail, because #orgs.size != #orgs - and it never will. #orgs refers to the collection of objects, while #orgs.size refers to the size of that collection. Even when #orgs == nil it will still fail because #orgs.size will throw a error because you're calling the size method on a nil object, which isn't allowed.
Your else statement is always the one called because of the problems listed above, and because the way you've written it prevent any of the other cases from ever being true.
However, that's probably still not enough. It looks to me like you're trying to return singularized or pluralized text depending on how many you have. Here's the code you probably intended to write, without the semi-colon usage:
#active_orgs = Organization.all.select{|a| a.active}
#singular_name = Organization.class.name
#plural_name = #singular_form.pluralize
case #active_orgs.size
when 1
"1 active #{#singular_name} of a total #{Organization.count} #{#plural_name}!"
else
"#{#active_orgs.count} active #{#singular_name} of a total #{Organization.count} #{#plural_name}!"
end
This will pluralize even when the count is 0, so it will say 0 active Organizations of a total 125 Organizations!
... or when the value is 1 it will say 1 active Organization of a total 125 Organziations!
If you don't want Organization capitalized, you can add a call to .downcase to take care of that.

Resources