How to make a handler to wrap around target code

I really need expert help here.
How can I make a handler and how to call it, so that it wraps around a few code lines in the main script and then passes the result to the script for further processing. For instance:

-- A script snippet
-- ==============
-- do many things .
set myPBtitle to "title"
set myPBheader to "header"
set myPBiconPath to ""
tell application "Finder"
	activate
	set myFolderPath to choose folder
	set myFolderContainer to container of myFolderPath as string
	set myFolderName to (name of myFolderPath) as string
	set myFolderPOSIX to quoted form of POSIX path of myFolderPath
	set myDmgNamePOSIX to quoted form of ((POSIX path of myFolderContainer) & myFolderName & ".dmg")
	-- make the dmg file.
	tell current application
		do shell script "hdiutil create -srcfolder " & myFolderPOSIX & " " & myDmgNamePOSIX
	end tell
	open (myFolderContainer & myFolderName & ".dmg")
end tell
-- do more things .

-- Progress Bar Handler
-- ===================
on ProgressBarHandler(myPBtitle, myPBheader, myPBiconPath)
	tell application "SKProgressBar" -- StefanK's app at http://macscripter.net/viewtopic.php?id=36409
		set floating to true
		set position to {600, 550}
		set width to 500.0
		set title to myPBtitle
		set header to myPBheader
		set header alignment to left
		set footer to "100%"
		set footer alignment to right
		set image path to myPBiconPath
		tell progress bar
			set minimum value to 0.0
			set maximum value to 100.0
			set current value to 0.0
		end tell
		set show window to true
		tell progress bar
			activate
			set indeterminate to false
			start animation
			
			(* === Here goes the code to be handled  ===*)
			
			repeat 10 times
				increment by 10.0
				delay 1
			end repeat
			stop animation
		end tell
		quit
	end tell
end ProgressBarHandler

The code to be handled in this example is

tell current application
	do shell script "hdiutil create -srcfolder " & myFolderPOSIX & " " & myDmgNamePOSIX
end tell

I know that I could make a handler specific for this script:

-- A script snippet
-- ==============
-- do many things .
set myPBtitle to "title"
set myPBheader to "header"
set myPBiconPath to ""
tell application "Finder"
	activate
	set myFolderPath to choose folder
	set myFolderContainer to container of myFolderPath as string
	set myFolderName to (name of myFolderPath) as string
	set myFolderPOSIX to quoted form of POSIX path of myFolderPath
	set myDmgNamePOSIX to quoted form of ((POSIX path of myFolderContainer) & myFolderName & ".dmg")
	
	-- insert the handler to make the dmg file with a progress bar.
	my SpProgressBarHandler(myPBtitle, myPBheader, myPBiconPath, myFolderPOSIX, myDmgNamePOSIX)
	
	open (myFolderContainer & myFolderName & ".dmg")
end tell
-- do more things .

-- Specific Progress Bar Handler
-- ===========================
on SpProgressBarHandler(myPBtitle, myPBheader, myPBiconPath, myFolderPOSIX, myDmgNamePOSIX)
	tell application "SKProgressBar" -- StefanK's app at http://macscripter.net/viewtopic.php?id=36409
		-- the first part of the progress bar handler code
			
			tell current application
				do shell script "hdiutil create -srcfolder " & myFolderPOSIX & " " & myDmgNamePOSIX
			end tell
			
		-- the second part of the progress bar handler code
	end tell
end SpProgressBarHandler

but, I want a vanilla Progress bar handler, callable from my library of handlers to handle any process which could benefit from a progress bar.

An alternative solution, which just occurred to me while writing this, is to write a vanilla progress bar handler with a variable where the code to be handled goes, pointing to a local handler containing the code to be handled. Kind of handler with a nested handler:

-- A script snippet
-- ==============
-- do many things .
set myPBtitle to "title"
set myPBheader to "header"
set myPBiconPath to ""
set ProcessToBeHandled to (my MakeDmgHandler(myFolderPOSIX, myDmgNamePOSIX))
tell application "Finder"
	activate
	set myFolderPath to choose folder
	set myFolderContainer to container of myFolderPath as string
	set myFolderName to (name of myFolderPath) as string
	set myFolderPOSIX to quoted form of POSIX path of myFolderPath
	set myDmgNamePOSIX to quoted form of ((POSIX path of myFolderContainer) & myFolderName & ".dmg")
	
	-- insert the handler to make the dmg file with a progress bar.
	my VanProgressBarHandler(myPBtitle, myPBheader, myPBiconPath, myFolderPOSIX, myDmgNamePOSIX)
	
	open (myFolderContainer & myFolderName & ".dmg")
