Creating my first ruby script using ChatGPT

Hello folks. I’m sure you’ve heard about ChatGPT, an artificial inteligence able to deliver text and code. Well, the thing is I don’t know how to write a singe line of code, but after some requests, I finally get a fully functional ruby script made by the IA. It’s really simple, it paints the selected faces with random colors, but I find amazing how an AI can deliver a code that actually works.

ChatGPT delivered some code but it worked slow, so I asked if it was possible to improve it, and I got a faster and improved extension. What a time to be alive.

As a developers, what are your feelings about this kind of technology?

I’m attaching a .rbz file, and here’s the code I get from ChatGPT:

# Import the SketchUp API.
require "sketchup.rb"

# Define a class for the extension.
class RandomColorPaint
  # This is a constant for the maximum value of a color component (256 in this case).
  MAX_COLOR_VALUE = 256

  # This is a custom method that will be called when the extension is activated.
  def activate
    # Get the current model and the selection set.
    model = Sketchup.active_model
    selection = model.selection

    # Use the #grep method to find only the faces in the selection.
    faces = selection.grep(Sketchup::Face)

    # Select only the faces that are visible.
    visible_faces = faces.select { |face| face.visible? }

    # Loop over the visible faces and paint them with random colors.
    visible_faces.each do |face|
      # Generate a random color and set it as the material for the face.
      color = generate_random_color
      face.material = color
    end
  end

  # This method generates a random color and returns it.
  def generate_random_color
    r = rand(MAX_COLOR_VALUE)
    g = rand(MAX_COLOR_VALUE)
    b = rand(MAX_COLOR_VALUE)
    Sketchup::Color.new(r, g, b)
  end
end

# Create a new instance of the RandomColorPaint class.
paint_tool = RandomColorPaint.new

# Get a reference to the Extensions menu.
extensions_menu = UI.menu("Extensions")

# Add a new menu item for your extension to the Extensions menu.
extensions_menu.add_item("Paint with random colors") do
  # When the menu item is clicked, activate your extension.
  paint_tool.activate
end

random-colors.rbz (813 Bytes)

2 Likes

Here’s how the ruby script works:

extension

3 Likes

I have no idea about ChatGPT, but your code is not an extension.
It is a snippet - a good start :wink: -, but it does not follow many rules that are required for to become an Extension. Zipping it to rbz file is not enough…

You can e.g. start go through on this:
https://developer.sketchup.com/developers/welcome

Examine the:
Ruby API
( especially check the Examples and Tutorials section)

Check the
Sketchup Extension UX guidelines

Get some link from Dan’s collection:
https://forums.sketchup.com/t/ruby-learning-resources-wikilists/22861

(I moved your topic to right category)

1 Like

Sorry! As I said, I don’t know how to write code. The thing I wanted to discuss was the fact an AI can actually write code. I’m not really interested in creating new extensions, there’s some great people out there doing it!
Just changed the title to “my first ruby script”, hope it fits better with the topic.

3 Likes

Yes … but it’s only a partial success. As @dezmo said, it breaks rules and is not an extension yet.

So my question back at ya’ is, … (assuming that this AI can learn,) … how can we teach this AI what rules it broke so it corrects the output of it’s code ?

