Weird problem about definition guid and start_operation method

In a blank model, import an instance like this:

path = 'some/path.skp'
d = Sketchup.active_model.definitions.load(path)
instance = Sketchup.active_model.entities.add_instance(d, IDENTITY)
puts "definition guid: #{d.guid}"
# redraw instance which has dynamic attributes
# redraw_without_undo method just is a copy of redraw_with_undo but without operation transaction
$dc_observers.get_latest_class.redraw_without_undo(instance)
puts "definition guid: #{instance.definition.guid}"
# in this case, guid changes, that's what I what.

In this case, the definition’s guid will be changed.
But if wrap those codes into an operation transaction like this:

Sketchup.active_model.start_operation 'test', true
path = 'some/path.skp'
d = Sketchup.active_model.definitions.load(path)
instance = Sketchup.active_model.entities.add_instance(d, IDENTITY)
puts "definition guid: #{d.guid}"
# redraw instance which has dynamic attributes
# redraw_without_undo method just is a copy of redraw_with_undo but without operation transaction
$dc_observers.get_latest_class.redraw_without_undo(instance)
puts "definition guid: #{instance.definition.guid}"
Sketchup.active_model.commit_operation
# in this case, guid doesn't change, that's not what I what.

The definition’s guid won’t change, and after this operation, even import the definition from the path again, the new imported definition is same as instance.definition

But if in the operation transaction use redraw_with_undo instead of redraw_without_undo, the definition’s guid changed, too.

Sketchup.active_model.start_operation 'test', true
path = 'some/path.skp'
d = Sketchup.active_model.definitions.load(path)
instance = Sketchup.active_model.entities.add_instance(d, IDENTITY)
puts "definition guid: #{d.guid}"
# redraw instance which has dynamic attributes
$dc_observers.get_latest_class.redraw_with_undo(instance)
puts "definition guid: #{instance.definition.guid}"
Sketchup.active_model.commit_operation
# in this case, guid changes, that's what I want.

So why does this happens?

Generally, with DCs, if the attribute settings cause the DC code to modify the DC component’s definition, then it must get a new guid. This can occur with material changes, or stretching where extra copies of subcomponents are generated.

So, in your test make sure that you do the exact same attribute changes.

We cannot reproduce the error on our machines without a test component file and the change to the attributes before the redraw calls. Also, whenever reporting an error with the API and/or an extension, it is normal to state the SketchUp/API version and the version of the extension.

FYI, in this second case, because the DC method starts a new undo operation, the operation you had already started is automatically committed.

Thanks. The result of the first case is what I want. I want the definition’ guid changed after redraw since after redraw the entities of the definition would be changed.

Yes, this is why using redraw_without_undo instead of redraw_with_undo, cause I want call redraw method multiple times under single operation.

What I do is define a new mehtod named redraw_without_undo like this:

class DynamicComponentsV1
  def redraw_without_undo(entity)
    determine_movetool_behaviors(entity)
    DCProgressBar::clear()
    redraw(entity)
    DCProgressBar::clear()
    refresh_dialogs()
  end
end

And the original method named redraw_with_undo should be like this:

def redraw_with_undo(entity,progress_bar_visible=true)
    Sketchup.active_model.start_operation translate('Redraw'), true
    determine_movetool_behaviors(entity)
    DCProgressBar::clear()
    redraw(entity)
    DCProgressBar::clear()
    refresh_dialogs()
    Sketchup.active_model.commit_operation
  end

The SketchUp Ruby ObjectSpace is a shared environment.

DO NOT directly modify Ruby Core, SketchUp API, or Trimble extension classes or modules. Doing so affects all other extensions and users.

If you are only doing this on your own machine, then enjoy.

But do not publish extensions that modify the DC code.

If you can … then use Ruby refinements - Documentation for Ruby 3.2 (ruby-lang.org)
They are like your private superclass to the DC class that only your code file can see.

Thanks. This extension won’t be published, so don’t worry.

I wonder if you have any ideas about my question, it confused me for a long time.

I do worry. Do so, would engrain poor coding habits in you. And later on you may forget that you “monkey-patched” classes you should not have. Again, use a refinement.

I already weighed in on this. The DC code is very complex and closed source.
I really don’t have much interest in digging deeper into it. (I have my own projects.)

I see, thanks anyway.