end tell
-- do more things .

-- Vanilla Progress Bar Handler
-- ==========================
on VanProgressBarHandler(myPBtitle, myPBheader, myPBiconPath, myFolderPOSIX, myDmgNamePOSIX, ProcessToBeHandled)
	tell application "SKProgressBar" -- StefanK's app at http://macscripter.net/viewtopic.php?id=36409
		-- the first part of the progress bar handler code
			
			ProcessToBeHandled
			
		-- the second part of the progress bar handler code
	end tell
end VanProgressBarHandler

-- Make dmg Handler
-- =================
on MakeDmgHandler(myFolderPOSIX, myDmgNamePOSIX)
	tell current application
		do shell script "hdiutil create -srcfolder " & myFolderPOSIX & " " & myDmgNamePOSIX
	end tell
end MakeDmgHandler

Is any of this possible? Can somebody please help me with a clever handler solution?

Cheers,
Chris

Something like this?

set theFolder to choose folder
invokeHandlerWithProgressBar(createDMG, {sourceFolder:theFolder})

on invokeHandlerWithProgressBar(theScript, theOptions)
	display dialog "start of progressbar" --just to simulate
	theScript's runHandler(theOptions)
	display dialog "end of progress bar" --just to simulat
end invokeHandlerWithProgressBar

script createDMG
	on runHandler(opts)
		set sourceFolder to sourceFolder of opts
		do shell script "hdiutil create -srcfolder " & quoted form of POSIX path of sourceFolder & " \"$(dirname " & quoted form of POSIX path of sourceFolder & ")/$(basename " & quoted form of POSIX path of sourceFolder & ").dmg\""
	end runHandler
end script

Discussion

First of all I’m using an script object to send with an fixed handler name. You can use handler names and pass them but as you said you want them to use as an library they will get out of scope. AppleScript can’t make late bindings on handler references and it feels like AppleScript isn’t designed to do that. So there is an runHandler in the script object that needs to be called.

There is also 1 argument, an record, so you don’t have an fixed amount of prarameters. You put all the parameters in one argument and read them out by the runHandler. Unlike many programming languages, AppleScript isn’t dynamic in handler arguments.

This is only 1 way to go. The advanatage of using script object is also that you can store them as separate files. So the createDMG script could be saved as file and used by many other scripts using the load script command. Like I said, it’s the way I should do it but that doesn’t make it the best way.

Hello.

There is another way, and that is for instance to do like I have done in post #22 in this thread. I learned by looking at Nigel. :slight_smile:

What should be possible, is to take say some lines of script, copy them to the clipboard, embed the clipboard into a script, which you then assign to a variable, maybe after having escaped stuff with sed in between, and then run it, to create a fully valid script object, with valid Applescript references inside it, which can communicate with the script around it.

You’ll need to know sed, or something else, that can generally escape such a script so it will run. Some effort but great results.

Somethinng else, would maybe be to have some script statements in a document with data detection, and the script statments selected there, then you should be able to use the “service runner” or whatever, to run the "run applescript service on that document. (TextEdit?).

I hope this helps.

The only difference would be that you’re using uncompiled script code whereas I’m using compiled code :slight_smile:

Sorry for the late reply. I had one of those crazy days.

Hi DJ, I do understand your script in principle but, I’m still trying to get my head around how to get the main handler to start and end the progress bar around the ‘theScript’s runHandler(theOptions)’. Could you please expand?

Hi McUsrII, I just read that recent thread. Very interesting. I do have some basic grouding in sed and this is an opportunity to get my hands dirty.

In the meantime, I did some work trying to see if my alternative brainstorm can work.

Here it is. It looks like brute force approach to the problem.
It’s ugly, not quite callable but hey, by some miracle, it just works, kind of :wink:
Obviously, you’ll need StefanK’s Progress Bar applet SKProgressBar.

-- A script snippet
-- ==============
-- do many things .