Ie :

  • All extension code (in all it’s files) must to be within an extension submodule that is within a top level unique namespace module.
    Meaning that wrapping an extension in a top level class is a big no-no. Only the Ruby Core and Library should ever do this.
    But also, a class is for defining a code object that will be instantiated with multiple copies, each likely holding state data unique to the instance. Since a given extension will only ever have 1 of itself, then it should instead have been defined as a module. (FYI, the Module class is actually the superclass of class Class so the latter inherits all the functionality of it’s superclass ancestor. Meaning that the are almost the same, but a module does not have the extra instancing that in this case is unnecessary.)
    But, if needed, an extension submodule may define within it any unique class definitions that it needs to do what it does. Observer or Tool classes are an example.

  • The extension package needs to be in the correct file/folder hierarchy consisting of an extension registrar script, a subfolder of the same name as the registrar and the extension’s file(s) inside the extension subfolder.

  • The naming of the registrar script and the extension folder should be Namespace_ExtensionName. So for example here, let us assume the “Alsomar” is unique (no one else in the SketchUp world is using it, and that it does not violate some entity’s copyrights or trademarks.)
    The norm for naming the registrar file would be Alsomar_RandomPaint.rb and the extension subfolder would be Alsomar_RandomPaint for the folder. (ie the same name without the .rb file extension.
    The actual files in the subfolder can follow any naming standard the author chooses. But all files should be wrapped up in top level module Alsomar and within it, submodule RandomPaint.

  • The term “tool” is specific to an object that the user interacts with using a pointing device and usually has it’s own cursor design. The term “activate” is best used with a true SketchUp Tool class object.
    What your AI wrote is actually a simple command, not a tool.

  • Lastly, the AI wrote …

    # Import the SketchUp API.
    require "sketchup.rb"
    

    This does not import the API. It simply loads and evaluates a file named "sketchup.rb" that (since v2014) is already loaded by the time any extension begins loading. This file defines a handful of global methods and variables that these days, experienced coders avoid using.
    This misnomer comes from old examples or from programmers coming from other coding languages, and don’t (or didn’t at the time) know Ruby very well.
    Anyway, the SketchUp API is written in C and compiled along with the application, so it is loaded before any of the extensions ever are evaluated.
    However, mainly this statement (loading the "sketchup.rb" file) can be used by IDEs to load API stubs files for autocompletion or debugging.
    But it does not “import the API” nor “load the API hooks”.


Now, yes it resulted in code that can actually run and do what you asked, but it also has no defensive clauses at all.

For example, bailing out (not calling the command method) if the selection set is empty and/or has no faces to process.

Some of this validation can be done in a validation proc attached to the menu item. The item can be grayed out when the selection cannot be acted upon.

Anyway, the generated code could be easily fixed manually in a code editor by someone who knows how.

But how to teach the AI how so manual editing is not needed ?


One thing that the AI got wrong was the treatment of SketchUp surfaces. When adjoining faces share a softened edge, they are considered part of the same surface. All the faces in a surface should be painted with the same material.

It’s also better to create a Material object and reuse this when painting multiple faces. The AI generated code does not check the Materials collection to first see if there is a matching material before creating a new color object. (Inefficiency that most users abhor.) The code will likely cause a unique material for every face painted.

3 Likes

It’s interesting.

I’m seeing it to be very useful to explore concepts, bulk out the outline of what you want to do. I’ve also seen it be useful to take a piece of code and have it explained, some times it can identify what algorithm is being implemented. I’ve also toyed myself in using it to translate algorithms that you normally find explained in math notation (which I’m not good at reading) into example code.

Where I’m more hesitant is; the final details. If you are looking to productise the project, you need to be able to know enough to spot when the output is wrong. I’ve seen many examples where the output looks coherent’ but ends up not being correct. It can appear to get things right up to 80-90% percent, but that last 10% would be key. How do you spot that if you use it to produce output that you aren’t that familiar with yourself?

For instance this script:

    # Select only the faces that are visible.
    visible_faces = faces.select { |face| face.visible? }

What is it doing here? It’s filtering what was in the Selection set, trying to exclude entities hidden, yet in the selection? Doesn’t seem applicable. And is it confusing Ruby’s select with SketchUp selection set?

ChatGPT delivered some code but it worked slow, so I asked if it was possible to improve it, and I got a faster and improved extension.

The script would run faster if the model changes was wrapped in model.start_operation/model.commit_operation. And it’s something that would be required for an extension on EW. That’s part of my concern with this tech, it gets things close, but not quite close enough. Which can be ok if you further can complete it. I’m a bit worried that we’ll over the years see a lot of content produced that not quite fully there. And then that itself becomes the basis for further machine learning input. A normalization of the average mean.

Then there is also the concern about where it sourced the information? There’s been a good amount of talk where these machine learning tools have sourced code that violates it license. So I’d be highly weary of that.

Overall I see it as a tool with great potential, but I also fear how hard it will be to find original and even correct information in the years to come.

2 Likes

The AI is available here: https://chat.openai.com, I think there’s an API so I guess it can be trained.

@DanRathbun I asume you’re right about what can be improved and what’s wrong with the code. My intention attaching the code was to give context to my post. If someone like me can create a basic script, I guess an experienced user can get real help from the AI.

Well, I’ve been playing with the AI trying to add and remove some features, so I guess it’s some junk code.

That’s my biggest fear. A lot of garbage coming in the next years, so the legit content would be hard to find.

Hold on to your papers!

3 Likes

Hi there! Despite I wasn’t really interested in the code itself, I thought it would be fun to give the valuable feedback I received to the AI and see what happens.

After readind the feedback provided by @dezmo, the answer was this:
It looks like this developer is suggesting that the code you shared is not a complete SketchUp extension, but rather just a snippet of code. A SketchUp extension is a self-contained package that adds new functionality to the SketchUp application. In order to create a valid extension, you will need to follow the rules and guidelines outlined in the SketchUp API documentation and the SketchUp Extension UX guidelines.

The developer also provided some links to helpful resources, including the Ruby API documentation, which includes examples and tutorials that can help you learn how to create SketchUp extensions. I would recommend going through these resources to learn more about creating extensions for SketchUp.

After readind the feedback provided by @DanRathbun, the answer was this:
I think this developer is correct in their assessment of the code you shared. They provided a detailed and accurate explanation of the issues with the code, and their suggestions for improving the code would be helpful in making it a valid and functional SketchUp extension. I believe that following their suggestions would be a good idea if you want to create a working SketchUp extension using this code.

In addition, they pointed out that the code does not include any defensive clauses to handle situations where the selection set is empty or does not include any faces, and that it does not properly handle surfaces in SketchUp models. These are important considerations when creating a SketchUp extension, and addressing them would be necessary in order to make the code work correctly and reliably.

Overall, I think that the developer named Dan provided a thorough and accurate analysis of the code, and their suggestions for improving the code would be helpful in making it a valid and functional SketchUp extension.

After readind the feedback provided by @tt_su, the answer was this:
It looks like this developer raises some valid concerns about the code you shared. The use of the select method to filter the selection set is not clear, and it is not clear whether the code is intended to filter out hidden entities or simply filter the selection set in Ruby. In addition, using model.start_operation and model.commit_operation to wrap the model changes would make the code run faster, as the developer suggests.

Overall, it seems that the code has some issues that would need to be addressed in order to make it suitable for use in a production environment. It is important for developers to carefully review and verify the output of any AI-generated code to ensure that it is correct and meets the necessary standards and requirements.

Then, I ask ChatGPT to rewrite the code following the conventions and good practices for SketchUp Extensions. After some iterations, I got a file called alsomar_randompaint.rb and another one called main.rb

You can check the files here: alsomar_randompaint.zip (1.5 KB)

Again … a failure.

ADD (had to break for lunch): The AI got the file & folder hierarchy correct, but failed to name the registrar file and the extension subfolder the same. IE:

  • "alsomar_randompaint.rb" vs "alsomar_RandomPaint"

The AI also did not even stick with the folder name above in the 2nd argument to the SketchupExtension constructor call, using "Alsomar_RandomPaint/main" ! (Such sloppiness would fail on a case sensitive file system.)

  • In a comment, the AI confuses the SketchupExtension class identifier as Sketchup::Extension

I suggest that the names of the registrar file and extension folder follow the same capitalization as the Ruby module identifiers. (But if you or the AI are going to downcase anything in the file/folder names, then downcase everything. Ie … "alsomar_randompaint.rb" & "alsomar_randompaint")

But the AI also screwed up what goes in what file and again, ALL of an extension’s code must be within the NamepaceModule::ExtensionSubmodule wrapper.

But worse still, the AI seems not to understand that SketchUp will automatically load the registrar script (ie, "alsomar_randompaint.rb") during application startup. After the extension object is registered, SketchUp looks in the user’s SketchUp JSON preferences files to see if the user has that specific extension switched ON. If so, it calls the extension registration’s load() method which loads the loader file in the extension’s subfolder (ie, the 2nd argument to the SketchupExtension constructor call. In this case, the "main" file.)

The bad here is that the "<submodule folder>/main.rb" file will be attempting to load the "alsomar_randompaint.rb" registrar file whilst it is already in the process of loading it. This can set up a vicious cycle of many duplicate SketchupExtension data objects created in the ExtensionManager and the redefinition over and over of the extension’s submodule code as well as many duplicate menu items.
What likely will occur is SketchUp locking up or eventually crashing during the startup sequence.

Regarding the multiple menu items. The AI has missed that GUI object creation should always be defined within a “run once” conditional code block to prevent duplicate objects (submenus, menu items, toolbars, toolbar buttons, etc.)
Example … creating a menu item that calls a random_paint_command() method:

    # Within the extension's submodule ...

    unless defined?(@loaded)
      # Get a reference to the Extensions menu.
      extensions_menu = UI.menu("Extensions")

      # Add a new menu item for your extension to the Extensions menu.
      extensions_menu.add_item("Paint with random colors") do
        # When the menu item is clicked, execute the command.
        self.random_paint_command()
      end

      @loaded = true
    end

An again, the AI continues to confuse tools with commands.

  • Commands are executed. Tools are activated.

So, in summation, for a basic no frills extension …

  1. The only thing that must go into the registrar script is the instantiation of the SketchupExtension data object and the call to register it with SketchUp’s Extension Manager.

  2. ALL of the rest of the extension’s code must go in file(s) within the extension’s submodule, so that the user can switch the extension ON or OFF with SketchUp’s Extension Manager.

So, the "Alsomar_RandomPaint.rb" registrar file should look similar to:

# encoding: UTF-8
#
require 'extensions' unless defined?(SketchupExtension)

# Define a submodule for the extension.
module Alsomar
  module RandomPaint

    # Create a new instance of the SketchupExtension class.
    EXT_DATA = SketchupExtension.new("Random Paint", "Alsomar_RandomPaint/main")

    # Attach some nice info.
    EXT_DATA.creator     = "Alejandro ChatGPT"
    EXT_DATA.version     = "1.0"
    EXT_DATA.copyright   = "2022 Alejandro y ChatGPT"
    EXT_DATA.description = "Paints selected faces with random colors in a SketchUp model."

    # Register and load the extension on first install.
    Sketchup.register_extension(EXT_DATA, true)

  end
end

ALL the rest of the code goes in the main.rb file in the with the UI creation within a conditional block.


Oh, and lastly, although a SketchUp extension package is a .zip file, it must be renamed .rbz so the extension manager can load it.

1 Like

I mean, is it not absolutely crazy that you can ask this random bot to make a code snippet that performs a specific task, and it just does… Even if it’s methods are shotty, if it ends up creating something that works at all, that is just mind boggling to me.

I asked it to write me some code to make a sphere, this is what it gave me, though I haven’t tested it.

# First, we need to require the SketchUp Ruby module
require 'sketchup.rb'

# Then, we can use the SketchUp API to do various things
# For example, we can create a new model and add a few shapes to it
model = Sketchup.active_model
entities = model.entities

# Let's create a new 3D point at the coordinates (0, 0, 0)
# This will be the center of our sphere
center = Geom::Point3d.new(0, 0, 0)

# Next, we need to specify the radius of our sphere
radius = 10

# Now, we can create the sphere using the `add_sphere` method
sphere = entities.add_sphere(center, radius)
One more example:

Me: “Write me some ruby script for SketchUp Pro so that I can click one point, and click another point and it will frame a 2x6 wall complete with sheathing, and drywall.”

ChatGPT: "Here is some sample Ruby script for SketchUp Pro that will create a 2x6 wall between two points that you click in the modeling window:

# Define some variables for the wall dimensions
wall_height = 84 # inches
wall_width = 2.5 # inches
wall_length = 24 # feet

# Define the materials for the wall
wall_material = "Pine"
sheathing_material = "Plywood"
drywall_material = "Drywall"

# Ask the user to select the starting point for the wall
puts "Please click on the starting point for the wall..."
start_point = inputpoint

# Ask the user to select the ending point for the wall
puts "Please click on the ending point for the wall..."
end_point = inputpoint

# Calculate the vector for the wall's direction and length
wall_vector = end_point - start_point

# Create the wall's frame
frame = []
frame << start_point
frame << start_point + [wall_vector.x, 0, 0]
frame << end_point + [0, wall_vector.y, 0]
frame << end_point

# Create the wall using the frame points and the wall material
model = Sketchup.active_model
entities = model.entities
wall = entities.add_face frame
wall.material = wall_material

# Add the sheathing to the wall
sheathing = entities.add_face frame
sheathing.pushpull wall_height
sheathing.material = sheathing_material

# Add the drywall to the wall
drywall = entities.add_face frame
drywall.pushpull wall_height + 0.25
drywall.material = drywall_material

This script will create a wall with a frame made of 2x6 lumber, sheathing on both sides, and drywall on one side. It will use default materials for the wall and sheathing, but you can customize these by replacing the wall_material and sheathing_material variables with the names of the materials you want to use.

You will need to have the appropriate materials defined in your SketchUp model before running this script. You can do this by using the “Materials” window in SketchUp and creating new materials with the names specified in the script.

I hope this helps! Let me know if you have any questions."

1 Like

Here is an example for you to compare: sketchup-ruby-api-tutorials/main.rb at main · SketchUp/sketchup-ruby-api-tutorials · GitHub

1 Like

Okay yes it looks pretty lame in comparison.

I don’t know if it’s lame. Maybe it could be useful (I haven’t tried it). It might be more useful for simpler questions. Try looking at the example code and asking very simple questions based on each (or a few) line(s) of the code. Try to recreate the code with questions.

1 Like

Following your advice (and after seeing another registrar files), I managed to get this.

require 'extensions' unless defined?(SketchupExtension)

module ASM
  module RandomColors

    # Constants
    plugin_name = 'Random Colors'
    plugin_version = '1.0'
    plugin_description = 'Apply random colors to the selected faces, surfaces, groups or components.'
    plugin_id = File.basename(__FILE__, '.rb')

    # Paths
    path_root = File.dirname(__FILE__)
    path_main = File.join(path_root, plugin_id, 'main.rb')

    # Create a new instance of the SketchupExtension class
    ext_data = SketchupExtension.new(plugin_name, path_main)

    # Attach some nice info
    ext_data.version = plugin_version
    ext_data.description = plugin_description
    ext_data.creator = "Alejandro Soriano"
    ext_data.copyright = "#{Time.now.year}, #{ext_data.creator}"

    # Register and load the extension on first install
    Sketchup.register_extension(ext_data, true)

  end # module RandomColors
end # module ASM

Now I’ll try to fight with the rest of the code in the main.rb file
Thank you for your advice!

1 Like

ChatGPT is pretty insane, honestly. I’ve had it write modem scripts. Yes, modem scripts, for a 2400 baud modem, as well as assembler code to draw a flower on the screen. Cobol code to format a disk, autoexec.bat files, config.sys. Why these? Because they are lost in history, yet, it can make all of them.
I have had it write poems and sonnets in spanish that are honestly incredible.
you can get silly. For example, I told it to write a poem that has the words, start, fart, cart, and art…here is what it came up with:
Write a poem that rhymes the words art, fart, cart and start:

In the beginning, there was art.
A beautiful and wondrous thing
It sprang forth from the heart
And brought joy to those it did bring

But then came the fart
A loud and noxious sound
It stank up the room
And brought laughter all around
And from the fart, came the start
Of a new and exciting venture
A journey that would take us far
On a wild and thrilling adventure

We traveled on a cart
Bouncing and jostling along
Through fields and forests and towns
Singing and laughing our hearts strong
We may have started with a fart
But our journey took us to great heights
And we found joy and beauty in our art
As we traveled on, day and night.

4 Likes

It still applies GIGO.

1 Like

After reading and studing about code, I managed to get this extension done. You can find it on github. GitHub - alsomar/random-colors: A SketchUp extension to apply random colors to the selected faces, groups or components.

After all this proccess, I learned an AI can’t create a whole extension nowadays. I needed to learn how a loader works, some variables and definitions, etc. Despite this, it helped me to understand how it works. I copied pieces of code to chatGPT asking how it works. I’m sure my code is quite rudimentary, but it’s a good start.

Happy holydays to everybody!

5 Likes

Sure, it works after all, and it is properly namespaced.

The AI makes very readable code that you can understand, but not the most efficient code.

Example …

    # Define a method to generate a random color
    def self.generate_random_colors
      # Generate a random color
      r = rand(256)
      g = rand(256)
      b = rand(256)
      color = Sketchup::Color.new(r, g, b)
      # Return the random color
      return color
    end

… as opposed to …

    # Define a method to generate a random color
    def self.generate_random_colors
      # Generate and Return a random color:
      Sketchup::Color.new(rand(256), rand(256), rand(256))
    end
1 Like

Oh, and a note about this …

  if !file_loaded?('asm_menu_loader')
    @@asm_tools_menu = UI.menu("Extensions").add_submenu("ASM Tools")
  end

1. Currently (and like forever,) SketchUp will not create persistent menu references.
So although the module variable will stick around, the menu object it points to becomes invalid after SketchUp gets done loading the files for this extension.

We’re hoping that this will change in the future. (Many developers like to put their extension commands submenus on a specific developer submenu.)

2. Although a lot of “official” examples encourage the use of the file_loaded() and file_loaded? methods, these old features are really inefficient. They also use a shared global array of strings named $loaded_files. This means that the possibility exists that search strings could clash or some other extension could modify the contents of the $loaded_files array.

Ruby string comparison is slow. As more and more extension stuff strings into the array, it gets bigger and bigger, making searching the array take more and more time.

We’ve been suggesting instead for extensions to use their own boolean variables … ie:

  if !defined?(@tools_menu_loaded)
    UI.menu("Extensions").add_submenu("ASM Tools")
    # Add menu items ...
    @tools_menu_loaded = true
  end

Faster and more reliable since your not using strings or relying upon some external feature.

2 Likes