Why is variable undefined


#1

I load my plugin:

#Require win32api
if RUBY_VERSION < "2.0.0"
  require File.join(PLUGIN_ROOT, "ene_viewport_resizer", "Win32API.so")#Bundled with plugin for SU 2013.
else
  require "Win32API"#Standard libraries included in SU 2014+.
end  

GetActiveWindow = Win32API.new("user32.dll", "GetActiveWindow", "", "L") unless defined?GetActiveWindow  
GetWindowRect = Win32API.new("user32.dll", "GetWindowRect", "LP", "I") unless defined?GetWindowRect
ShowWindow = Win32API.new("user32.dll", "ShowWindow", "LI", "I") unless defined?ShowWindow
MoveWindow = Win32API.new("user32.dll", "MoveWindow", "LIIIII", "I") unless defined?MoveWindow
GetAncestor = Win32API.new("user32.dll", "GetAncestor", "LI", "I") unless defined?GetAncestor
GA_ROOTOWNER = 3
@@hwnd
hwnd = GetActiveWindow.call
puts "START hwnd:"
puts hwnd

This prints hwnd valid value.

Then I have function defined

def setViewPort(w=400,h=200);
  puts "hwnd:"
  puts hwnd
end

and when I call setViewPort from ruby console after the plugin been loaded, this prints undefined local variable or method hwnd for Object …

Why is it undefined and how to make it remember the hwnd detected during plugin load?


#2

In ruby the method body can’t access local variables accessed outside of it. Multiple methods can have the same locsal variables names without collisions. If you want to actually point to the same variable you need to use class variables (or class instance variables).

In this case however I think I’d call GetActiveWindow inside the method instead of using a variable since the active window may not have been SketchUp while the firtst code ran (users can switch focus to another window, e.g. to check their mail, while SU starts up).

Also I would recommend you to read a Ruby style guide as your code is often quite hard to read. In Ruby snake_case is used for variables and method names. Using CamelCase makes it appear as a constant, even if the first character is lower case. Also semicolon is redundant and distracting in Ruby.


#3

That’s why I need to save the hwnd when the plugin is first time loaded. To be able to switch it back when I call the functions working with Sketchup root window.

So I will try to create class for this and an instance of it.

I have my own style which makes me better to read the code.


#4

Class variables works in modules too in case creating instances doesn’t make sense in this context.


#5

Do you say, that I must create module? When I create class and new instance white the load of the plugin, the instance is removed from memory when the script finished/program reached the end?

Do I need to create folder for one file or can I create module in one file? Create it and then execute it or add it to Sketchup all in one file?

Edit:
I could use

ex = SketchupExtension.new("My modul", "myplugin.rb"))
ex.hwnd = ...
Sketchup.register_extension ex, true

to remember the whnd


#6

You must use a module whenever you write any ruby code (except code you run from the terminal) to avoid collisions. Look up the documentation for how modules work.


#7
module Bara
  extend self # creates both class and module methods

  # module constant
  HELLO = 'Hello from Barra' unless defined?( HELLO )

  # use the constant in any method in your namespace
  def say_hi
    UI.messagebox( HELLO )
  end # say_hi

end #Barra

# Usage from outside of code
# use the method
# Bara.say_hi
# or use the constant
# UI.messagebox( Bara::HELLO )

john


#8

Why do you need extend self? Is this really needed?

Thanks.


#9

not really need in such a basic example…

I always use it in sub modules and it’s a habit I guess…

john


#10

Take a look at other plugins (there exists already a lot of source code) to learn Ruby and to learn best practices.

Both modules and classes are containers for code (namespaces). You must use one of them to keep your code separate from other plugins. Classes are modules that can be instantiated. If it does not make sense to instantiate something (because there exists only one of that kind), use a module. See John’s answer.

Everything that is not needed anymore is automatically removed from memory. That is also true for instances. Something is not anymore used if there exists no variable referencing it. Learn what scope is: scope is the region of a program where a variable name is valid. Different variable types have different scope. Example:

# No reference → instance can be removed from memory
MyClass.new()

def method1
  # Local variable → lives only until the end of the current method; or outside of methods until the end of the scope/script.
  my_instance = MyClass.new()
end

module MyModule
  # Module variable → lives permanently in the scope of the module. 
  # Because of this, you must ensure it always has a valid value. 
  # For example don't set it to nil when another part of the program assumes it is not nil.
  @module_variable = 42

  def self.method1(value)
    @module_variable = value
  end

  def self.method2()
    # It still exists here!
    return @module_variable
  end
end


#11

Seems I cannot access HELLO directly calling Bara.HELLO . So I must create method for this always, right?


#12
Bara::HELLO # it's not a method so you need ::

#13

No, it still dosn’t work for me. Either the variable or method is undefined or the get method is undefined when I call it from ruby console. The code is longer so I pasted it externally:

pasted

ShadowsModifier.get_hwnd
Error: NoMethodError: undefined method get_hwnd for ShadowsModifier:Module

ShadowsModifier::hwnd
Error: NoMethodError: undefined method hwnd for ShadowsModifier:Module

I tried to define it as whnd or @whnd in the module.


#14

To define class methods (or module methods), write “def self.method_name”. Otherwise you are creating class instance methods and there is no such things for modules as they cannot be instanced.


#15

I am blind. I have watched that many times in your plugin but didn’t realized I miss it in my code.

Edit: but wait, the guys above also skipped the def self.methodname syntax…

Edit: I had serious error/mistake in the code bellow:

if RUBY_VERSION < "2.0.0"
  require File.join(PLUGIN_ROOT, "ene_viewport_resizer", "Win32API.so")#Bundled with plugin for SU 2013.
else
  require "Win32API"#Standard libraries included in SU 2014+.

I remove the end from else block. This resulted in error which I corrected in the way that I have add end just before the end#module block :smile:


#16

that’s why I used extend self in my own code…

when you questioned it I went back a tried without and it worked so I edited…

but it only worked because it was still in the same session, doh…

extend self creates an instance method and a class method…

def self.some_method is just a class method…

john


#17

#18

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.