How can I use a text file as rubycode in SketchUp

I am developing a script that selects sub-components based on multiple Layers.
See a piece of code below:

    
    @complist[]

    ents.each do | bl_entity | 
	if bl_entity.is_a? (Sketchup::ComponentInstance)
	subcomp = bl_entity.definition.entities.grep(Sketchup::ComponentInstance)
	subcomp_onlayer = subcomp.find_all {|comp|
	comp.layer.name == ("Layer1" || "Layer2" || "Layer3")
	}
	subcomp_onlayer.each {|compo|
	@complist << compo.definition.name
	}
	end
    end

This works but I have to pre-program all the Layers that the script must run.
I want to save the layer names as described above in a text file for the user to adjust.
The script must read the layer names from the text file and the text file is stored in the parameter (@qlayerlist)

    subcomp_onlayer = subcomp.find_all {|comp|
	comp.layer.name == eval(@qlayerlist)
	}

With eval I can read a text file as a ruby ​​code, but is not very secure.

Are there other options for solving this?

Eval of an external file is indeed not secure - it could be modified to contain malicious code. Why not save the list as either one layer name per line or comma-delimited or some other plain text alternative, then open the file, read its lines, and load the strings into your list of layers?

1 Like

Off course, this is done before by @TIG :slight_smile:
https://sketchucation.com/pluginstore?pln=TIG_LayersToList
https://sketchucation.com/pluginstore?pln=TIG_LayersFromList

I would store the data in something like JSON - then serialize between Hash and a JSON string. Then have your extension/script process the JSON data and use that to transform your model.

2 Likes

This statement does not do what you think.
("Layer1" || "Layer2" || "Layer3") is a logical expression that will always evaluate as true because the 1st subexpression "Layer1" will evaluate as true.

(In Ruby ALL objects evaluate as true except the nil and false objects. This means even 0, an empty string and an empty array evaluate as true when tested because they are still valid objects.)

So, the whole statement will always be false because layer.name always returns a String that will never be equivalent to true. In effect your conditional expression for the find_all is: false, and nothing will ever be found, always returning an empty array as subcomp_onlayer.


You can use a regular expression …

# Define a regular expression outside loops ...
one_of_target_layers = /(Layer1|Layer2|Layer3)/i

ents.each do | bl_entity | 
  if bl_entity.is_a?(Sketchup::ComponentInstance)
    subcomp = bl_entity.definition.entities.grep(Sketchup::ComponentInstance)
    subcomp_onlayer = subcomp.find_all { |comp|
      comp.layer.name =~ one_of_target_layers
    }
    subcomp_onlayer.each { |compo|
     @complist << compo.definition.name
    }
  end
end

If you don’t understand regular expressions (they are complex,) …
then you can use an array of layer names for comparison instead …

# Define an array of layer names outside loops ...
target_layers = ["Layer1","Layer2","Layer3"]

ents.each do | bl_entity | 
  if bl_entity.is_a?(Sketchup::ComponentInstance)
    subcomp = bl_entity.definition.entities.grep(Sketchup::ComponentInstance)
    subcomp_onlayer = subcomp.find_all { |comp|
      target_layers.any? {|ln| ln == comp.layer.name }
    }
    subcomp_onlayer.each { |compo|
      @complist << compo.definition.name
    }
  end
end

Thank you all for your input :smiley:

The script currently works on the basis of an external file that is read in as an array.

3 Likes