Global Variable?

I’ve never used true global variables in Ruby because the SketchUp team have helped me steer away from that (ie. $somevar) possible disaster.

However I would like to make a variable accessible across two different modules:

module Medeek_Engineering_Inc_Extensions::MedeekWallPlugin::Settings

  class MedeekMethods
  	class << self

and here:

module Medeek_Engineering_Inc_Extensions::MedeekWallPlugin::Wall

  class MedeekMethods
  	class << self

What is the suggested way to handle this?

A mixin module of shared module @@ vars within the Plugin submodule.

The setup is just like the following example, but instead of constants in the shared mixin module, you define @@vars, and then use include(SharedVarsModule) in each of your 3rd level submodules.

When you mixin modules, it does not create new vars or constants in the mixee modules/classes. Instead it creates proxy lookups directly to the mixin module’s vars and constants. (The mixin module becomes a pseudo superclass to anything that includes it, and as such appears in the mixee’s ancestry chain.)

1 Like

If I understand all of this correctly I create a separate .rb file with the “@@globalsettingsread” variable defined within it and then use the include statement listed above in each of my submodules that need access to this variable?

You can also use a class instance variable in a module and create your own accessors.

module MyModule
  @var = nil

  def var
    @var
  end

  def var=(v)
    @var = v
  end

end

There is a common theme you can see running through the various replies: to be safe, an item of data needs to belong to some user-defined object (be “encapsulated”). The issue with globals is that they don’t really belong to any object - if you prefer, they belong to everything - which opens the door for everything to alter them, causing side-effects in other code.

In a nutshell I would like to encapsulate this variable in the MedeekWallPlugin module but be able to modify and read it within to two sub-modules: Wall and Settings.

One file or three files, … whatever number of files is not the point.
(Ie, the separation of code into multiple files has it’s own reasons, foremost is maintainability and organization.)


The key here is namespacing using modules, and having one shared module that holds the shared variables.
The sharing is activated using the Module class’ include() method.

So, for a single file example, using your module names …

# "Medeek_WallPlugin/WallPlugin_SharedVars.rb"
module Medeek_Engineering_Inc_Extensions
  module MedeekWallPlugin

    module SharedVars
      @@some_var ||= ["string1","string2","string3"]
      @@some_switch = false unless defined?(@@some_switch)
    end

    module Settings
      include(SharedVars) 
      # THIS module can see other constants defined
      # at the same scope it is defined
    end

    module Wall
      include(SharedVars)

      if !@@some_switch
        # Create a menu item perhaps ?
        # Actually change the var in the shared module:
        @@some_switch = true # all mixee's see the change !
      end

      class MedeekMethods
        # The grandparent namespace to this one:
        GRANDPARENT = Module::nesting[2]
        include(GRANDPARENT::SharedVars)
        def self.get(index)
          @@some_var[index]
        end
      end

    end

  end
end

Then at the the console after pasting in the above …

Medeek_Engineering_Inc_Extensions::MedeekWallPlugin::Wall::MedeekMethods.get(2)
#=> string3

Note, in order to use the Module::nesting class method, you must define the modules in the normal nested manner (as shown above.) It will not work (in the file in which it is called,) if you use the shortcut manner as shown in your first post.

This only means that if you prefer the shortcut format, then you’d have to use a fully qualified module reference for the include() call … ie …

include(Medeek_Engineering_Inc_Extensions::MedeekWallPlugin::SharedVars)

Once the SharedVars module is defined within it’s parent module, you an then also include the submodule in it’s parent, so that the parent module can share the variables as well as any other class or module. Ie …

# "Medeek_WallPlugin/WallPlugin_SharedVars.rb"
module Medeek_Engineering_Inc_Extensions
  module MedeekWallPlugin

    module SharedVars
      @@some_var ||= ["string1","string2","string3"]
      @@some_switch = false unless defined?(@@some_switch)
    end

    include(SharedVars)
 
    # ... the rest as shown above ...

  end
end

Then at the console …

Medeek_Engineering_Inc_Extensions::MedeekWallPlugin.class_variable_get(:@@some_var)[0]
#=> string1

