Indirectly Referencing One Macro From Another

Earlier tonight I posted, then removed, a macro that tracked macro usage. The macro itself works great, that's not the problem. The problem is that the way it has to work seemingly makes it basically impossible to ever replace—I can edit mine just fine, but if I distribute it, and someone already has the macro installed, all sorts of heck will eventually occur.

Here's the problem I'm trying to solve: I need to get the UUID (and name, etc., but if I can get the UUID, I can get all of that) of the user's macro that is to be counted. In my current version, it works in this manner:

User's macro: An action is inserted which calls my count launch macro, which simply gets the %ExecutingMacroUUID% token (and the other associated tokens. The count launch macro saves these values in a permanent variable, then hands things off to the worker module, which runs asynchronously. (You can't get macro info from the calling macro if the called macro is running asynchronously, hence my two-step solution.)

And while this works great, it means that each macro has a call in it to my starter macro. Everything's going great, but now the user updates my macro. When they do, the original call to the counter will fail in one of two ways. If they left the old version installed, it will call that version (and if it's disabled, the macro will quit itself). That's the better of the two scenarios.

If they delete the old version, then every macro of theirs that calls the counter will fail to run, because the first step tells it to run a macro that doesn't exist.

I honestly don't see a solution to this problem. I thought maybe I could launch by name via AppleScript, and use that as the macro that gets added to each macro. Given a unique name, it should work. However...

If you launch via AppleScript, it appears things happen asynchronously, because the UUID I get back is that of the counting macro, not that of the calling macro.

I'm not expecting much help here, as this is really esoteric, but it seems like it's something that should be doable ... any ideas?

-rob.

I figured it out, and it's oh so much simpler than I was trying to make it. The answer is to change the tracking macro to one that runs periodically. Now each macro to be tracked appends its info to a permanent "to be tracked" variable. When the periodic tracking macro runs, it checks for that variable's existence. If it exists, it then processes each line in it, using the same code as before with minor changes, adding each entry to the usage counts. It then deletes the "to be tracked" variable and waits.

It's working well so far; I'll post an updated version later today.

-rob.

2 Likes

Hey @griffman

I am sorry to read about the issues you had with the described Macro - but I am thrilled to see what you have done to fix the issues.
Since I saw it as it was present to all for downloading I can say that it is an outstanding one like other Macros from you. - Thanks for taking so much effort building and sharing these.

And since I dont have the original code here are some thoughts I have:

For the first run of the Macros I would create a Variable in JSON format which will store all the nessesary stuff from all the Macros belonging their Group or Groups - if the Macros are installed in more than one Group - what I would prefer since you are working with an update routine.

The JSON could be something like this pseudo Code:


[
	{
		// Macro Group 1:
		// -> Macro Group Details not added yet.
		// -> TWO MACROS with TWO Key/Value Pairs each
		// 	  -> Macro Name & Macro UUID 
		"Main_Routine_Macros" : {
			"MR_Macro 1" : {
				[
					{
						"Name_MR_Macro_1" : "My_Name_is_MR_1"
					},
					{
						"UUID_MR_Macro_1" : "UUID_MR_1"
					}
				]
			}
		},
		{
			"MR_Macro_2" : {
				[
					{
						"Name_MR_Macro_2" : "My_Name_is_MR_2"
					},
					{
						"UUID_MR_Macro_2" : "UUID_MR_2"
					}
				]
			}
		}
	},
	{
		// Macro Group 2:
		// -> Macro Group Details not added yet.
		// -> TWO MACROS with TWO Key/Value Pairs each
		// 	  -> Macro Name & Macro UUID 
		"Update_and_Install_Routines" : {
			"UPIR_Macro_1" : {
				[
					{
						"Name_UPIR_Macro_1" : "My_Name_is_UPIR_1"
					},
					{
						"UUID_UPIR_Macro_1" : "UUID_UPIR_1"
					}
				]
			}
		},
		{
			"UPIR_Macro_2" : {
				[
					{
						"Name_UPIR_Macro_2" : "My_Name_is_UPIR_2"
					},
					{
						"UUID_UPIR_Macro_2" : "UUID_UPIR_2"
					}
				]
			}
		}
	}
]

Note this is a quick Template.
With something like that you have the ability to delete only the Macro Group (s) which / whose not Perform the Setup Work and You can rebuild the other ones from scratch using AppleScript and/or JXA using the Infos from the Variable so you can relink them all at run time like they weren't deleted before.
For that I prefer to use a detailed Version of this JSON code containing all nested enable / disable / toggle / exeute ~ Macro / Macro Group Actions of every Macro In the Macro Set.
If the main purpose Macros are running the first time after the Update they can also use the JSON Variable to relink the Updater - also at runtime using their during the Install / Update Process newly created UUIDs with the help of a dedicated Install Helper Macro In the Install & Update Group.

