Skip to content

Instantly share code, notes, and snippets.

@estum
Created September 22, 2022 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save estum/80754559a82987fb29b9f2d908e672c3 to your computer and use it in GitHub Desktop.
Save estum/80754559a82987fb29b9f2d908e672c3 to your computer and use it in GitHub Desktop.
Ruby Osascript runner
# @example Simple
# Osascript.new(<<~SCPT.freeze).()
# activate application "Finder"
# SCPT
#
# @example JSC with args
# # The script takes 2 arguments: directory path & image path
# # to set a folder icon to the given directory.
# script = Osascript.new(<<-JS.freeze, lang: 'JavaScript')
# ObjC.import("Cocoa");
# function run(input) {
# var target_path = input[0].toString();
# var source_image = $.NSImage.alloc.initWithContentsOfFile(input[1].toString());
# var result = $.NSWorkspace.sharedWorkspace.setIconForFileOptions(source_image, target_path, 0);
# return target_path;
# }
# JS
# script.(target_dir, folder_icon)
class Osascript
attr_accessor :script
def initialize(script = nil, lang: "AppleScript")
@script = block_given? ? yield : script
@lang = lang
end
def call(*other)
handle_errors do
cmd = ["/usr/bin/env", "osascript", *params(*other)]
IO.popen cmd, "r+", 2 => %i(child out) do |io|
io.write script
io.close_write
io.readlines
end
end
end
def params(*args)
["-l", @lang].tap { |e| e.concat(args.unshift(?-)) unless args.empty? }
end
ERROR_PATTERN = /(?<=execution error: )(.+?)(?=$)/
USER_CANCELLED_PATTERN = /user canceled/i
NL = "\n"
private
def handle_errors
yield().each_with_object([]) do |line, buf|
line.match(ERROR_PATTERN) { |m| raise error_for(m[0]), m[0], caller(4) }
buf << line.strip
end.join(NL)
end
def error_for(msg)
USER_CANCELLED_PATTERN.test?(msg) ? UserCanceled : ExecutionError
end
class ExecutionError < RuntimeError
CAPTURE_MSG_AND_CODE = /(.+?) \((-?\d+?)\)$/
attr_reader :code
def initialize(msg)
msg.match(CAPTURE_MSG_AND_CODE) { |m| msg, @code, * = m.captures }
super(msg)
end
end
UserCanceled = Class.new(ExecutionError)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment