I usually just rewrite it every time I start a project.
Something like thisā¦
Import {
classvar root, cached;
*projectRoot { |path| root = path; cached = IdentityDictionary(); }
*new { |path|
var fullPath = (root +/+ path).asSymbol;
^cached[fullPath] ?? {
var e = Environment.make( { fullPath.asString.load } );
e.know = true;
cached[fullPath] = e;
^e
}
}
}
...
Import.projectRoot("~/Desktop".standardizePath);
~a = Import("a.scd");
~a.foo == 10; // its starting value
~a.foo = (); // reassigning the value.
~b = Import("a.scd");
~b.foo === ~a.foo // these are *identical* and equal '()'
Some things to considerā¦
I like to have flat modules, so all the paths are given relative to the project directory, not from the current module. Change that if you donāt like it, but I find it helps me keep things simple.
Modules are singleton objects and you can change their values if you want. This is confusing with the cache system as all instances change. I just donāt mutate stuff, or pass an argument to disable the cache if I find it really necessary.
All the nonsense with name collision and environment variables⦠when I get the time, I will get abstract object working as this is a good use case for it.
When having a file that starts a process (or something with mutable state), I usually have an ~init function that creates the process, I do not pass arguments to the modules.
Variables declared with var provide a way of making things āprivateā to the file, environment variables are āexportedā.
Additionally, I sometimes add an Import.sync(...) which calls s.sync afterwards, this is useful when making synthdefs. There are other ways of doing this, but often I make all my synthdefs at the beginning and really donāt care. For some projects, I make all imports āsyncā and put all the main code in an s.waitForBoot block.
It isnāt a perfect solution⦠but I find it much easier that dealing with files directly as I often abuse them and things quickly get too complicated.