I am currently building another sytem for Me to store Infos from Macros like this what brought me to post this Idea for you if you aren't using something like that - and of course for all others ...

Maybe I will post something using this Idea when I am done with it - but I'm not sure how log this will take

There are many many ways to use this kind of aproach - from simple to advanced ...

greetings from Germany

Tobias

I think this is a great solution! I'm not sure I would have thought of it.

When you append to the variable, do you do it in one single action, or does it take more than one action? Because if it takes more than one action, you could run into multi-threading issues (which are easy to work around, if you need to).

It's just one action:

I tried to beat up on it quite a bit, running multiple macros with pauses in them rapidly, to make sure it caught them all. It seemed to work fine for me, but I'll be interested to hear if anyone catches it not recording something.

And I realized the macro that does the work doesn't even have to run periodically; I just call it when you want to see the report. So now there's basically no always-running overhead for macro usage tracking. (I'm reposting it now.)

-rob.

That looks good to me. I'm pretty-sure I remember Peter saying he protects variables from multi-threaded read/write issues, at least on a per action basis.

Ooh, except for one thing: When you grab the variable and update your stats, you probably reset the variable, right? So if something updates the variable between the time you get its value and the time you clear it, you could lose something.

Probably not real likely, though, and it's not like it's mission-critical data anyway, right?

Yea, that'd be the only time it could happen—and it's probably going to be a small window of time, given I don't imagine there are going to be tons of entries between processing. Just for fun, though, I put 500 items in the list and...

...something could get written in that time. That seems extreme; with 20 or so, it takes 0.2 seconds. But I thought of a fix for this, too—I can just copy the contents to a local variable, then delete the main variable:

That cuts the exposure window to 0.007 seconds, which seems tiny enough for me. The risk, of course, is that something goes wrong with the move to the local variable, and you lose the last round of tracking updates. Given I've never had a copy variable operation fail in KM, I think I can live with that risk—and as you noted, not mission critical data.

I could add a logic check, I guess, to see if the two variables are the same immediately after copying. But if they're not, I think there are bigger issues going on :).

-rob.

Nah, you don't want to do it that way. If you're OK with multiple actions, then use Semaphores - that's what they're for.

Like this, for each macro:

  • Lock Semaphore (give it a name unique to what you're doing. Anything, really.)
  • Append Variable
  • Unlock Semaphore

In your "Update" macro, you do basically the same thing:

  • Lock Semaphore (same name)
  • Get Variable
  • Clear Variable
  • Unlock Semaphore

Then you're golden.

The downside of this is that you need multiple actions in each macro, which isn't a huge deal - you could even wrap them in a group.

But let's be honest here - It's not really necessary. The chances of an overwrite happening are extremely remote, and if it did, you wouldn't lose very much.

Personally I'd leave it as is, and that's saying something, because I'm usually anal about stuff like this (which is why I mentioned it in the first place).

1 Like

Yea, I thought about semaphores, but really wanted to keep the "insert this into your macro" bit as simple as possible. And you're probably right—best to just live with the risk of maybe missing one even if you're updating hundreds plus at once then risk losing the big pile.

-rob.

Sounds good. One thing I thought of, though. You might still want to have the timed macro. That variable could grow pretty fast.

It's in there, disabled by default with instructions on how to turn it on (and why you might want to):

-rob.

Sounds good! I'd hate for you to have to re-upload the macros. I've been there before, and it stops being fun right away. :laughing:

The good news is the in-macro updater will notify anyone who's using it if I release a new one—and that won't require any rework on the tracking bit in each macro now, which was the nightmare scenario last night.

-rob.

Uh, you wanna repeat that? How the heck does that work, and where can I get one? :grinning_face_with_smiling_eyes:

That's a bit of a speed-related mistype—it's not actually an updater, it's a notifier of an update, with the ability to download from within the macro. I plan on posting it shortly, but I keep distracting myself with other tasks.

If you download my macro, though, you can see it in the Check for Update macro. It's quite basic—it just checks the version number of the macro against a version number stored on my blog server. If there's a newer one, it shows the release notes and a download button.

Well, that's way cool, and I happen to have a blog server of my own (well, GitHub's server, but good enough). When I get done with what I'm working on, I'll take a look at it.

I wonder if there's any way to have it tied to a post here on the forum? Perhaps some special way of formatting something in the post? Anyway, way cool, and I'll be sure to check it out.

I just curl a file on the server, so you could do the same with a forum post. Save the output to a variable or file and then parse it, looking for the version number. The dangers are if someone copied your version number 'code' they could make it look like an update was out—maybe count lines between your name at the top and the version number to verify? Second danger is if the forum post goes away, then the updater breaks.

-rob.

1 Like

Posted:

-rob.