Safe place to store user-defined parameters
-
@aerilius said:
when OS X is detected:
- check if plugin directory is writable and use it
SU2013 in ~/Library/Application Support/SketchUp 2013/SketchUp…
in earlier versions only writable if owned by the user /Library/Application Support/SketchUp 8/…
-
otherwise do what? (question 1, to be determined),
maybe a subfolder ofFile.expand_path("~/Library/Application Support/SketchUp")
, did this location change with the transition to Trimble?
otherwise (when platform detection fails) -
check if plugin directory is writable and use it
-
otherwise fallback (temporary folder, user folder, ignoring or error handling)
Another question 2 is whether to put the plugins folder at highest priority if it is writable? This would make it simpler and more consistent, it would be prepared for when SketchUp moves plugins into user level on Windows, and makes plugins "self contained".
my vote is to use
File.dirname(__FILE__)
and TEST IT?my_unkown_path = File.dirname(__FILE__) # for testing I used "/System/Library" # and "/tmp" # if not (my_unkown_path[/User/]) my_unkown_path_test = (File.join(my_unkown_path, "/.My_path_test.txt")) begin File.open(my_unkown_path_test, 'w+') {|f| f.write("did you read the manual or just are you just lucky?")} rescue => e UI.messagebox("RTFM then move me #{e} ") abort("force error to end") end my_warning = UI.messagebox "need to check you have the RIGHTS to write?" if FileTest.readable_real?(my_unkown_path_test) my_good_news = UI.messagebox(IO.readlines(my_unkown_path_test)) File.delete(my_unkown_path_test) else my_bad_news = UI.messagebox("I can only guess why you might possibly think that my plugin would run from #{my_unkown_path}\n RTFM") end end
EDIT: added a rescue to test against /System/Library which I know I can't write to...
tested and works in v8 and v2013 -
- If I understood right, /Library is at system level and the user level (in the SketchUp island ) is preferred in the longterm.
- Not sure why you want the root, but Ruby is so nice and resolves
File.expand_path("/")
to root on OSX/
and WindowsC:\
(or whatever drive letter is root).
So maybe we can crystallize the following choices (?):
when Windows is detected:-
check for
ENV["LOCALAPPDATA"]
and use it if available # Vista, 7, 8 -
otherwise check for
ENV["APPDATA"]
# XP -
otherwise if both envs are empty, fallback to something (temporary folder, user folder, ignoring or error handling)
when OS X is detected: -
check if plugin directory
File.dirname(__FILE__)
is writable and use it
SU2013 in ~/Library/Application Support/SketchUp 2013/SketchUp…
in earlier versions only writable if owned by the user /Library/Application Support/SketchUp 8/…
-
otherwise do what? (question 1, to be determined),
maybe a subfolder ofFile.expand_path("~/Library/Application Support/SketchUp")
, did this location change with the transition to Trimble?
otherwise (when platform detection fails) -
check if plugin directory is writable and use it
-
otherwise fallback (temporary folder, user folder, ignoring or error handling)
Another question 2 is whether to put the plugins folder at highest priority if it is writable? This would make it simpler and more consistent, it would be prepared for when SketchUp moves plugins into user level on Windows, and makes plugins "self contained".
-
Come to think of it, this could go well as part of
SketchupExtension
Something like (for example only):
APPDATA_DIRECTORY = get_appdata_directory_somehow() RESTRICTED_CHARACTERS = Regexp.new('[\\/;*?"<>|%]') class SketchupExtension def data_directory() dir = APPDATA_DIRECTORY if FileTest.writable?(dir) if creator dir = File.join(dir, creator.gsub(RESTRICTED_CHARACTERS, '')) Dir.mkdir(dir) if !File.directory?(dir) end if name dir = File.join(dir, name.gsub(RESTRICTED_CHARACTERS, '')) Dir.mkdir(dir) if !File.directory?(dir) end return dir end if respond_to(;extension_path) && extension_path dir = File.dirname(extension_path) if File.directory?(dir) && FileTest.writable?(dir) # Make a sub directory to avoid clashes dir = File.join(dir, '__appdata__') Dir.mkdir(dir) unless File.directory?(dir) return dir end end # Alternatively, return a default directory, such as # SU/plugin_data/<creator>/<name>/ raise RuntimeError.new('Not data directory could be located') end end
Then usage would be something like:
module AuthorModule module PluginModule EXTENSION = SketchupExtension.new('...', '...') # .... some_path = File.join(EXTENSION.data_directory, 'settings.txt') end end
-
BUT...
No one has still answered my question...
What is wrong with using the container folder for the ENV for the OS's user's 'Temp' folder?
That is easily found for either OS and is writable, like the Temp folder itself...
Once you have the path to the user's 'Temp' folder we can then make an app-specific subfolder to hold our temporary files...
AND from File.dirname(Temp) we can get the container-location into which we can make an app-specific subfolder to hold our 'permanent' files...
I do it without problems on all OS's - the rest of you seem to obsessed with finding 'the exact right folder that we must use', when in truth there are several valid possibilities, and my earlier posts cover much of this too... KISS ? -
@tig said:
BUT...
No one has still answered my question... :?I thought Steve had answered when he gave his brief lesson on why temporary directories exist?
Your 'trick' of hiding your 'cookie' from my computer maintenance routines works a treat for now, but may not after the next release of Safari [in beta] or Mavericks [dev release available].
The writings on the wall, Apple doesn't like Plugins or Cookies and if you recall, they weren't to fond of Flash.
Apples allows and even encourages 'Extensions', but they are meant to fully contained within their Parents 'approved' structures that include the ENV[TMPDIR] and or /tmp depending on what your doing. There are also approved external paths for any launch daemon's, browser cookies or cache's that the Parent app and or it's extensions may require. All these additional paths are meant to be in an info.plist so they can be easily removed at any point in time.
Is there a reason to think your cookie [for example] won't work from
File.join(File.dirname(__FILE__), "/Data/.Cookie_Jar/.My_cookie.dat")
?I know the path works, is the problem checking it's the contents from your own Plugins sub-directory?
From a general user perspective that is hidden >> inside a hidden folder >> inside your NameSpaced Plugins Folder >> inside the Parent apps 'Plugins' >> inside the normally hidden
ENV[HOME]/Library/...
.Is there a need to look for anything if your plugin KNOWs where it is.
When your User decides to remove it, the Parent.app or better still, your Plugin only needs to trash the one folder and there are no lurking remains hidden, inappropriately scattered around the system/usr folders.
john
-
@tig said:
BUT...
No one has still answered my question...
What is wrong with using the container folder for the ENV for the OS's user's 'Temp' folder?One last try and then I will shut up. Your suggestion is what we used to call a "bad hack". It is a hack because it uses an unofficial trick to work around an issue. It is bad because it is only works today due to oversights and mistakes on the part of the OS developers. The fact that the parent directories of temp locations are writable is an oversight, not by intent. They simply never considered that programmers would do what you suggest, so they didn't bother to lock the gate or sweep the floor. They could decide to secure their house at any time, at which point this technique will fail. Do you really want your plugin users to plague you with support calls when Windows 9 or Mac OS X 10.9 changes the rules? Why not look for an OS-endorsed and stable technique rather than take this risk?
Steve
-
The parent-folder of Windows 'Temp' folder - 'Local' - IS clearly intended to be written to...
Many applications already make their own subfolders in it to store data more permanently that the Temp folder itself would allow...If the MAC-guys don't want you writing to the user's 'T' folder's parent-folder, then they have never said so, and to make it non-writable would be straightforward for them...
I don't consider my Windows suggestion a 'hack' at all... the MAC side is less documented, but to date has always worked too...
@driven
The Plugins directory is NOT always writable by the user - this is always the possibility on a PC and before v2013 so it was on a MAC too.
So we can't confidently write any of our folder/files there - in fact we are now full circle because Fredo started this thread trying to find where to save data outside of this potentially limited tree...
YourENV[HOME]
is MAC only, BUT I agree it could be used for that OS by reconstructing the other tree elements from/Library/
- but then who can be sure they'll also remain 'constant' too ?
The equivalent for the PC is actuallyENV["HOMEPATH"]
, but then returns say 'C:/Users/TIG
', from which we'd need to construct the upwards tree towards /AppData/Local/XXX etc... This is more easily got working downwards from TEMP or TMP and makes no assumptions about paths ! It does expect Local and Temp to be writable - which is their purpose...There are many places we cab write folders and files, knowing that the users will assuredly have permission - like their documents folder, their desktop, their 'temp' folders etc...
With the likes of Fredo's folder/files he wants it somewhere discreet and unnoticed...
The 'cookie.dat' file you refer to was originally written to an app specific subfolder inside 'Temp' or 'T' but the potential for it getting purged led me to move it into the container 'parent-folder' - on a PC that is expected to be written to... -
@TIG,
Your suggestion is interesting on Mac.
Actually, my problem is not so much to find a place where to write freely, but also a place:
- whose path is stable across time
- which is not erased without the user deciding
On Mac, with the convoluted name of the ENV["TMPDIR"] path, it remains to check it its parent remains stable through time.
Fredo
-
VERSION AGNOSTIC MAC PATH... vain attempt...v2013
any_mac_or_SUv_path = File.dirname(File.expand_path(Sketchup.find_support_file("Shortcuts.plist"))) << ('/.MY_NAMESPACE/.my_hidden_file.*')
Returns:
` */Users/johns_iMac/Library/Application Support/Google SketchUp 6/SketchUp/.MY_NAMESPACE/.my_hidden_file.[/Users/johns_iMac/Library/Application Support/Google SketchUp 7/SketchUp/.MY_NAMESPACE/.my_hidden_file.*
/Users/johns_iMac/Library/Application Support/Google SketchUp 8/SketchUp/.MY_NAMESPACE/.my_hidden_file.*
/Users/johns_iMac/Library/Application Support/SketchUp 2013/SketchUp/.MY_NAMESPACE/.my_hidden_file.***`
All mac users can write shortcuts... Shortcuts.plist may be dynamic but these are common fresh standard instal at that same path, so could be used as well or instead... "Autosave" "OldColors"
john
-
@tig said:
The equivalent for the PC is actually ENV["HOMEPATH"], but then returns say 'C:/Users/TIG', from which we'd need to construct the upwards tree towards /AppData/Local/XXX etc...
Problem occur if the user has non-ASCII names - then it will fail.
-
Non-ASCII characters in file names/paths cause issues with some of Ruby's File commands [in the flavor of Ruby shipped with PC SketchUp].
So you will get a false 'false
' result if you test for its existence, even when the folder actually exists:
So if you test for its existence, and then hope to make it if it doesn't exists, e.g.:
Dir.mkdir('C:/Users/Usersname/AppData/Local/XXXé') unless File.exist?('C:/Users/Usersname/AppData/Local/XXXé')
you will crash and burn because of the falsefalse
that results from theé
!The way to do this safely is...
begin Dir.mkdir('C:/Users/Usersname/AppData/Local/XXXé') rescue end
Which will make the folder, BUT if it does exists there's a silent error because it exists already...
Either way you'll get the specified folder !This is not ideal but ca trap for some false
false
File test issues... -
But that doesn't help anything. You cannot write to that folder in any way.
-
@tig said:
BUT...
No one has still answered my question...
What is wrong with using the container folder for the ENV for the OS's user's 'Temp' folder?
That is easily found for either OS and is writable, like the Temp folder itself...
Once you have the path to the user's 'Temp' folder we can then make an app-specific subfolder to hold our temporary files...
AND from File.dirname(Temp) we can get the container-location into which we can make an app-specific subfolder to hold our 'permanent' files...
I do it without problems on all OS's - the rest of you seem to obsessed with finding 'the exact right folder that we must use', when in truth there are several valid possibilities, and my earlier posts cover much of this too... KISS ?The temp directory is easy to get (
require 'tmpdir'; Dir.tmpdir
) but only useful for temporary files.If you're writing something to the temp directory with the intention of reading it later, you're doing it wrong. The temp directory is for files that have no consequence if they are removed at a later date. The temp directory is for writing, and reading only when you've been explicitly told to.
The "correct" place for persistent data for an application that is not part of the installation is the appdata location. These directories are separate for each user and guaranteed to be readable/writable for the associated user. This is normally were things like plugins/extensions would exist, also.
A good example of this (if you have it installed) is Sublime Text. (On windows) it is installed to
%PROGRAMFILES%/Sublime Text
, which contains all required binaries and application defaults. Then in%LOCALAPPDATA%/Sublime Text/
(or%APPDATA%/Sublime Text 2/
for st2 user level information is available, including plugins, settings, key configurations, ...Driven has posted what appears to be the MAC equivalent of
LOCALAPPDATA || APPDATA
.So, the search order for a path to write to should be: (keeping in mind I'm no MAC expert)
%LOCALAPPDATA% (win) %APPDATA% (win) ~/Library/???/Sketchup/ [or similar] (MAC) ["~" -> "/Users/<username>/"] Sub-directory in SketchUp directory [plugin-data or similar] (if writable) temp directory (last resort)
-
@unknownuser said:
The "correct" place for persistent data for an application that is not part of the installation is the appdata location. These directories are separate for each user and guaranteed to be readable/writable for the associated user. This is normally were things like plugins/extensions would exist, also.
But as I mentioned - Ruby 1.8 cannot handle non-ASCII characters. Usernames might include Unicode characters and then all file operations on the user directory will simply fail.
-
I will release the new versions of LibFredo6
- with ENV["LOCALAPPDATA"] on Windows
- with TIG's method on Mac, that is the parent directory of the ENV["TMPDIR"] folder. I think using ENV["HOME"] with some hidden subfolder would have been considered too intrusive.
These will be root folders into which I will create the LibFredo6 subfolders for persisting data.
I think in both cases these folder won't contain non-ascii characters, as they are defined and created by the system itself.
Fredo
-
On windows the Local path (ENV["LOCALAPPDATA"]) could return as say
f=ENV["LOCALAPPDATA"]) 'C:\Users\TIGé\AppData\Local'
which will return 'false
' in aFile.exist?(f)
test when it actually exists, just because the user-name contains an accented character!So use my 'safe' way [a few post ago] to make the folder, even if it exists already!
-
@thomthom said:
But as I mentioned - Ruby 1.8 cannot handle non-ASCII characters. Usernames might include Unicode characters and then all file operations on the user directory will simply fail.
All the more reason to update the ruby installation. The provided order would still "work" if you checked each of the directories existed (because the check would "simply fail"), but it's certainly no supplement for the actual feature.
It seems to be reoccurring that the SketchUp ruby API will gives you "clipped wings". I'm excited to see where it can go, but also impatient with its shortcomings (even though this one isn't really SU fault).
Side-note: No unicode support seems strange for something coming out of japan
-
@fredo6 said:
I will release the new versions of LibFredo6
- with ENV["LOCALAPPDATA"] on Windows
- with TIG's method on Mac, that is the parent directory of the ENV["TMPDIR"] folder. I think using ENV["HOME"] with some hidden subfolder would have been considered too intrusive.
These will be root folders into which I will create the LibFredo6 subfolders for persisting data.
I think in both cases these folder won't contain non-ascii characters, as they are defined and created by the system itself.
Fredo
ENV["LOCALAPPDATA"]
may contain non-ascii characters, as it contains the current users username. Also, you should useENV["LOCALAPPDATA"] || ENV["APPDATA"]
so you can support XP at no extra effortBonus:
-
Just to test things I created a user account "tæst" under Windows7. And ran some tests in the Ruby Console:
ENV['HOMEPATH'] \Users\t‘st ENV["LOCALAPPDATA"] C;\Users\t‘st\AppData\Local ENV["APPDATA"] C;\Users\t‘st\AppData\Roaming file = File.join( ENV['LOCALAPPDATA'], 'FooBar.txt' ) C;\Users\t‘st\AppData\Local/FooBar.txt File.open( tempfile, 'wb' ) { |file| file.puts 'Hello world' } Error; #<Errno;;ENOENT; (eval);0;in `initialize'; No such file or directory - C;\Users\t‘st\AppData\Local/FooBar.txt> (eval) (eval);0;in `open' (eval);0 ENV['TEMP'] C;\Users\TST~1\AppData\Local\Temp tempfile = File.join( ENV['TEMP'], 'FooBar.txt' ) C;\Users\TST~1\AppData\Local\Temp/FooBar.txt File.open( tempfile, 'wb' ) { |file| file.puts 'Hello world' } nil
Interesting observations:
Most
ENV
has some strange character replacements. Is this a Ruby thing?ENV['TEMP']
returned a path in the format of the old DOS 8.3 format. And this could be written to.ENV.each { |k,v| puts "#{k}\t#{v}" } ALLUSERSPROFILE C;\ProgramData APPDATA C;\Users\t‘st\AppData\Roaming CommonProgramFiles C;\Program Files (x86)\Common Files CommonProgramFiles(x86) C;\Program Files (x86)\Common Files CommonProgramW6432 C;\Program Files\Common Files COMPUTERNAME ARC-CU-10-09 ComSpec C;\Windows\system32\cmd.exe FP_NO_HOST_CHECK NO HOMEDRIVE C; HOMEPATH \Users\t‘st LOCALAPPDATA C;\Users\t‘st\AppData\Local LOGONSERVER \\ARC-CU-10-09 NUMBER_OF_PROCESSORS 8 OS Windows_NT PATHEXT .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC PROCESSOR_ARCHITECTURE x86 PROCESSOR_ARCHITEW6432 AMD64 PROCESSOR_IDENTIFIER Intel64 Family 6 Model 26 Stepping 5, GenuineIntel PROCESSOR_LEVEL 6 PROCESSOR_REVISION 1a05 ProgramData C;\ProgramData ProgramFiles C;\Program Files (x86) ProgramFiles(x86) C;\Program Files (x86) ProgramW6432 C;\Program Files PSModulePath C;\Windows\system32\WindowsPowerShell\v1.0\Modules\ PUBLIC C;\Users\Public SESSIONNAME Console SystemDrive C; SystemRoot C;\Windows TEMP C;\Users\TST~1\AppData\Local\Temp TMP C;\Users\TST~1\AppData\Local\Temp ULTRAMON_LANGDIR C;\Program Files\UltraMon\Resources\en USERDOMAIN arc-cu-10-09 USERNAME t‘st USERPROFILE C;\Users\t‘st windir C;\Windows
The TEMP path seem to be the only one using this format.
So, question is, is it possible to monkey-patch this so one can extract the user directory in DOS8.3 format and use that to replace the mangled paths of the other variables?And what does it return under other Windows versions? (Win8, Vista, XP)
What about chineese/japanese types or characters - does the same thing apply?
-
@unknownuser said:
Also, you should use
ENV["LOCALAPPDATA"] || ENV["APPDATA"]
so you can support XP at no extra effortWhat folders does this yield on XP? Under Win7 it's both in the user directory.
Advertisement