Class: Mailmate::AppleScriptDriver

Inherits:
Object
  • Object
show all
Defined in:
lib/mailmate/applescript_driver.rb

Overview

Build and run AppleScript calls against MailMate. macOS-only.

A class (not a module like the stateless utilities ‘HeaderReader`, `MidUrl`, `Identity`, etc.) because each driver carries per-invocation state — `dry_run`, `output`, `errput`. The rule throughout the gem: stateless surface → module with `extend self`; state-bearing → class.

Defined Under Namespace

Classes: Error

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dry_run: false, output: $stdout, errput: $stderr) ⇒ AppleScriptDriver

Returns a new instance of AppleScriptDriver.



19
20
21
22
23
# File 'lib/mailmate/applescript_driver.rb', line 19

def initialize(dry_run: false, output: $stdout, errput: $stderr)
  @dry_run = dry_run
  @output  = output
  @errput  = errput
end

Instance Attribute Details

#dry_runObject (readonly)



17
18
19
# File 'lib/mailmate/applescript_driver.rb', line 17

def dry_run
  @dry_run
end

Instance Method Details

#applescript_escape(s, allow_controls: false) ⇒ Object

Escape a Ruby string for inclusion inside an AppleScript double-quoted string literal. Order matters: backslash first, then quote. Newlines/ tabs are rejected unless ‘allow_controls: true`.



83
84
85
86
87
88
# File 'lib/mailmate/applescript_driver.rb', line 83

def applescript_escape(s, allow_controls: false)
  if !allow_controls && s.match?(/[\r\n\t]/)
    raise Error, "AppleScript arg contains control character (\\r/\\n/\\t): #{s.inspect}"
  end
  s.gsub("\\", "\\\\\\\\").gsub('"', '\\"')
end

#build_perform_script(selector, args) ⇒ Object

Internal — build the ‘tell application “MailMate” to perform { … }` script with proper quoting. AppleScript string literals require:

\ → \\   (single backslash becomes \\, otherwise it interprets
          \b / \n / \t / etc. as control-character escapes)
" → \"   (terminates the string otherwise)

Newlines and tabs in args are rejected — they have no defensible meaning inside an AppleScript single-line script and almost certainly indicate data the caller doesn’t want injected.



70
71
72
73
74
75
76
77
78
# File 'lib/mailmate/applescript_driver.rb', line 70

def build_perform_script(selector, args)
  escaped_selector = applescript_escape(selector.to_s, allow_controls: false)
  if args.empty?
    %(tell application "MailMate" to perform {"#{escaped_selector}"})
  else
    list = args.map { |a| %("#{applescript_escape(a.to_s)}") }.join(", ")
    %(tell application "MailMate" to perform {"#{escaped_selector}", #{list}})
  end
end

#close_windows(ids) ⇒ Object

Close every window in ‘ids`. No-op for IDs that no longer exist.



55
56
57
58
59
60
# File 'lib/mailmate/applescript_driver.rb', line 55

def close_windows(ids)
  return if dry_run
  Array(ids).each do |id|
    `osascript -e 'tell application "MailMate" to close window id #{id}' >/dev/null 2>&1`
  end
end

#open_url(url) ⇒ Object

Open a URL in MailMate (used for ‘mid:` URLs to select a message).

Raises:



35
36
37
38
39
40
41
42
43
# File 'lib/mailmate/applescript_driver.rb', line 35

def open_url(url)
  Mailmate::PlatformError.check_darwin!(component: "AppleScriptDriver") unless dry_run
  if dry_run
    @output.puts "DRY: open -a MailMate #{url.inspect}"
    return
  end
  success = system("open", "-a", "MailMate", url)
  raise Error, "open command failed for #{url}" unless success
end

#perform(selector, *args) ⇒ Object

Drive a MailMate selector against the current selection. ‘selector` is the key-binding selector name (`“markAsRead:”`, `“setTag:”`, etc.). `args` are positional arguments passed alongside.



28
29
30
31
32
# File 'lib/mailmate/applescript_driver.rb', line 28

def perform(selector, *args)
  Mailmate::PlatformError.check_darwin!(component: "AppleScriptDriver") unless dry_run
  script = build_perform_script(selector, args)
  run_apple_script(script)
end

#window_idsObject

Return the array of MailMate’s current window IDs (integers). Empty array if MailMate isn’t running or ‘osascript` errors.



47
48
49
50
51
52
# File 'lib/mailmate/applescript_driver.rb', line 47

def window_ids
  return [] if dry_run
  out = `osascript -e 'tell application "MailMate" to get id of every window' 2>&1`
  return [] unless $?.success?
  out.strip.split(",").map { |s| s.strip.to_i }
end