End of input error

WHoops! I made an error and left out the word no from the previous post. (Now fixed.)

1 Like

I’m still working on the group for the trim, but does this look better? I also have fixed the folder structure and naming, created the register file as shown, and placed icons in a “Icons” folder. Thanks!

module CaydenWilson
  module ProTrim

    extend self

    #Loading in handles
    model = Sketchup.active_model
    selection = Sketchup.active_model.selection
    entities = Sketchup.active_model.entities
    materials = Sketchup.active_model.materials
    layer_array = Sketchup.active_model.layers

    require 'sketchup.rb'
    require 'extensions.rb'

    def get_points(axis, height, thickness)
      case axis
      when X_AXIS
        [ [0,0,0], [0,0,height], [thickness,0,0], [thickness,0,height] ]
      when Y_AXIS
        [ [0,0,0], [0,0,height], [0,thickness,0], [0,thickness,height] ]
      end
    end

    def create_trim_material(materials)
      trim_material = Sketchup.active_model.materials.add "Trim"
      trim_material.color = [red, green, blue]
      trim_group.material = trim_material
    end

    def build_trim(axis, selection, height, thickness)
      #Input to gather data for trim
      prompts = ["Trim Height:            ", "Trim Thickness:", "Quarter Round:", "R:", "G:", "B:"]
      defaults = [5.0,0.5,"No",255.0,255.0,255.0]
      list = ["","","Yes|No","","",""]
      input = UI.inputbox prompts, defaults, list, "ProTrim"
      return unless input
        height,thickness,quarter_round,red,green,blue=input
      if quarter_round == 'Yes'
        quarter_round_width = 0.75
        quarter_round_height = 0.75
      end
      message = "Select edges for your trim path."
      result = UI.messagebox(message, MB_OKCANCEL)
      if result == IDCANCEL
        return
      end
      pts=get_points(axis, height, thickness)
      #Creating a face using array
      face = entities.add_face(pts)
      edges = face.edges
      connected = face.all_connected
      face.back_material = "Trim"
      material = trim.back_material
      face.material = trim
      #Selecting edges (path) to extrude on
      face.followme( selection.grep(Sketchup::Edge) )
    end

    def create_trim_layer(layer_array, entities)
      new_layer = model.layers.add("Trim")
      model.active_layer = new_layer
      name = trim
      visable = True
    end
    if !@loaded
      submenu = UI.menu("Extensions").add_submenu("ProTrim")
      toolbar=UI::Toolbar.new "ProTrim"
      cmd = UI::Command.new("Build Trim") { init() }
      cmd.small_icon = File.join(__dir__, "Icons", "IconSmall.png")
      cmd.large_icon = File.join(__dir__, "Icons", "IconLarge.png")
      cmd.menu_text = "ProTrim"
      toolbar.add_item cmd
      toolbar.show
      submenu.add_item(cmd)
      # Set the @loaded var to true so this block is only evaluated ONCE:
      @loaded = true
    end
  end
end

Firstly, many of the examples you might see, are very old and violate good programming style rules that have been accepted since.)

It looks a little better, but you’ve still missed a few places without spaces around =.

(This line [above] is also indented, but should not be.)


Secondly, whenever you call a method upon an object with dot notation, you should use parenthesis around the argument list. (The call to the UI::Toolbar.new constructor [above] is a prime example where parenthesis should always be used.)

Only global methods from module Kernel, BasicObject, and Object are allowed generally to omit parenthesis if readability permits.

It has also been proposed that private method calls of classes Module and Class, from within a module or class definition where the receiver is self or implied to be the module or class itself, can also omit parenthesis.

(This rule is referred to as “methods having keyword status” [reserved word == keyword].)
However, in my opinion, you should never be ridiculed for using parenthesis even when allowed to omit them. (The foremost Ruby coding style guide will take the opposite tack and will produce rule violations if parenthesis are used when they can be omitted.)


At the module level, insert a blank line before blocks like this and methods (or other blocks.)


spacing for readability is paramount for argument lists, arrays, hashes and expressions.
(Arrays or argument lists of numbers tend to blur together at the end of a long day.) Ie:

defaults = [5.0, 0.5, "No", 255.0, 255.0, 255.0]

As said above … You do not need to require dependent files, if this file does not use them.
And since SketchUp 2014, the SketchUp load cycle will load the 3 files from it’s "Tools" subfolder, and your code does not really need to require them anyway. IE, the calls …

    require 'sketchup.rb'
    require 'extensions.rb'

… are not explicitly needed because they will always already be loaded by the time your module is being loaded, but more so because this file doesn’t call any feature defined by those files.


And, … the code that was in the body of the init() method, again is not being used, so delete …

    #Loading in handles
    model = Sketchup.active_model
    selection = Sketchup.active_model.selection
    entities = Sketchup.active_model.entities
    materials = Sketchup.active_model.materials
    layer_array = Sketchup.active_model.layers

Local variables at the module level are unusable by the module’s methods anyway.
They’d need to be @@vars or @vars in order for any of the methods to access them.

But such assignments would need to be dynamic, because users close models and open other models. So assigning a set of “@handles” would be done at the last second, probably as the first step in a command, so that your code is sure that the handles are for objects in the active model.

A note about entities. Always prefer to work with the active_entities rather than assume the user always wants to work at the top level of the model. Ie, instead of …

    @entities = Sketchup.active_model.entities

… use …

    @entities = Sketchup.active_model.active_entities

… should be …

      result = UI.messagebox(message, MB_OKCANCEL)
      return if result == IDCANCEL

… or even better

      result = UI.messagebox(message, MB_OKCANCEL)
      return unless result == IDOK

This is called a statement with a conditional expression in modifier position.
Note also that the return statement can have an expression to return immediately following the return. Ex:

      result = UI.messagebox(message, MB_OKCANCEL)
      return false unless result == IDOK

As said above, don’t change the USER’s active layer/tag. Ruby API code doesn’t need to do this anyway as code would likely always be creating geometry inside a group or component’s definition’s entities collection (associated with “Layer0”/“Untagged”,) then creating an instance in the model and afterward setting the instance’s layer/tag.

Besides this workflow, changing the active Layer/Tag will fire off any LayersObserver objects that are active by other extensions. This is not something your code needs to have happening.

In addition the 3 local variable assignments in the method are unneeded of do nothing. This method is really a one-liner …

    # Create (if necessary) and return a reference to the "Trim" layer/tag.
    def create_trim_layer(model, tagname = 'Trim')
      model.layers[tagname] || model.layers.add(tagname)
    end

… this above example allows localized language layer/tag names to be passed into the method.
(Not all users run SketchUp in English.)