Running a script on startup

I have a http call on my extension load it works fine and hits the server but the message box does not show. Here’s my sample code

my_extension.rb

module Woodlogic
  ex = SketchupExtension.new('Sample Fetch', 'sample_extension/fetch_server')
  Sketchup.register_extension(ex, true)
end

my_extension/my_own_extension.rb

class FetchServer
  request = Sketchup::Http::Request.new("http://localhost:3000", Sketchup::Http::POST)
  request.start do |request, response|
    if request.status == Sketchup::Http::STATUS_SUCCESS
      UI.messagebox 'Success', MB_OK
    else
      UI.messagebox 'Failed', MB_OK
    end
  end

But if I update the FetchServer and add a message box on the class it now shows the the status message box

class FetchServer
  request = Sketchup::Http::Request.new("http://localhost:3000", Sketchup::Http::POST)
  request.start do |request, response|
    if request.status == Sketchup::Http::STATUS_SUCCESS
      UI.messagebox 'Success', MB_OK
    else
      UI.messagebox 'Failed', MB_OK
    end
  end
  UI.messagebox 'Fetch called', MB_OK
end

Is there any work around so that I won’t need to call another message box?

In general, it is a good practice to wrap every piece of code in a method, so that you have full control when this code is run (namely when you call the method).

class FetchServer

  def self.do_fetch()
    request = Sketchup::Http::Request.new("http://localhost:3000", Sketchup::Http::POST)
    request.start do |request, response|
      if request.status == Sketchup::Http::STATUS_SUCCESS
        UI.messagebox 'Success', MB_OK
      else
        UI.messagebox 'Failed', MB_OK
      end
    end
  end

  self.do_fetch()

end

I don’t think users would enjoy message boxes for the sole purpose of making some code work. Message boxes interrupt users because they are “modal”, that means a message box blocks execution of Ruby code (and any other user interaction) and waits until the user has responded to it.

That means when code works with message box and not without, it is very likely a timing issue which comes from asynchronicity. The request callback block is scheduled to run asynchronously after the request is fulfilled, while code execution immediately continues with the next line (“Fetch called”) which blocks execution and waits. And although such timeouts hide issues with asynchronous code, they are not a reliable solution.

I also wouldn’t run heavy computations at load time because other extensions want to be loaded as well and the user is impatient to interact with the app. You would probably better call the server after startup and after all extensions have been loaded. You can do this with a zero timeout, which effectively schedules your code after the loading/execution of all prior scripts has completed:

UI.start_timer(0, false) {
  FetchServer.do_fetch()
}

Please post Ruby coding questions in the Ruby API subcategory.


(1) ALL of your extension’s code should be within a separate submodule that is within your unique top level namespace module. (Ie, is an an absolute non-no to define custom classes in the top level ObjectSpace as they will become global classes. Only certain API classes and Ruby core classes should be defined at the top level.)

(2) Server requests should be delayed until absolutely necessary, not during SketchUp’s startup cycle.
The modal message boxes can either interrupt the loading of other extensions or might be suppressed by SketchUp as it loads (to avoid interference with the loading of other extensions.)

(3) Something like this should be fired by a method call, not during the load and evaluation of your extension’s classes.

(4) Generally try to avoid modal messageboxes for debugging and output informational strings to the console using puts().

(5) You are not properly naming your extension registrar file nor it’s folder, nor are you setting the correct pathname in the SketchupExtension object’s constructor call.
Ie … the 2nd arg to the constuctor is "sample_extension/fetch_server", but the actually path given in the example is … "my_extension/my_own_extension.rb".

The “Plugins” file space is a shared space just as is SketchUp’s Ruby ObjectSpace.

So let us say your unique namespace module identifier will be “MarcoSyu”, and your extension submodule name will be “SampleFetch”.

You name the regsistrar file and the extension subfolder both using the chained module names: "MarcoSyu_SampleFetch.rb" and "MarcoSyu_SampleFetch/"
Ex:

# encodng: UTF-8
# File: "MarcoSyu_SampleFetch.rb"
#
module MarcoSyu
  module SampleFetch
    EXTENSION = SketchupExtension.new(
      'Sample Fetch', 'MarcoSyu_SampleFetch/fetch_server'
    )
    Sketchup.register_extension(EXTENSION, true)
  end
end

… and then the loader file must be as named in the constructor’s 2nd argument:
"MarcoSyu_SampleFetch/fetch_server.rb"


Now specifically I think your issue arises from violating the above conditions (during the startup cycle) and that you are misusing the class object.

Classes are for instantiating multiple instances of code that each keeps it’s own state and instance variables. A class instance object is created via a constructor call, usually to new() that then automatically calls the new instance’s initialize() method.

Your class does not define it’s initialize() method so this raises a red flag that you might not be actually needing a class. Perhaps this should be wrapped up in a method and within your extension’s submodule ?

Example: Try using the AppObserver to trigger a delayed call to a fetch_server() method …

# encodng: UTF-8
# File: "MarcoSyu_SampleFetch/fetch_server.rb"
#
module MarcoSyu
  module SampleFetch

    extend self

    # This is an AppObserver callback called by the
    # SketchUp engine during the startup cycle.
    def expectsStartupModelNotifications
      UI.start_timer(5.0,false) {
        # Call the server after 5 seconds:
        fetch_server()
        # Detach this submodule as an AppObserver:
        Sketchup.remove_observer(self)
      }
      return false
    end ###

    def fetch_server
      @request = Sketchup::Http::Request.new(
        "http://localhost:3000", Sketchup::Http::POST
      )
      @request.start do |request, response|
        if request.status == Sketchup::Http::STATUS_SUCCESS
          puts "Request was successful."
          # Read response.body here ...
          @response = response.body
        else
          puts "Request was not sucessful!"
        end
      end
    end ###

    def get_status(status)
      case status
      when Sketchup::Http::STATUS_UNKNOWN
        "Unknown"
      when Sketchup::Http::STATUS_SUCCESS
        "Success"
      when Sketchup::Http::STATUS_PENDING
        "Pending"
      when Sketchup::Http::STATUS_CANCELED
        "Canceled"
      when Sketchup::Http::STATUS_FAILED
        "Failed"
      else
        "Unknown"
      end
    end ###

    def show_result
      UI.messagebox(
        "Request status was: #{get_status(@request.status)}"
      )
    end ###

    # Run this block ONCE upon first load:
    unless defined?(@loaded)
      @request  = nil
      @response = nil

      UI.menu('Extensions').add_item('SampleFetch: Result') {
        show_result()
      }

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

      @loaded = true
    end

  end
end
2 Likes