JXA - Classes, Subclasses / Libraries, Sub-Libraries. Is it possible?

Really? How odd. In my year or so using javascript I have never seen this pattern for constructing classes... typically I have seen The Constructor Pattern and then the full on class myClass {...} method. Most of these patterns are new to me even. Perhaps the constructor pattern just doesn't work when called from a library in JXA...

No, the constructor pattern should work, although oddly I have had instances where it told me I was assigning something to a readonly property, and I couldn’t figure out why.

The Module pattern is nice because it allows you to have “private” members. Nice, but by no means necessary.

And to be honest, there’s some issues with the pattern. For one thing, unit testing is difficult, because you can’t use mock functions with the private members. The more I use it, the more I wonder if it’s the best solution.

But it’s what I started with, so for now I’m keeping it. I do like private members. (No jokes, please. :open_mouth:)

Why? It is for boys only? LOL

Hey! What part of “No jokes, please” didn’t you understand?!?

LOL.

I wouldn't have even thought about a joke if you hadn't mentioned it.
But with such an invitation, . . .

1 Like

A post was merged into an existing topic: MACRO: Macro Repository (i.e. Version Control) Beta

Hey Dan,

This is what I was talking about:

https://puu.sh/to3nb/d78f7b94fc.png

You can't use the FileUtils class in a library, on my mac anyways... Any idea why?

P.S. lib is saved in ~/Library/Script Libraries/ so can act as a library as specified here.

Nope, no ideas. These are the exact kinds of issues I ran into when I tried using libraries myself, and why I gave up on them.

If memory serves, the standard Constructor pattern works. If so, it’s probably a nail in the coffin of the Module pattern, at least for uses like this.

That explains it. That's actually what this thread is about... The inability to do such things (even with Constructor Pattern). I probably didn't explain this well (i was very frustrated last night after spending the whole weekend on the library! xD).

You can call a class from the same document as the class definition. But as soon as you put the class into a seperate library everything grinds to a hault.

Libraries are not something I personally associate with projects as small and ephemeral as scripts, particularly as dependencies of any kind add friction to script-sharing – I tend to just paste the boiler-plate in, and let the file stand alone.

If what you are building is not so much transient throwaway scriptlets (built around automation glue) as larger JS tools that may sometimes want to sling a few AE events about, then it might be worth looking at using Node.js / npm approaches, and do things like this:

(The Library mechanism provided by Apple for JavaScript for Automation was probably never intended for scale or complexity, and probably isn’t all that deeply tested or maintained either)

1 Like

On the particular issue you report, however, the thing to notice is that the Automation.Library method exports only method names from the referenced library file. (See the Library section in JS for Automation release notes). (It doesn't export names which are bound to objects which are not themselves functions).

To obtain a reference to Dan's FileUtils module, you would need to call a method, at the top level of the library file, which returns a reference to the module you want to use.

If, for example we add such a method to the top of Dan's file:

We can then, from a client script, use the Automation.Library method like this:

Scratch that. I think I’m misunderstanding the mechanism :slight_smile:

Hey ComplexPoint,

Thanks for the attempt! I did think about npm and it would definitely make a lot of things easier, however I am not only making this library for me. I would prefer it in native apple Scripting Languages since then others will be able to use it easily without need for installing dependencies.

JXA/Javascript is fantastic for this as it is very close to the programming languages I am used to, and is native on all apple machines Yosemite+. If I have to stick to the “boiler-plate” method, I will do just that :slight_smile:

With regards to how a library works, I am under the impression that when JXA launches a library it actually runs it as it’s own stand-alone application/process running in ObjC JavascriptCore. When you instantiate a library, what you actually get in the original script appears to be an [Object Specifier] pointing to the application running in ObjC JSCore. This is how I’ve interpreted the documentation / errors I have received anyway.

1 Like

I have a technique I used previously, but up until now I haven’t adapted it for more general use.

The technique is basically an “include” feature, where I can put special comments in a JXA script to say “include FileUtils.scpt”, or whatever. Then I run a utility, and it copies the indicated file(s) into the script.

What’s the use of that?, you may ask. The use is, I can run the utility anytime I change one of the “include” files, and it updates the script to have the latest version in it. So when I fix a bug, I can easily update all scripts that use it.

I hope that makes sense. Anyway, I think I’ll update this to be a more general-purpose utility, and start using it. Let me know if it interests you at all. It’s cool if it doesn’t - it’s not the perfect solution.

That's likely the best way to go indeed. Even more ideal if it were part of a script editor (perhaps even written itself in JXA). Alternatively written as a plugin for atom editor...

Ooh, I like what you're thinking. When will you get it done? :wink: I'll beta-test it!

I barely have any time for such a project at the moment, but will look into it at some point I am sure!

Had a brief look around and it appears that to execute a shell script from a file you can use the following function:

const exec = require('child_process').exec;
exec('cat *.js bad_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.log(`stderr: ${stderr}`);
});

or

const execFile = require('child_process').execFile;
const child = execFile('node', ['--version'], (error, stdout, stderr) => {
  if (error) {
    throw error;
  }
  console.log(stdout);
});

See this

To execute applescript/JXA from atom commands such as this can be used:

var cmd = "osascript -e 'set x to "a"\nsay x'"

Not entirely sure how well this will work with more complex scripts, like those requiring subclassing for instance… However it would likely be better to compile these to .app… I’ll see if I get time this weekend to look into it!

Not sure I entirely followed that, but go for it! :slight_smile:

Regarding the class issue, i just asked Sal Soghoian whether it was possible. The response i got was:

Ah I see. Alas, the class support you seek is available in libraries. It was planned to be added, but Apple decided to focus on other things instead.

I interpret this as:

  • Classes are available in libraries
  • Sub-Libraries were planned to be added, but Apple decided to focus on other things.

That's just my interpratation though. My original message that motivated this response:

I know very well the problem and I am fairly fine with it, since many windows applications are the same - I'm fairly used to having to resort to UI scripting.

My main issue is I can't figure out how to make a JavaScript class equivalent in a JXA library... There must be a way but I have no idea how?

1 Like

Hi all,

I haven’t made this a package yet, and neither have I got this auto-running the OSAScript on “compile”, however I have created the following code.

See it in action:

It produces a file in the same directory called e.g. “myscript_transpiled.scpt”. This script can then be officially compiled with osascript compile process. This is tomorrow’s job! :stuck_out_tongue:

1 Like