sketchucation logo sketchucation
    • Login
    ℹ️ Licensed Extensions | FredoBatch, ElevationProfile, FredoSketch, LayOps, MatSim and Pic2Shape will require license from Sept 1st More Info

    Global Object Stupidity, Ruby Question

    Scheduled Pinned Locked Moved Developers' Forum
    12 Posts 6 Posters 593 Views 6 Watching
    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

      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

        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
        • Chris FullmerC Offline
          Chris Fullmer
          last edited by

          @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

            @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
            • Chris FullmerC Offline
              Chris Fullmer
              last edited by

              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
              • fredo6F Offline
                fredo6
                last edited by

                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

                  @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
                  • fredo6F Offline
                    fredo6
                    last edited by

                    @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
                    • Chris FullmerC Offline
                      Chris Fullmer
                      last edited by

                      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

                        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

                          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

                            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
                            • First post
                              Last post
                            Buy SketchPlus
                            Buy SUbD
                            Buy WrapR
                            Buy eBook
                            Buy Modelur
                            Buy Vertex Tools
                            Buy SketchCuisine
                            Buy FormFonts

                            Advertisement