If this seems weird, note that in the Ruby core it is common to have shared constants in submodules, that are mixed into their parent module or class, so that other custom namespaces can also include() them.
An example is the class File which has a mixin submodule File::Constants that it itself includes. This allows coders to also include these constants into their own namespaces for use in making method calls to File class objects.

I think the “class instance variable” approach would require “self.” in front of the method names, I didn’t try it out. Instead of instance variables, use a class variable (which in this case, is inside of a module object):

module MyModule
      @@data = "something"
      
      def self.get_data
         return @@data
      end
end 

Note the keyword self to make it accessible outside of the module. You could return the clone of the class/module variable, but it would be good programming practice not to modify the reference to it.

“class instance variable” is an oxymoron. There is no such thing.
I believe you meant to say "class attribute" approach …

I thought that Ruby core was going to implement class / module attribute methods but I do not yet see it in Ruby 2.2.4 docs. It may still be a gem or a library ?

ie… I’m thinking there are automation methods such as (or similar) …

class_attr(:user_name,:company)
class_attr_reader(:privilege_level)

… etc.

ADD:

There is a gem I find … class_attr | RubyGems.org | your community gem host
… and it’s docs: Documentation for class_attr (0.1.3)
It modifies Ruby’s core class Class, but for a shared environment like SketchUp, could be rewrapped into a mixin module and then coders would only include it in the classes where they need class attributes.


Ruby on Rails has it built-in, viz:

I checked Ruby core 2.5.1 and it does not have it, so it must be Rails I was thinking of.

This is a whole lot more complicated than I imagined, I thought I might just be able to declare an instance variable within the second level module and then it be available within all other nested modules and classes.

What is so hard to understand about a shared mixin module ?

You have no difficulty grasping how the Math module can be included by any class or module anywhere, written by anyone, so that it’s nifty math methods can be shared.

Same principle, except you’d be sharing the variables.

That thought makes no sense. It is not what instance variables are designed for.

However, constants are accessible as you say … examine …

module Medeek_Engineering_Inc_Extensions
  module MedeekWallPlugin

    OPTIONS ||= Hash[:opt1,"string1",:opt2,true,:opt3,false]

    module Wall
      def self.option1
        puts "Option 1 is: #{OPTIONS[:opt1]}"
      end
    end

  end
end

Then at the console …

Medeek_Engineering_Inc_Extensions::MedeekWallPlugin::Wall.option1
#=> Option 1 is: string1

After giving it some more thought I realized that perhaps a global variable is not really needed. What I am trying to do is prevent my plugin from having to keep reading in all of the global parameters from the registry or .plist file everytime it want to create a wall or edit a wall or anything else for that matter.

Once I’ve read in the global parameters for a session then I set my global settings read flag:

@Globals_read = true

At the top of my check_global_settings method I enclose all of the lookups within this conditional:

unless @Globals_read

This is all fine and dandy however the user then might fire up the global settings icon and want to make some modifications to the default parameters during the session.

In this case, the next time a user goes to draw or edit a wall I need the plugin to recheck the global parameters so I need the @Globals_read variable to be false.

My solution is to reset the flag (instance variable) at the top of the settings method with this line:

# Reset Global Settings Flag for Wall Module

Medeek_Engineering_Inc_Extensions::MedeekWallPlugin::Wall::MedeekMethods.reset_global_settings_flag

Which then calls this small chunk of code:

module Medeek_Engineering_Inc_Extensions

module MedeekWallPlugin
module Wall

	
	class MedeekMethods
  	class << self


def reset_global_settings_flag

	@Globals_read = false

end


	end # << self
	end # MedeekMethods Class

	
	end # Wall


	end # MedeekWallPlugin
end # Medeek Module

Does my code seem reasonable or in any way inefficient?

The global parameters are set within a different module:

module Medeek_Engineering_Inc_Extensions

	module MedeekWallPlugin

	module Settings
	


##############################
#
# Class Methods of Plugin
#
##############################

class MedeekMethods
  	class << self

_My code goes here...._

end # << self
	end # MedeekMethods Class

	
	end # Wall


	end # MedeekWallPlugin
end # Medeek Module

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