How to get SU 2017's Files preferences in Ruby code?

In SU 2017 both PC and Mac give the user ability to set custom File location paths in Preferences. To avoid having to copy my custom Materials, Styles, etc. to each update of SU, I created folders outside the SU standard places and put those paths into the preferences (I used to use symbolic links for the same purpose). But Sketchup#find_support_file and Sketchup#find_support_files do not seem to be aware of these preference settings. So, without knowing the user’s preferences (as opposed to something the code creates) Ruby code can’t find any of these folders or their contents.

Does anyone know how to access the Preference Files settings without having to read the Windows registry or Mac .plist explicitly?

Sketchup.find_support_file(‘Materials’)
etc returns the default path set up… NOT the one in Preferences !
You can try Sketchup.read_default(“File Locations”, “Materials”, “?”) which returns an error on PC - but oddly the error message actually includes the path you are looking for ! I think it’s the un-escaped \ in the filepath ??
Looks like you need to read the Registry or plist with Ruby !

Alas, on my Mac your read_default idea also raises a syntax error exception when I try it in the Ruby Console. This suggests a bug in the way read_defaults parses a plist or registry entry. And, as you wrote, the error message indeed contains the path I was trying to read! That suggests I should be able to rescue the exception and scrape the path out of the message!?

loc = Sketchup.read_default("File Locations", "Materials", "?")
Error: #<SyntaxError: <main>: unknown regexp options - tv
<main>: syntax error, unexpected tIDENTIFIER, expecting keyword_do or '{' or '('
/Users/steve/Documents/Cad files/materials
                                ^>
<main>:in `eval'
<main>:in `read_default'
<main>:in `<main>'
SketchUp:1:in `eval'

Edit: it seems one can’t rescue syntax errors because they already make the code un-parsable!

Known issue, maybe this custom method works for you.

It actually runs the value read through eval() which chokes on paths (especially on PC because SketchUp uses backslashes under Windows, even though Windows has always accepted forward slashes in path strings.)

Been there, tried that, and failed.

First of all SyntaxError is not a subclass of StandardError, so you must specify subclasses of ScriptError on the rescue line.

But last I tried, even that would not work because SketchUp directly raises the exception on the C-side of things. I think it has to do with the “sensetivity” of eval(), or whatever, that can cause application crash if not trapped.

And it does not work to alias the Ruby eval, as the SketchUp code of read_default is calling Ruby’s C eval function.

Trying to trap $! also does not work. (By the time you read the message it refs nil again.)

The only possible thing, might be to monitor $stderr IO while making the call.

But it’s a lot easier to just read it from the registry.

Thanks for the reference. After reading it, the cause of the original problem became clear to me: The SU preferences are stored externally in the Windows Registry or in a Mac plist file. In both cases, the Ruby types that can be stored are limited. So write_default converts most types to character strings and read_default passes them through eval to reconstruct Ruby objects. Any string that begins with ‘/’ will cause eval to start processing a regular expression, and eval will typically conclude that the regex is not correctly formed, raising a SyntaxError. Besides, I don’t need a regex object returned when I read back the path “/Users/steve/Documents/MyMaterials/” even if it could be parsed as a regex!

As @DanRathbun noted in his response (which was posted while I was writing this), for some unstated reason the SU developers decided to trap the error below the API, post a message, but not pass the exception back to where you could handle it. So a “clean” solution isn’t possible.

I see several problems with doing a workaround of the sort described in that previous topic:

First, it relies on empirically determined knowledge of where and using what keys these preferences are stored. Trimble does not document either the mechanism or the keys, so the entire idea involves “reverse engineering” (a possible violation of the EULA), and a risk that they may change the implementation in the future (each version of SU already uses a separate section of the registry or a separate plist file - there would be no issue of compatibility for SU itself).

Second, there is no assurance that SU has yet written the preference value out when your Ruby code tries to read it. It is permissible for a program to hold preferences internally and write them only when the program closes or at any other time it chooses. This issue is compounded on Mac because preferences are cached by the system, which may in turn wait until still later to write them to the physical plist file. One is expected to go through the official macOS preferences API, not to read the plist manually.

Third, there would need to be an equivalent for Mac. There is no equivalent to the win32/registry library provided for Mac. I’m a Mac user. I could not test the Windows version and would have to figure out a clean way to access the Preferences system on my own. Another of the myriad ways in which Trimble has neglected the Mac version of SU compared to Windows!

Fourth, the original problem of correctly converting from stored strings to Ruby objects remains. Your registry/plist reader would need to be aimed at a specific kind of saved preference to know when eval is safe vs needing a workaround.

I spelled out all these objections explicitly to make the point: Trimble needs to provide an official and documented way to access preferences so that we are not relegated to reverse-engineered workarounds. I suppose this topic could be changed to a Feature Request, though it would be of interest only to developers.

Today we have a mess on both Windows and Mac! Some preferences are accessible via things such as OptionsProviders (though it is hit-or miss and the documentation sucks!). Some are accessible via other methods in the Ruby API. And some, like these Files path values are not accessible at all.

Been there, Done that, requests filed years ago.

What I suggested was that there be a new application level Sketchup::options() getter that returns an Sketchup::OptionsManager subclass instance, call it PreferencesManager (or whatever.)

Then this new collection subclass holds references to new Sketchup::OptionsProvider instances of appropriate names. Some of which might have attributes that are read only.

I was hoping that the exposure of regional settings would be added to the API this way. But they were not. Instead the team just implemented a whole new module.

1 Like

This topic was automatically closed after 91 days. New replies are no longer allowed.