Context Menu Errors


#1

My context menus are working fine however if a user of the plugin right clicks on a line (not a group or component) an error gets thrown in the Ruby console.

My code is:

unless file_loaded?(File.basename(__FILE__))
		UI.add_context_menu_handler do |context_menu_medeek1|
  
  			if Sketchup.active_model.selection[0].name.include?('TRUSS_ASSEMBLY_')
   
				# context_menu_medeek1.add_separator
    				context_menu_medeek1.add_item("Edit Truss Assembly") {
      					
     					# UI.messagebox("#{MDK_TRUSS} Assembly - Editing coming soon...")

					Sketchup.load(File.join(this_dir,"MEDEEK_ROOF_TRUSS.rbs"))
					Medeek_Engineering_Inc_Extensions::MedeekTrussPlugin::MedeekMethods.roof_truss_edit_menu pluginversion, pluginserial, pluginlang
    				}
  			end
		end

		UI.add_context_menu_handler do |context_menu_medeek2|
  
  			if Sketchup.active_model.selection[0].name.include?('RAFTER_ASSEMBLY_')
   
				# context_menu_medeek2.add_separator
    				context_menu_medeek2.add_item("Edit Rafter Assembly") {
      					
     					 UI.messagebox("#{MDK} Rafter Assembly - Editing coming soon...")
    				}
  			end
		end

		UI.add_context_menu_handler do |context_menu_medeek3|
  
  			if Sketchup.active_model.selection[0].name.include?('FLOOR_ASSEMBLY_')
   
				# context_menu_medeek3.add_separator
    				context_menu_medeek3.add_item("Edit Floor Assembly") {
      					
     					# UI.messagebox("#{MDK} Floor Assembly - Editing coming soon...")

					Sketchup.load(File.join(this_dir,"MEDEEK_FLOOR_TRUSS.rbs"))
					Medeek_Engineering_Inc_Extensions::MedeekTrussPlugin::FloorTruss::MedeekMethods.floor_truss_edit_menu pluginversion, pluginserial, pluginlang
    				}
  			end
		end
		
	end

The error is:

Error: #<NoMethodError: undefined method `name' for #<Sketchup::Edge:0x0000000fee9e68>>
c:/users/administrator/appdata/roaming/sketchup/sketchup 2017/sketchup/plugins/medeek_truss_ext/medeek_load.rbs:314:in `block in <module:MedeekTrussPluginModuleLoader>'
SketchUp:1:in `call'
Error: #<NoMethodError: undefined method `name' for #<Sketchup::Edge:0x0000000fee9e68>>
c:/users/administrator/appdata/roaming/sketchup/sketchup 2017/sketchup/plugins/medeek_truss_ext/medeek_load.rbs:329:in `block in <module:MedeekTrussPluginModuleLoader>'
SketchUp:1:in `call'
Error: #<NoMethodError: undefined method `name' for #<Sketchup::Edge:0x0000000fee9e68>>
c:/users/administrator/appdata/roaming/sketchup/sketchup 2017/sketchup/plugins/medeek_truss_ext/medeek_load.rbs:341:in `block in <module:MedeekTrussPluginModuleLoader>'
SketchUp:1:in `call'

#2

Obviously the issue is that there is no name parameter associated with certain geometry.

I need to make my conditional probably first determine whether this is a group or not. I just wanted to put this out there so that I don’t inadvertently create something that somehow bogs down the model or SketchUp.

Context menus are not something I deal with on a daily basis so I’m trying to avoid beginner errors.


#3

I thought I could grep to see if the selection is a group but that doesn’t work either:

Sketchup.active_model.selection[0].grep(Sketchup::Group)


#4
s = Sketchup.active_model.selection[0]
s.name if s.respond_to? :name

john


#5

The key piece of code here is the .respond_to? :name

Works like charm. Thank-you for squaring me away.


#6
  1. You do not need 3 separate context menu handler blocks. One will do.

  2. You need not qualify the block menu object reference as it is local to the block and will not ever clash with any other reference. (And your handlers should always be within your coding namespaces (modules and classes, so there is no need to worry about clashing references here.)

  3. Menu item object command blocks should always call a method so that you can tweak the code (as you develop it,) and simply reload the code that redefines the command’s method.

  4. The way you’ve organized this the commands are dependent upon the load, so should be wrapped in a begin ... rescue ... else clause.


module Medeek_Engineering_Inc_Extensions::MedeekTrussPlugin::FloorTruss

  extend self

  def edit_menu(menu_symbol, filename, version, serial, language)
    begin
      Sketchup.load(File.join(__dir__,filename))
    rescue
      raise
    else
      MedeekMethods.send( menu_symbol, version, serial, language )
    end
  end

  unless file_loaded?(File.basename(__FILE__))
    
    UI.add_context_menu_handler do |context_menu|

      selection_set = Sketchup.active_model.selection

      if selection_set.single_object?

        if selection_set.first.respond_to?(:name)
        
          obj_name = selection_set.first.name
   
          if obj_name.include?('TRUSS_ASSEMBLY_')
            context_menu.add_item("Edit Truss Assembly") {
              edit_menu(
                :roof_truss_edit_menu,
                "MEDEEK_ROOF_TRUSS.rbs",
                pluginversion, pluginserial, pluginlang 
              )
            }
          elsif obj_name.include?('RAFTER_ASSEMBLY_')
            context_menu.add_item("Edit Rafter Assembly") {
              edit_menu(
                :rafter_edit_menu,
                "MEDEEK_RAFTER.rbs",
                pluginversion, pluginserial, pluginlang 
              )
            }
          elsif obj_name.include?('FLOOR_ASSEMBLY_')
            context_menu.add_item("Edit Truss Assembly") {
              edit_menu(
                :floor_edit_menu,
                "MEDEEK_FLOOR.rbs",
                pluginversion, pluginserial, pluginlang 
              )
            }
          end

        end # group and component handling

        # Handle other kinds of objects here ...

      end # single object selected

      # Handle multiple selections of objects here ...

    end # context menu handler block

  end # run once upon first load block

end # wrapping namespaces

#7

Indexing without knowing whether an element exists at that index! One of the most common error sources.

The return value of Array#[](index) is [Object, nil], either an object or nil. Your code must handle both cases, or the second case must be provable impossible (because in every possible program flow, the array would have already an element in this position).

Similarly, Sketchup::Selection#[](index) returns [Sketchup::Drawingelement, nil], once checked whether it is not nil, you can call any method of drawing element, but you would still have to check whether it is a ComponentInstance.


#8

Okay, I’ll be the first to admit that I don’t have any idea what I’m doing when it comes to the context menu code.

What is the best and most efficient and bullet proof code and I will implement it.


#9

I gave an example that tests for an selection set with 1 member (above.)
You can add other conditional expressions to handle multiple objects …

elsif selection_set.size > 1

… and then when the selection set is empty nothing happens.

Or up near the top, before the if statement, you could add a bailout statement …

break if selection_set.empty?

Also note that …

selection_set.first

… is the same as …

selection_set[0]