Ruby Version Issue

I have a plugin which functions differently in SketchUp 8 versus SketchUp 2015 Make. This is most likely caused by the different underlying Ruby versions and not a SketchUp problem, per se, but I thought I would mention it here (perhaps this is already a well-known issue and I’m simply being redundant).

I open a text file and read it line-by-line using ‘gets’. For each line, I iterate through the characters as an array and test each value, For example:

fh = File.open('test.txt','r')
t = fh.gets
if(t[0] == 65) do something # returns true in 8 / false in 2015
if(t[0] == 'A') do something # returns true in 2015 / false in 8
fh.close

In SketchUp 8, the class type for ‘t’ is String and the class type for ‘t[0]’ is FixNum. In 2015, they are both of class String. This got me to wondering about other “minor” differences between versions that might cause issues as well.

Is there documentation somewhere that identifies these little quirks?

This is a difference between Ruby 1.8 and 2.0. Between those two versions string handling in particular had some changes - like this. In this case the change is due to Ruby 1.8 not being Unicode aware. In Ruby 2.0 you want to make sure you deal with the correct encoding for your strings and when you iterate files that you iterate per character and not per byte (unless you deal with binary files off course.)

We haven’t documented this in the SketchUp API pages because this is a Ruby change which is well documented in general in the Ruby community. If you look up what’s new in Ruby 2.0 you should find several good resources which outline the most significant changes.

(p.s. I edited your post to mark your code up as Preformatted Text so it reads easier.)

They are called “breaking changes” because they can break code.

Yes the issue you speak of was on the list. Specifically the subscript method of String, using one index argument, returning different types in the two versions.

The workaround is to use the 2 argument form:
str[ start_pos, length ]

ie: t[0,1] will return a one character String in all versions.


The SketchUp staff posted a simple list:
Ruby 1.9/2.0 Breaking Changes [PDF]

Do a Google search on “Ruby 1.9 Breaking Changes” to find more comprehensive lists on the web.

Thanks for the explanation(s) :smile:

I remember reading the list of changes a few months ago, but this part didn’t particularly stand out in my mind at the time:

'test'[0] returns 't' under 2.0 and 116 under 1.8.
Use 'test'[0,1] for compatibility. 

SketchUp is my only exposure to Ruby and I’m admittedly ignorant of most of it. I’m surprised at how much broke between releases … it must make the job of upgrading the underlying code quite interesting.

BTW, I’m working to upgrade my plugins to make them play friendlier with others … I’ve attached the example code I wrote for “Desk Activity” and would appreciate some critiquing of the structure as it relates to SketchUp. I’ve tried to follow Dan’s suggested template, but I’m not sure how well I understand it. Any advice is greatly appreciated.

desk_activity.rb (3.6 KB)

downloads aren’t working…
copy/paste, highlight the code and use

</> 

from the toolbar
john

@sam this is happening to all downloads…

I should have realized that attachments were misbehaving before I attached my file …

# desk_activity.rb - (C) 2015 by Aqualung -- Permission granted to freely use this code
# as long as this line and the preceding line are included in any derivative work(s)JRH
#
# 5/23/2015 Version 1.0
# 5/24/2015 Version 1.1 - replaced groups with component instances
# 5/25/2015 Version 2.0 - wrapped plugin with DR's suggested module framework
# 6/1/2015  Version 2.1 - modified string array test to work with 2015
#--------------------------------------------------------------------------------------
require 'sketchup.rb'
module Jimhami42
  module Desks
    class << self
# CLASS VARIABLES -----------------------------------------------------------------
      @@num_desks = 0
      @@s = []
      @@scene = 0
      @@compdef = 0
# MAIN BODY -----------------------------------------------------------------------
      def spirix_desk_activity()
        Sketchup.active_model.start_operation("Desk Activity")
        puts "Starting... (" + Time.now.to_s + ")"
        spirix_get_desk_data("c:/desks.txt")
        puts "Activities... (" + Time.now.to_s + ")"
        spirix_get_activity_data("c:/activity.txt")
        puts "Scenes... (" + Time.now.to_s + ")"
        spirix_create_desk_scenes()
        puts "Done! (" + Time.now.to_s + ")"
        Sketchup.active_model.commit_operation
      end
# GET DESK DATA -------------------------------------------------------------------
      def spirix_get_desk_data(filename)
        desks = File.open(filename,"r")
        t = desks.gets.chomp
        num_desks,dx,dy = t.split(',')
        @@num_desks = num_desks.to_i
        dx = dx.to_f
        dy = dy.to_f
        for i in 0...@@num_desks do
          t = desks.gets.chomp
          x,y = t.split(',')
          @@s[i] = [x.to_f,y.to_f,0.0]
        end
        desks.close
        comp = Sketchup.active_model.entities.add_group
        comp.entities.add_face([0,0,0],[dx,0,0],[dx,dy,0],[0,dy,0])
        comp.layer = Sketchup.active_model.layers.add("Component")
        comp.layer.visible = false
        @@compdef = comp.to_component.definition
      end
# GET ACTIVITY DATA AND CREATE INSTANCES ------------------------------------------
      def spirix_get_activity_data(filename)
        act = File.open(filename,"r")
        t = act.gets
        while(t)
          layer = Sketchup.active_model.layers.add("Scene " + @@scene.to_s)
          layer.visible = false
          for i in 0...@@num_desks do
            vec = Geom::Vector3d.new(@@s[i].x,@@s[i].y,@@s[i].z)
            trans = Geom::Transformation.translation(vec)
            inst = Sketchup.active_model.entities.add_instance(@@compdef,trans)
            inst.layer = layer
            if(t[i,1] == 'A')
              inst.material = "green"
            else
              inst.material = "red"
            end
          end
          t = act.gets
          @@scene += 1
        end
        act.close
      end
# CREATE SCENES -------------------------------------------------------------------
      def spirix_create_desk_scenes()
        for i in 0...@@scene
          Sketchup.active_model.layers["Scene " + i.to_s].visible = true
          Sketchup.active_model.pages.add("Scene " + i.to_s)
          Sketchup.active_model.layers["Scene " + i.to_s].visible = false
        end
      end
    end
# STARTUP -------------------------------------------------------------------------
    unless file_loaded?("desk_activity.rb")
      menu = UI.menu("PlugIns").add_item("Desk Activity") { spirix_desk_activity() }
      file_loaded("desk_activity.rb")
    end
#--------------------------------------------------------------------------------------
  end
end

I’d actually feared more - going from Ruby 1.8 to 2.0. Though it appear most issues people had was related to strings. For the guys that used Ruby for the web this probably hurt more as they deal with nothing but strings. Though for the SketchUp devs which in large part deal with the SketchUp API the impact was reduced.
Fortunately Ruby has switched to semantic versioning - so the Ruby 2.x branch should remain compatible. (excluding Ruby binaries off course.)

I strongly recommend setting the second argument to true in order to obtain significant performance boost when creating operations. The fact that it’s left to default to false is just a compatibility thing with SU6 and older.

Coincidentally, I’m wearing my RTFM t-shirt today … this is good to know and something I’ll be including from here on out. Thanks for the tip :slight_smile:

test.rb (8 Bytes)

Attachements should be working now, sorry about the temporary glitch

2 Likes