tell application "Finder"
	activate
	set myFolderPath to choose folder
	set myFolderContainer to container of myFolderPath as string
	set myFolderName to (name of myFolderPath) as string
	set myFolderPOSIX to quoted form of POSIX path of myFolderPath
	set myDmgFilePOSIX to quoted form of ((POSIX path of myFolderContainer) & myFolderName & ".dmg")
	
	(* === This is required to localise the vanilla progress bar handler. === *)
	-- Progress bar variables
	set myPBtitle to "title"
	set myPBheader to "header"
	set myPBiconPath to ""
	-- Set the variable pointing to the local process handler nested in the vanilla progress bar handler.
	set ProcessToBeHandled to (my MakeDmgHandler(myFolderPOSIX, myDmgFilePOSIX))
	-- Add the variable from the proces handler to the ones from the progrss bar handler.
	my VanProgressBarHandler(myPBtitle, myPBheader, myPBiconPath, ProcessToBeHandled, myFolderPOSIX, myDmgFilePOSIX)
	(* === *)
	
	open (myFolderContainer & myFolderName & ".dmg")
end tell
-- do more things .

-- Vanilla Progress Bar Handler
-- ==========================
on VanProgressBarHandler(myPBtitle, myPBheader, myPBiconPath, ProcessToBeHandled)
	tell application "SKProgressBar"
		set floating to true
		set position to {600, 550}
		set width to 500.0
		set title to myPBtitle
		set header to myPBheader
		set header alignment to left
		set footer to "100%"
		set footer alignment to right
		set image path to myPBiconPath
		tell progress bar
			set minimum value to 0.0
			set maximum value to 100.0
			set current value to 0.0
		end tell
		set show window to true
		tell progress bar
			activate
			set indeterminate to false
			start animation
			
			-- Variable pointing to the process handler.
			-- Here it is (my MakeDmgHandler(myFolderPOSIX, myDmgNamePOSIX))
			ProcessToBeHandled
			
			repeat 10 times
				increment by 10.0
				delay 1
			end repeat
			stop animation
		end tell
		quit
	end tell
end VanProgressBarHandler

-- Make dmg Handler
-- =================
-- This is an example of a process handler to be nested in the progress bar handler.
on MakeDmgHandler(myFolderPOSIX, myDmgFilePOSIX)
	tell current application
		do shell script "hdiutil create -srcfolder " & myFolderPOSIX & " " & myDmgFilePOSIX
	end tell
end MakeDmgHandler

-- Copy to temp folder Handler
-- ===========================
-- This is another example of a process extracted from a script and converted to a local process handler.
-- It can then be nested in the progress bar handler.
on CopyToTempFolderHandler(ItemsCount, MySelection, tempFolder)
	-- Copy the finder selection to the temporary folder.
	repeat with i from 1 to ItemsCount
		tell application "Finder"
			set theItemPOSIX to quoted form of POSIX path of (item i of MySelection as string)
			set nextItem to quoted form of (tempFolder & name of item i of MySelection)
		end tell
		tell current application
			do shell script "cp -R " & theItemPOSIX & " " & nextItem
		end tell
	end repeat
end CopyToTempFolderHandler

Any comments or ideas?

Cheers, Chris

Sure, first I try to explain the previous code first. The createDMG script object is an script object from your main script or separate file. The invokeHandler is from your progress handler which receives an complete script object that contains an runHandler handler with will be invoked by the progress bar library.

run mainScript

script progressBarLib --this is the library file
	on invokeHandlerWithProgressBar(theTitle, theHeader, theFooter, theScript)
		tell application "SKProgressBar"
			set floating to true
			set position to {600, 550}
			set width to 500.0
			set title to theTitle
			set header to theHeader
			set header alignment to left
			set footer to theFooter
			set footer alignment to right
			set show window to true
			tell progress bar
				activate
				set indeterminate to true
				start animation
			end tell
		end tell
		theScript's runHandler()
		tell application "SKProgressBar"
			stop animation
			set show window to false
		end tell
	end invokeHandlerWithProgressBar
end script

script mainScript --this is the main script
	set createDMG's sourceFolder to choose folder
	
	progressBarLib's invokeHandlerWithProgressBar("Disk Image Creator", "Creating image from: " & POSIX path of createDMG's sourceFolder, "", createDMG)
	
	
	script createDMG --this is an script object that we'll send to progressbar that can be loaded from a file as well
		property sourceFolder : missing value
		on runHandler()
			do shell script "hdiutil create -srcfolder " & quoted form of POSIX path of sourceFolder & " \"$(dirname " & quoted form of POSIX path of sourceFolder & ")/$(basename " & quoted form of POSIX path of sourceFolder & ").dmg\""
		end runHandler
	end script
