Why in the code below the following puts message still display on Ruby Console while the method located above it named exit_tool has a return?
module RafaelRivera
module Test
class Main
def activate
puts "I expect this to appear in Ruby Console"
exit_tool
puts "Why this message appears in Ruby Console when method exit_tool has a return in it?"
end
def exit_tool
Sketchup.send_action("selectSelectionTool:")
return
end
end # class
end # module
end # module
model = Sketchup.active_model
model.select_tool RafaelRivera::Test::Main.new
Because Sketchup.send_action merely queues up an action for execution at the next time SketchUp deems āsafeā. I donāt know that SketchUp has ever fully defined what āsafeā means, but certainly not until the current tool yields control back from the current Tool callback method (āactivateā in this case).
Also, return inside a method just returns from that method, in this case passing control back to activate.
With the code below, I can make the exit_tool method work.
module RafaelRivera
module Test
class Main
def activate
puts "I expect this to appear in Ruby Console"
return if exit_tool
puts "Not displayed in Ruby Console anymore, which is what I want "
end
def exit_tool
UI.beep
UI.messagebox("Thanks for using the tool")
Sketchup.send_action("selectSelectionTool:")
return true
end
end # class
end # module
end # module
model = Sketchup.active_model
model.select_tool RafaelRivera::Test::Main.new
Sketchup::Tools.pop_tool()
Popping the tool is supposed to have the advantage of restoring the previous Rubytool, but it doesnāt seem to be working at this time.
However, a Ruby Tool class is an event driven object. It would be the user that instigates the deactivation of the tool. I cannot see any reason why you wish to exit the tool from itās activate() method. The activate method needs to return in order for the user to do anything with the tool.
So Iād think that there would be a key press or menu item or toolbar button (or something) which would actually make the call to the exit_tool() method.
module RafaelRivera
module Test
class Main
def activate
puts "#{Module.nesting[0].name} Tool Activated."
reset()
end
def reset()
@toolstate = 0
# ... other initialization of state ...
end
def deactivate(view)
UI.messagebox("Thanks for using the tool.\nTool Deactivated.")
return true # This value will get returned to the SketchUp
# engine, but *may* not get used for anything AFAIK.
end
def exit_tool
UI.beep
# Either:
Sketchup.active_model.select_tool(nil)
# Or:
#Sketchup.active_model.tools.pop_tool
# The following lines will get executed AFTER deactivate() ...
puts "Exiting tool ..."
return true
end
def onCancel(reason, view)
if reason == 0 && (@toolstate.nil? || @toolstate == 0)
exit_tool()
else # Reinitialize the tool to it's initial state
reset()
view.invalidate
end
end
if !@loaded
UI.menu("Plugins").add_item("RafaelRivera: Test Tool") {
Sketchup.active_model.select_tool( RafaelRivera::Test::Main.new )
}
#
@loaded = true
end
end # class
end # module
end # module
If the tool is being activated in a scenario where things are not ready, then an extension should instead create a UI::Command with a validation proc method to ensure that the tool cannot be activated (ie, graying | disabling of menu items and toolbar buttons.) in such scenarios.
Also is is bad coding form to create local variables in the toplevel ObjectSpace ...
Ie ā¦
model = Sketchup.active_model
model.select_tool RafaelRivera::Test::Main.new
I know youāll say it is just for a test, or that Ruby require has built-in cleanup of toplevel local variables when loading files ⦠but (a) itās better to train yourself to always follow good form and (b) if pasting this into the Ruby Console the built-in cleanup that in the require() method does not happen. (So, your global variable model propagates into all objects.)
⦠so the test example above uses a test menu item so that everything can happen normally with the main SketchUp application window having the focus instead of the Ruby Console window.
⦠because activate just calls exit_tool⦠which in return returns nothing (or nil). Every function at some point returns, it would be weird if the callee would also stop the callerā¦
The syntax for returning a value from a method varies from language to language. In Ruby, return can be omitted and the last evaluated value is returned.
return only affects the method enclosing it.
return is implicit with most programming languages. In code like the following, return is not needed:
def exit_tool
Sketchup.send_action("selectSelectionTool:")
return
end
If there is only one return āpathā in a method, returning a static value doesnāt infer any additional information. In the below method, true will always be returned.
def exit_tool
Sketchup.send_action("selectSelectionTool:")
return true
end
Most methods can be written such that return is never used. One might consider it to be a convenience statement. For instance, both the following are equivalent:
def meth1
# code block a
if x
# code block b
end
end
def meth2
# code block a
return unless x
# code block b
end
Using return in lambdas or procs has specific rules.
The method always returns something, you have no control over it. By default a method returns the value of the last statement. Best practice is to just ignore the return value in the caller if you donāt need a return value.
As @MSP_Greg and @jim_foltz suggest, the answer depends on the ācontractā that the method has with its caller. That is, does the caller expect a meaningful return value. Ruby always returns a reference to an object; there is no way to prevent this from happening. The only question is whether the caller expects to do anything with the value.
When the answer is yes, sometimes it is appropriate to return nil as a special value indicating that the method failed in some legitimate, anticipated way. Returning nil is cheaper than raising an exception, which can matter if the method is called from within a loop.
When a method is executed purely for side effects, not for the return value, it may be legitimate to return nil to prevent anyone from ever misunderstanding this aspect of the contract.
It is never appropriate for a method to always return the same, hard-wired value no matter what happened. Thatās a waste of effort.
Except ⦠if it is an instance method of a class, and the method isnāt written in such a way that would return a meaningful value, ⦠I in this case then consider returning the instance itself so that the method call can be used in call chaining. Ie ā¦
obj.method.another_method.etc
This may also be appropriate for modules used primarily as mixin modules for classes.
However, setter instance methods using ā=ā suffix always by convention should return the argument as set or raise an appropriate exception.
But a normal module or class method doesnāt normally return itself. (I suppose it can, but it would be very rare.)