• Login
sketchucation logo sketchucation
  • Login
ℹ️ GoFundMe | Our friend Gus Robatto needs some help in a challenging time Learn More

Global Object Stupidity, Ruby Question

Scheduled Pinned Locked Moved Developers' Forum
12 Posts 6 Posters 582 Views
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    MartinRinehart
    last edited by 25 Sept 2009, 13:04

    Was cleaning up my use of window, the global object, in my JavaScript. Put all the data into vismap.this and vismap.that, for instance.

    This is just plain dumb, isn't it? My webdialog has a "window" global object. Your webdialog has another "window." You and I can choose identical "global" names, but they are attached to separate global objects. No problem. Am I missing something?

    Now, I've read a bunch about avoiding Ruby name conflicts. Could someone give this an explanation? Write it as if your audience knows very little, so I can understand it.

    Thanks.

    Author, Edges to Rubies - The Complete SketchUp Tutorial at http://www.MartinRinehart.com/models/tutorial.

    1 Reply Last reply Reply Quote 0
    • M Offline
      MSP_Greg
      last edited by 25 Sept 2009, 13:52

      Martin,

      I knew you'd get back to this...

      This topic could almost be 'how do you like to code?', as there's lots of thoughts on it, much of it not Ruby specific.

      Some things I've seen and done --

      1. Wrap everything in modules with names like MR1.0, etc
      2. Prefix all public functions and class defs with something like MR1.0
      3. Make all methods and 'getters/setters' in classes private if there is no reason for them to be called from outside the class. Same thing for functions and class defs in modules
      4. I don't use global objects. Many coders never use them. I use '@@' variables in the module that loads everything
      5. We're not, but pretend we're writing code for the government, and everyone wants to break/hack it...

      I wrote a lot of my code with class variables, and at present on Windows that seems to be fine. For instance, the active model would be a class variable. It works now, but I don't know about mac's, and SU could change to allow multiple drawings in one instance, and hence, using class variables that way could break. When I get to it, I intend to rewrite my code. Put simply, at present, the Ruby environment is scoped to a single model. What if that changes?

      HTH,

      Greg

      1 Reply Last reply Reply Quote 0
      • C Offline
        Chris Fullmer
        last edited by 25 Sept 2009, 14:44

        @martinrinehart said:

        This is just plain dumb, isn't it? My webdialog has a "window" global object. Your webdialog has another "window." You and I can choose identical "global" names, but they are attached to separate global objects. No problem. Am I missing something?

        I think the problem is that the global variable points to an object. And it can only point to one object. So if you name your global that same as someone else, then you are playing a dangerous game. The ruby that gets loaded first will define the global to point to their window object. But then the next ruby will load and redefine the global to point to their window object. Now the first ruby still thinks that the global is pointing to its winow object, but its not. So it tries to interact with its window object, but it is the wrong window object.

        At least that is my understanding.

        Chris

        Lately you've been tan, suspicious for the winter.
        All my Plugins I've written

        1 Reply Last reply Reply Quote 0
        • M Offline
          MartinRinehart
          last edited by 25 Sept 2009, 16:41

          @msp_greg said:

          I knew you'd get back to this...

          Looks like you were there before me.

          You explained the fixes, but not the problem. Is the problem this: there is a single Ruby workspace. If my Ruby loads and assigns to "foo", then your Ruby is loaded and it assigns to "foo" my "foo" and your "foo" are the same and I lost the race by coming in first?

          So JavaScript, which is legendary for its global object issues, in this instance has lots of window "globals" and they're all private. It's Ruby that has global object issues.

          @chris fullmer said:

          I think the problem is that the global variable points to an object. And it can only point to one object.
          Chris

          Watch out! In JavaScript in a browser "window" is the global object, so these three are all the same.

          
          foo = 123;
          var foo = 123;
          window.foo = 123;
          
          

          The first two are just shorthand for the third.

          But the "global object" is not so global. If your webdialog is flying along side mine, we both have a separate "window", so our JavaScripts have no conflicts. In fact, if you use frames, each frame has a "window" object so "window.foo" could exist and be different in each frame.

          Author, Edges to Rubies - The Complete SketchUp Tutorial at http://www.MartinRinehart.com/models/tutorial.

          1 Reply Last reply Reply Quote 0
          • C Offline
            Chris Fullmer
            last edited by 25 Sept 2009, 17:34

            Yes, well its the same idea. In JS, each window is separate from the other. And in SketchUp, each SketchUp instance is different than the others. So you can run 2 instances of SU side by side, and the globals of your script in one window don't mess up the globals in another.

            But within a single instance of SU, all globals are in fact global. And if one script changes a global, then i rightly messes up anyone else's identically named globals. Its why they are called globals.

            If you make a window object, and point a global variable to it. Then someone else comes along and re-points that global variable to their window object, your window object still exists, it just has nothing pointed at it. And no way to access it.

            Lately you've been tan, suspicious for the winter.
            All my Plugins I've written

            1 Reply Last reply Reply Quote 0
            • F Offline
              fredo6
              last edited by 25 Sept 2009, 17:42

              This is just a matter of scope of the script engine.

              You will agree that if you run 2 instancesof Sketchup, the global variables are not shared (because Sketchup runs two different Ruby environment instances)

              In Java script, the scope of the script engine is at the level of the browser session (materialized by 'window').

              Anyway, Greg'advice is very relevant. You can always code without global variables. This said, Ruby'strength (or weakness) is that the exception are the constants, among which are the Module and Class names. All SU Ruby scripters should maybe maintain a table of namespace (module name) to avoid clashes.

              Fredo

              1 Reply Last reply Reply Quote 0
              • M Offline
                MartinRinehart
                last edited by 25 Sept 2009, 20:00

                @unknownuser said:

                All SU Ruby scripters should maybe maintain a table of namespace (module name) to avoid clashes.

                Yes, and if you find a good "how to convert your Ruby to a module" explanation, please send it my way. I tried "module vismap" at the start and "end" at the end and found out that was just the beginning.

                Author, Edges to Rubies - The Complete SketchUp Tutorial at http://www.MartinRinehart.com/models/tutorial.

                1 Reply Last reply Reply Quote 0
                • F Offline
                  fredo6
                  last edited by 25 Sept 2009, 20:21

                  @martinrinehart said:

                  @unknownuser said:

                  All SU Ruby scripters should maybe maintain a table of namespace (module name) to avoid clashes.

                  Yes, and if you find a good "how to convert your Ruby to a module" explanation, please send it my way. I tried "module vismap" at the start and "end" at the end and found out that was just the beginning.

                  Then you need either:

                  • to prefix your methods with the module name (or self)
                  • or you create a class, and then create an instance of that class to use the method.

                  Fredo

                  1 Reply Last reply Reply Quote 0
                  • C Offline
                    Chris Fullmer
                    last edited by 25 Sept 2009, 21:10

                    Here's an example of a script where I've wrapped my script into a module. Like Fredo says, you then have to define each method by using "def module_name.method_name" or "def self.module_name". I've bolded the 3 things to look out for.

                    1. Define the module.
                    2. Define methods correctly
                    3. Call the methods correctly

                    ` module Clf_face_to_component
                    def Clf_face_to_component.main

                    (code here)

                    end #method
                    end #module

                    UI.menu("Plugins").add_item("Faces into Components") { Clf_face_to_component.main}`

                    Chris

                    Lately you've been tan, suspicious for the winter.
                    All my Plugins I've written

                    1 Reply Last reply Reply Quote 0
                    • J Offline
                      Jim
                      last edited by 26 Sept 2009, 13:18

                      For example, there is no top-level "active_model" - it is Sketchup.active_model. So if we ask Ruby what Sketchup is, we see it is a Module.

                      Sketchup.class
                      Module.
                      

                      Which, if we were to define it ourselves, might look like this:

                      
                      module Sketchup
                        def Sketchup.active_model # or self.active_model
                           return TheActiveModel
                        end
                      end
                      
                      

                      It's a way to collect related methods and avoid over-writing someone else's methods and variables.

                      You can still use classes if they are appropriate, just define them in the module.

                      
                      module ME
                        TWO_PI = 2.0 * Math;;PI # < Module-level constant
                        class Model
                        end
                      end
                      
                      

                      which would then be access externally as model = ME::Model And the constant can be access by: value = ME::TWO_PI

                      Hi

                      1 Reply Last reply Reply Quote 0
                      • M Offline
                        MartinRinehart
                        last edited by 26 Sept 2009, 18:07

                        Thanks, guys.

                        
                        # vismap.rb
                        
                        require 'sketchup'
                        
                        module Vismap
                        
                        class Vismap_Model
                        
                        	def initialize()
                        		@model = Sketchup.active_model
                        		@layers = @model.layers
                        		@scenes = @model.pages
                        	end
                        
                        	def layers()
                        		return @layers
                        	end
                        
                        	def layer_names()
                        		names = []
                        		@layers.each { |lr| names.push(lr.name) }
                        		return names
                        	end
                        
                        	def scenes
                        		return @scenes
                        	end
                        
                        	def scene_names()
                        		names = []
                        		@scenes.each { |s| names.push(s.name) }
                        		return names
                        	end
                        
                            # return "VIVV...", one "V"isbl or "I"nvisbl for each scene in each layer
                            def getVisibles()
                            	answer = 'V' * (@scenes.count*@layers.length)
                            	i = 0
                            	@scenes.each do |s|
                        			s.layers.each do |lr|
                        				loc = Vismap.locate( lr.name, layer_names() ) * @scenes.count
                        				loc += Vismap.locate( s.name, scene_names() )
                        				answer[loc..loc] = "I"
                        			end
                        			i += 1
                            	end
                            	return answer
                        
                            end # getVisibles()
                        
                            # JSON to send to webdialog
                            def getJson()
                            	ret = '{ '
                        		begin
                        			ret += 'layers;' + Vismap.namesJson( self.layer_names )
                        			ret += ', scenes;' + Vismap.namesJson( self.scene_names )
                        			vis = self.getVisibles()
                        			ret += ", vis;'" + vis + "'"
                        			ret += ' }'
                        		rescue
                        			errMsg = $!.to_s
                        			errMsg.gsub!( '<', '&lt;' )
                        			vis = 'vis;\"Error - ' + errMsg + '\"'
                        			ret = '{ layers;\"\", scenes;\"\",' + vis + ' }'
                        		end
                            	return ret
                        
                            end # getJson()
                        
                        	# given "VI" string (see getVisibles) sets visibilities
                        	def setVisibles( vis )
                        
                        		sn = 0 # scene number
                        		@scenes.each do |s|
                        
                        			ln = 0 # layer number
                        			@layers.each do |lr|
                        
                        				vn = ln*@scenes.count + sn # vis number
                        				v_or_i = vis[vn..vn]
                        
                        				s.set_visibility( @layers[ln], v_or_i == 'V' )
                        				ln += 1
                        
                        			end # layers.each
                        
                        			sn += 1
                        
                        		end # scenes.each
                        
                        	end # of setVisibles()
                        
                        end # of class Model
                        
                        class Bitmap
                        =begin
                        JavaScript returns the new list of Visibles and Invisibles as a bit map encoded in a string. The first three characters encode the length of the string. Each following character encodes six bits of the map. CHR(48) == 000000; CHR(49) == 000001; CHR(50) == 000010; CHR(51) == 000011; etc.
                        =end
                        	@@vismap_powers = [ 32, 16, 8, 4, 2, 1 ]
                        	@@base = 48
                        
                        	def initialize( map )
                        		@map = map
                        		self.getLength() # sets @length
                        		self.getVis()    # sets @vis
                        	end # end of initialize()
                        	
                        	def length()
                        		return @length
                        	end
                        	
                        	def vis()
                        		return @vis
                        	end
                        	
                        	def getVis()
                        		@vis = ''
                        		bits = @map[3,@map.length - 3]
                        		(0..bits.length-1).each do |i|
                        			word = bits[i]
                        			word -= @@base;
                        			@vis += self.getVisFromWord( word )
                        			@vis = @vis[0, @length]
                        		end
                        	end
                        	
                        	def getVisFromWord( word )
                        		v = ''
                        		@@vismap_powers.each do |p|
                        			v += ( p & word ) > 0 ? 'V' ; 'I'
                        		end
                        		return v
                        	end
                        	
                        	def getLength()
                        		@length = 64 * 64 * ( @map[0] - @@base )
                        		@length += 64 * ( @map[1] - @@base )
                        		@length += @map[2] - @@base;
                        	end
                        	
                        end # of class Bitmap
                        
                        def self.check( len )
                        	nlrs = $vismap_model.layers().length
                        	nsns = $vismap_model.scenes().count 
                        	ok = ( len == (nlrs * nsns) )
                        	
                        	UI;;messagebox(
                        		"Wrong number of layers and/or scenes \n" +
                        		"Click \"Get Data from Model\" and try again." ) if ( !ok ) 
                        			
                        	return ok
                        end
                        
                        def self.launchWebDialog
                        
                        	wd = UI;;WebDialog.new( "Layer/Scene Visibility Map", true, 
                        			'vismap_v1.0', 400, 300, 100, 500, true )
                        
                            wd.add_action_callback( "refresh" ) do |js_wd, msg|
                        		reloadModel()
                            	json = $vismap_model.getJson
                            	script = 'rubyReturned( "' + json + '" );'
                        		# puts script
                            	js_wd.execute_script( script )
                            end
                        	
                            wd.add_action_callback( "newVis" ) do |js_wd, msg|
                        
                        		reloadModel()
                        		
                        		unless msg.nil?() 
                        			map = Bitmap.new( msg )
                        			$vismap_model.setVisibles( map.vis ) if Vismap.check( map.length() ) 
                        		end
                        				
                        	end # of newVis()
                        	
                        	pathname = Sketchup.find_support_file( 'vismap.html', 'Plugins/vismap/' )
                            wd.set_file( pathname  )
                            wd.show()
                        
                        end # launchWebDialog()
                        
                        def self.reloadModel()
                        	$vismap_model = Vismap_Model.new
                        end
                        
                        # true if any member of array matches item
                        def self.is_in( item, array )
                        	return self.locate( item, array ) > -1
                        end
                        
                        # index of item in array (-1 == not found)
                        def self.locate( item, array )
                        	i = 0
                        	array.each do |a|
                        		return i if (a == item)
                        		i += 1
                        	end
                        	return -1
                        end
                        
                        # convert array of names to JSON array
                        def self.namesJson( names )
                        
                        	ret = '[ '
                        	start = true
                        	names.each do |n|
                        		unless start
                        			ret += ', '
                        		else
                        			start = false
                        		end
                        		ret += quote( n )	
                        	end
                        	ret += ' ]'
                        	return ret
                        
                        end # namesJson()
                        
                        # convert name (which may contain embedded quotes) to name in quotes
                        def self.quote( name )
                        	name.gsub!( '"', '\"' )
                        	name.gsub!( "'", "\'" )
                        	#	name.gsub!( "\", "\\\\" ) # how do you do this in Ruby?
                        	return "'" + name + "'"
                        end
                        
                        launchWebDialog()
                        
                        end # module Vismap
                        
                        # end of vismap.rb
                        
                        

                        Author, Edges to Rubies - The Complete SketchUp Tutorial at http://www.MartinRinehart.com/models/tutorial.

                        1 Reply Last reply Reply Quote 0
                        • N Offline
                          NewOne
                          last edited by 26 Sept 2009, 19:21

                          I think this can help to understand modules. Since a few hours ago, I did not had any idea about what modules are. This gave me a clue.
                          I hope others will find it enlighting too. 💭
                          http://www.rubyfleebie.com/an-introduction-to-modules-part-1/

                          1 Reply Last reply Reply Quote 0
                          • 1 / 1
                          1 / 1
                          • First post
                            4/12
                            Last post
                          Buy SketchPlus
                          Buy SUbD
                          Buy WrapR
                          Buy eBook
                          Buy Modelur
                          Buy Vertex Tools
                          Buy SketchCuisine
                          Buy FormFonts

                          Advertisement