# Loading ruby observers into the active model

**URL:** <https://forums.sketchup.com/t/loading-ruby-observers-into-the-active-model/184203>\
**Category:** Ruby API\
**Created:** [December 15, 2021, 4:55pm UTC](https://forums.sketchup.com/t/loading-ruby-observers-into-the-active-model/184203 "2021-12-15T16:55:01Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![esekkin](https://avatars.discourse-cdn.com/v4/letter/e/bc79bd/32.png) [@esekkin](https://forums.sketchup.com/u/esekkin)\
**Post date:** [December 15, 2021, 4:55pm UTC](https://forums.sketchup.com/t/loading-ruby-observers-into-the-active-model/184203/1 "2021-12-15T16:55:01Z")

</div>

Hello 👋

I have been using some of the ruby observers in my extension:  
`Sketchup::InstanceObserver, Sketchup::EntitiesObserver, Sketchup::ModelObserver`

If I initialize them directly inside the `loader.rb` file they work for the first model that loads (until I do a File\>New or File\>Open). After this they don’t.

I added an `AppObserver` and each time there is a callback to `onOpenModel` or `onNewModel`  
I attach the observers I defined in the same `loader.rb`.

This solved my issue but I am wondering what happens to the observers I attached earlier in the previous models. (if the user keeps clicking File\>Open) Do I need to handle these when I load a new file each time? or does Sketchup magically do some sort of garbage collection?

```auto
class myObserver < Sketchup::AppObserver
  def onNewModel(model)
    #add all of the implemented observers
  end
  def onOpenModel(model)
    #add all of the implemented observers
  end
  def onQuit()
    #execute onQuit
  end
end
Sketchup.add_observer(myObserver.new)

```

---

<div class="post-metadata">

**Author:** ![DanRathbun](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/danrathbun/32/500_2.png) [@DanRathbun](https://forums.sketchup.com/u/DanRathbun)\
**Post date:** [December 15, 2021, 7:10pm UTC](https://forums.sketchup.com/t/loading-ruby-observers-into-the-active-model/184203/2 "2021-12-15T19:10:41Z")

</div>

> [@esekkin](#):
>
> Do I need to handle these when I load a new file each time?

First of all, there currently is no `#onPreCloseModel` observer callback (although it has been requested,) which a coder would need in order to go and remove observers. The code would also need to keep references to all the observer objects in order to pass to the various `#remove_obsever` methods.

> [@esekkin](#):
>
> … or does Sketchup magically do some sort of garbage collection?

Yes, I think it does. When a “watched” object becomes unreferenced by Ruby and marked for GC I think that SketchUp removes that object from it’s callback queues.

In your example, your code is not keeping a reference to the [`AppObserver`](https://ruby.sketchup.com/Sketchup/AppObserver.html) object, instead you are letting the SketchUp observer queue hold the only reference. When the application is in the process of shutting down and has processed all the `#onQuit` callbacks, it will clear it’s queue and all the observers will become unreferenced.  
But even before this all the model collection observer queues would also be cleaned up and their observers dereferenced.

* * *

Now, several things.

You should include the [`#expectsStartupModelNotifications`](https://ruby.sketchup.com/Sketchup/AppObserver.html#expectsStartupModelNotifications-instance_method) callback so that the initial model opened (from a double-click on a `skp` file) or created (the new blank model) will get the observers attached.

On Mac it becomes very important to make sure that the model reference points at the correct model object as multiple may be open at the same time.  
But, on Windows there is a weird thing where the singleton model reference is reused when the model changes. But the collection references for the new model do get new references. As was explained to us, when you attach the same kind of observer to the same object, it’s previous item in the queue is deleted and a new item is added at the end of the callback queue. (But things could change.) Anyway, we have not needed to worry on Windows about reattaching a model observer to the singleton `model` reference.

Where it becomes important to remove observers is when the tasks done or the watching upon the object are temporary. Ie, for an example, say you only need to watch something when a certain native tool is active. (You would use a [`ToolsObserver`](https://ruby.sketchup.com/Sketchup/ToolsObserver.html) to detect tool changes and attach the temporary observer if the active tool id was a certain value, and remove the observer when it again changes.)  
Another example would be if you knew that a component definition was going to be created and you need to do something when it was a valid object. You would temporarily attach a [`DefinitionsObserver`](https://ruby.sketchup.com/Sketchup/DefinitionsObserver.html) to react when the next definition was fully created, and then detach the observer.

Anyway, … references must be kept in order to remove observers later. The best place to keep these references, is the instance of the [`ModelObserver`](https://ruby.sketchup.com/Sketchup/ModelObserver.html) attached to the model object.

Now, with regard to an extension’s [`AppObserver`](https://ruby.sketchup.com/Sketchup/AppObserver.html) object, … there is only 1 application object … SketchUp itself. So in reality your extension does **not** need a class object. Classes are used when you need multiple copies (instances) of something.  
The various API’s `#add_observer` methods do not do any type checking to verify that their arguments are subclasses of anything in particular. The observer mechanism only requires that the object’s callback methods be **publicly accessible**.  
So when you only need 1 code object, a module is the correct kind of object. Since your extension must be within a submodule (of your namespace module,) it makes sense that the extension submodule itself be the singleton [`AppObserver`](https://ruby.sketchup.com/Sketchup/AppObserver.html) object.  
Something that many newer API observer coders miss is that “a module” is an instance of class [`Module`](https://ruby-doc.org/core-2.7.2/Module.html) and “a class” is an instance of class [`Class`](https://ruby-doc.org/core-2.7.2/Class.html), which is a subclass of class [`Module`](https://ruby-doc.org/core-2.7.2/Module.html). So “a class” and “a module” are very similar kinds of objects, but a class has been given the ability to have multiple instances each holding different state data.  
What I’m getting at is that having your extension submodule be it’s own [`AppObserver`](https://ruby.sketchup.com/Sketchup/AppObserver.html) object is not so weird a thing as it is also an instance object.

Example …

```ruby
module SomeAuthor
  module SomeNiftyExtension

    extend self

    def attach(model)
      model.add_observer(ModelSpy.new(model))
    end

    def expectsStartupModelNotifications
      true
    end

    def onNewModel(model)
      attach(model)
    end

    def onOpenModel(model)
      attach(model)
    end

    def onQuit()
      # execute onQuit tasks
    end

    class ModelSpy < Sketchup::ModelObserver
      attr_reader :model
      def initialize(model)
        @model = model
        # Hash to hold observer references:
        @spy = {}
        # Add all of the implemented observers:
        model.definitions.add_observer(@spy[:definitions]= DefinitionsSpy.new)
        # ... etc ...
      end
      # ... ModelObserver callback method definitions ...
    end

    class DefinitionsSpy < Sketchup::DefinitionsObserver
      def onComponentAdded(definitions, definition)
        # Do something with the new definition ...
      end
    end

    # .. other observer class definitions ...

    # RUN ONCE AT STARTUP:
    if !defined?(@loaded)
      # Define UI objects here ...

      # Attach this module as an AppObserver object:
      Sketchup.add_observer(self)

      # Mark this extension as loaded:
      @loaded = true
    end

  end # extension submodule
end # namespace module

```

Another nifty thing is that since the v2016 release all the **empty** callback methods in the API observer superclasses were **deleted** so there is really no functionality for observer classes to inherit.  
This fact and that also `#add_observer` does not type check means that you can create hybrid observers that contain the callback methods for multiple “abstract” observer superclasses.  
It makes it much easier to pass data between callback methods if they are in the same class.

I often combine `DefinitionsObserver` and `DefinitionObserver` in the same class. And sometimes I will wrap all of the collection observers within a hybrid `ModelObserver` class. A sometimes when I don’t need separate model state variables I’ll put a mix of callbacks right in the extension submodule and it becomes a singleton observer object attached to whatever I need to attach it to, both permanently and temporarily.

---

<div class="post-metadata">

**Author:** ![esekkin](https://avatars.discourse-cdn.com/v4/letter/e/bc79bd/32.png) [@esekkin](https://forums.sketchup.com/u/esekkin)\
**Post date:** [December 15, 2021, 7:31pm UTC](https://forums.sketchup.com/t/loading-ruby-observers-into-the-active-model/184203/3 "2021-12-15T19:31:49Z")

</div>

thanks for the detailed response… super helpful for future reference also…

> [@DanRathbun](#):
>
> there currently is no `#onPreCloseModel` observer callback

I see how this would be helpful for some developers.

> [@DanRathbun](#):
>
> it will clear it’s queue and all the observers will become unreferenced.  
> But even before this all the model collection observer queues would also be cleaned up and their observers dereferenced.

and this would resolve the issue for me.

> [@DanRathbun](#):
>
> You should include the [`#expectsStartupModelNotifications`](https://ruby.sketchup.com/Sketchup/AppObserver.html#expectsStartupModelNotifications-instance_method) callback so that the initial model opened (from a double-click on a `skp` file) or created (the new blank model) will get the observers attached.

thanks for the tip, I was currently initializing it inside of the main module for the initial open. I will look into this callback -sounds like a better way to do it.