end script

Thank you! Thank you! A vey elegant solution :slight_smile:
But, I hate that I didn’t think of splitting the SKPregressBar the way you did :expressionless:
Thanks again DJ :cool:

My pleasure,

Always nice to hear that help is appreciated.

Hi DJ,
The shell command ‘do shell script "hdiutil .’ in your script doesn’t seem to work.
Building the full path of the dmg file name fails with → error "hdiutil: create: Only one image can be created at a time.
I replaced it with a combination of AS and shell commands:

.
tell application "Finder" to set dmgFilePath to quoted form of POSIX path of ((container of sourceFolder as string) & (name of sourceFolder as string) & ".dmg")
do shell script "hdiutil create -srcfolder " & quoted form of POSIX path of sourceFolder & " " & dmgFilePath
.

That works but, I can’t work out why yours doesn’t. Can you fix it please?

Cheers, Chris

It lacks for checking if there is already an dmg located with same name. probably the error occurred when you ran the script for the second time choosing the same source folder.

Just replace the createDMG script with this:

script createDMG --this is an script object that we'll send to progressbar that can be loaded from a file as well
		property sourceFolder : missing value
		on runHandler()
			set src to quoted form of POSIX path of sourceFolder
			set trgt to quoted form of (do shell script "echo $(dirname " & src & ")/$(basename " & src & ").dmg")
			set x to do shell script "[ ! -e " & trgt & " ] && hdiutil create -srcfolder " & src & space & trgt & " || echo [NULL]"
			if x = "[NULL]" then
				activate me
				display alert "File Exists:" message "The requested file " & trgt & " already exists. The process can't continue and will be stoppped" buttons {"OK"} default button 1 as warning
			end if
		end runHandler
	end script

Thanks DJ, this version works, as well as adding to my ‘do shell script’ bag of tricks :wink:
However, the failure of the previous version at #6 was not due finding an existing dmg file with the same name.
Had that been the case, the error would have been:
→ error “hdiutil: create failed - File exists” number 1, instead of the actual error
→ error "hdiutil: create: Only one image can be created at a time.
I checked the old version again. The failure was due to something wrong with the way the target was built, although I still don’t know what was wrong with that syntax :frowning:

-- This fails
do shell script "hdiutil create -srcfolder " & quoted form of POSIX path of sourceFolder & " $(dirname " & quoted form of POSIX path of sourceFolder & ")/$(basename " & quoted form of POSIX path of sourceFolder & ").dmg"
--> error "hdiutil: create: Only one image can be created at a time.

-- While this works (using your new 'src' and 'trgt' settings)
set src to quoted form of POSIX path of sourceFolder
set trgt to quoted form of (do shell script "echo $(dirname " & src & ")/$(basename " & src & ").dmg")
do shell script "hdiutil create -srcfolder " & quoted form of POSIX path of sourceFolder & space & trgt
-- Since 'src' is 'quoted form of POSIX path of sourceFolder', the bug must be in the old 'trgt' syntax.
set trgt_old to " $(dirname " & quoted form of POSIX path of sourceFolder & ")/$(basename " & quoted form of POSIX path of sourceFolder & ").dmg"

Anyway, your overall solution is great. I have already implemented it in two of my most used scripts :cool:
Many thanks again,
Cheers, Chris

The problem with the old way is that it should be wrapped in double quotes. Now when the file path contains one or multiples spaces it will be send as different parameters to hdiutil. Thanks, it was way past bedtime so maybe I mis interpreted your error wrong.

it should be something like this:

do shell script "hdiutil create -srcfolder " & quoted form of POSIX path of sourceFolder & " \"$(dirname " & quoted form of POSIX path of sourceFolder & ")/$(basename " & quoted form of POSIX path of sourceFolder & ").dmg\""

edit: the new code checks if the file exists before continuing with it’s own error, so I think you should stick with the latest version. I also update my previous posts in #2 and #6

That’s exactly what I’ve done. Error catching is always foremost in my mind.

Many thanks again for your overall solution,
Chris