rb-appscript 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +243 -0
- data/LICENSE +1 -0
- data/README +42 -0
- data/TODO +31 -0
- data/doc/aem-manual/01_introduction.html +48 -0
- data/doc/aem-manual/02_apioverview.html +89 -0
- data/doc/aem-manual/03_packingandunpackingdata.html +98 -0
- data/doc/aem-manual/04_references.html +401 -0
- data/doc/aem-manual/05_targettingapplications.html +133 -0
- data/doc/aem-manual/06_buildingandsendingevents.html +175 -0
- data/doc/aem-manual/07_findapp.html +54 -0
- data/doc/aem-manual/08_examples.html +85 -0
- data/doc/aem-manual/09_notes.html +41 -0
- data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
- data/doc/aem-manual/full.css +21 -0
- data/doc/aem-manual/index.html +43 -0
- data/doc/appscript-manual/01_introduction.html +82 -0
- data/doc/appscript-manual/02_aboutappscripting.html +244 -0
- data/doc/appscript-manual/03_quicktutorial.html +154 -0
- data/doc/appscript-manual/04_gettinghelp.html +101 -0
- data/doc/appscript-manual/05_keywordconversion.html +91 -0
- data/doc/appscript-manual/06_classesandenums.html +174 -0
- data/doc/appscript-manual/07_applicationobjects.html +181 -0
- data/doc/appscript-manual/08_realvsgenericreferences.html +86 -0
- data/doc/appscript-manual/09_referenceforms.html +232 -0
- data/doc/appscript-manual/10_referenceexamples.html +142 -0
- data/doc/appscript-manual/11_applicationcommands.html +204 -0
- data/doc/appscript-manual/12_commandexamples.html +129 -0
- data/doc/appscript-manual/13_performanceissues.html +115 -0
- data/doc/appscript-manual/14_problemapps.html +193 -0
- data/doc/appscript-manual/15_notes.html +84 -0
- data/doc/appscript-manual/application_architecture.gif +0 -0
- data/doc/appscript-manual/application_architecture2.gif +0 -0
- data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
- data/doc/appscript-manual/full.css +21 -0
- data/doc/appscript-manual/index.html +49 -0
- data/doc/appscript-manual/relationships_example.gif +0 -0
- data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
- data/doc/index.html +30 -0
- data/doc/mactypes-manual/index.html +216 -0
- data/doc/osax-manual/index.html +169 -0
- data/extconf.rb +54 -0
- data/misc/adobeunittypes.rb +14 -0
- data/misc/dump.rb +72 -0
- data/rb-appscript.gemspec +20 -0
- data/sample/AB_list_people_with_emails.rb +8 -0
- data/sample/Create_daily_iCal_todos.rb +72 -0
- data/sample/Hello_world.rb +9 -0
- data/sample/List_iTunes_playlist_names.rb +7 -0
- data/sample/Make_Mail_message.rb +29 -0
- data/sample/Open_file_in_TextEdit.rb +9 -0
- data/sample/Organize_Mail_messages.rb +57 -0
- data/sample/Print_folder_tree.rb +12 -0
- data/sample/Select_all_HTML_files.rb +8 -0
- data/sample/Set_iChat_status.rb +20 -0
- data/sample/Simple_Finder_GUI_Scripting.rb +14 -0
- data/sample/Stagger_Finder_windows.rb +21 -0
- data/sample/TextEdit_demo.rb +126 -0
- data/sample/iTunes_top40_to_html.rb +64 -0
- data/src/lib/_aem/aemreference.rb +1006 -0
- data/src/lib/_aem/codecs.rb +617 -0
- data/src/lib/_aem/connect.rb +100 -0
- data/src/lib/_aem/findapp.rb +83 -0
- data/src/lib/_aem/mactypes.rb +228 -0
- data/src/lib/_aem/send.rb +257 -0
- data/src/lib/_aem/typewrappers.rb +57 -0
- data/src/lib/_appscript/defaultterminology.rb +245 -0
- data/src/lib/_appscript/referencerenderer.rb +132 -0
- data/src/lib/_appscript/reservedkeywords.rb +107 -0
- data/src/lib/_appscript/terminology.rb +314 -0
- data/src/lib/aem.rb +216 -0
- data/src/lib/appscript.rb +830 -0
- data/src/lib/kae.rb +1484 -0
- data/src/lib/osax.rb +171 -0
- data/src/rbae.c +766 -0
- data/test/README +1 -0
- data/test/test_aemreference.rb +112 -0
- data/test/test_appscriptreference.rb +102 -0
- data/test/test_codecs.rb +159 -0
- data/test/test_findapp.rb +24 -0
- data/test/test_mactypes.rb +67 -0
- data/test/testall.sh +9 -0
- metadata +143 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
# Copyright (C) 2006 HAS.
|
3
|
+
# Released under MIT License.
|
4
|
+
|
5
|
+
module FindApp
|
6
|
+
# Support module for obtaining the full path to a local application given its name, bundle id or creator type. If application isn't found, an ApplicationNotFoundError exception is raised.
|
7
|
+
|
8
|
+
require "ae"
|
9
|
+
|
10
|
+
class ApplicationNotFoundError < RuntimeError
|
11
|
+
|
12
|
+
attr_reader :creator_type, :bundle_id, :application_name
|
13
|
+
|
14
|
+
def initialize(creator, id, name)
|
15
|
+
@creator_type, @bundle_id, @application_name = creator, id, name
|
16
|
+
super()
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#######
|
21
|
+
|
22
|
+
def FindApp._find_app(creator, id, name)
|
23
|
+
begin
|
24
|
+
return AE.find_application(creator, id, name)
|
25
|
+
rescue AE::MacOSError => err
|
26
|
+
if err.to_i == -10814
|
27
|
+
ident = [creator, id, name].compact.to_s.inspect
|
28
|
+
raise ApplicationNotFoundError.new(creator, id, name), "Application #{ident} not found."
|
29
|
+
else
|
30
|
+
raise
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#######
|
36
|
+
|
37
|
+
def FindApp.by_name(name)
|
38
|
+
# Find the application with the given name and return its full path.
|
39
|
+
#
|
40
|
+
# Absolute paths are also accepted. An '.app' suffix is optional.
|
41
|
+
#
|
42
|
+
# Examples:
|
43
|
+
# FindApp.by_name('TextEdit')
|
44
|
+
# FindApp.by_name('Finder.app')
|
45
|
+
#
|
46
|
+
if name[0, 1] != '/' # application name only, not its full path
|
47
|
+
begin
|
48
|
+
new_name = _find_app(nil, nil, name)
|
49
|
+
rescue ApplicationNotFoundError
|
50
|
+
if ('----' + name)[-4, 4].downcase == '.app'
|
51
|
+
raise ApplicationNotFoundError.new(nil, nil, name), "Application #{name.inspect} not found."
|
52
|
+
end
|
53
|
+
new_name = _find_app(nil, nil, name + '.app')
|
54
|
+
end
|
55
|
+
name = new_name
|
56
|
+
end
|
57
|
+
if not FileTest.exist?(name) and name[-4, 4].downcase != '.app' and not FileTest.exist?(name+ '.app')
|
58
|
+
name += '.app'
|
59
|
+
end
|
60
|
+
if not FileTest.exist?(name)
|
61
|
+
raise RuntimeError, name
|
62
|
+
end
|
63
|
+
return name
|
64
|
+
end
|
65
|
+
|
66
|
+
def FindApp.by_id(id)
|
67
|
+
# Find the application with the given bundle id and return its full path.
|
68
|
+
#
|
69
|
+
# Examples:
|
70
|
+
# FindApp.by_id('com.apple.textedit')
|
71
|
+
#
|
72
|
+
return _find_app(nil, id, nil)
|
73
|
+
end
|
74
|
+
|
75
|
+
def FindApp.by_creator(creator)
|
76
|
+
# Find the application with the given creator type and return its full path.
|
77
|
+
#
|
78
|
+
# Examples:
|
79
|
+
# FindApp.by_creator('ttxt')
|
80
|
+
#
|
81
|
+
return _find_app(creator, nil, nil)
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
# Copyright (C) 2006 HAS.
|
3
|
+
# Released under MIT License.
|
4
|
+
|
5
|
+
module MacTypes
|
6
|
+
# Defines wrapper classes for Mac OS datatypes that don't have a suitable Ruby equivalent.
|
7
|
+
#
|
8
|
+
# Note: all path strings are/must be valid UTF8.
|
9
|
+
|
10
|
+
require "ae"
|
11
|
+
require "kae"
|
12
|
+
|
13
|
+
class FileBase
|
14
|
+
|
15
|
+
URLPrefix = 'file://localhost'
|
16
|
+
|
17
|
+
def FileBase._path_to_url(path)
|
18
|
+
return URLPrefix + path.gsub(/[^a-zA-Z0-9_.-\/]/) { |c| "%%%02x" % c[0] }
|
19
|
+
end
|
20
|
+
|
21
|
+
def FileBase._url_to_path(url)
|
22
|
+
if url[0, URLPrefix.length] != URLPrefix
|
23
|
+
raise ArgumentError, "Not a file:// URL."
|
24
|
+
end
|
25
|
+
return url[URLPrefix.length, url.length].gsub(/%../) { |s| "%c" % s[1,2].hex }
|
26
|
+
end
|
27
|
+
|
28
|
+
def FileBase._coerce(desc, type, path=nil)
|
29
|
+
begin
|
30
|
+
return desc.coerce(type)
|
31
|
+
rescue AE::MacOSError => e
|
32
|
+
if [-35, -43, -120, -1700].include?(e.to_i) # disk/file/folder not found, or coercion error
|
33
|
+
if path != nil
|
34
|
+
raise FileNotFoundError, "File #{path.inspect} not found."
|
35
|
+
else
|
36
|
+
raise FileNotFoundError, "File not found."
|
37
|
+
end
|
38
|
+
else
|
39
|
+
raise
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def ==(val)
|
45
|
+
return (self.equal?(val) or (
|
46
|
+
self.class == val.class and
|
47
|
+
self.desc.type == val.desc.type and
|
48
|
+
self.desc.data == self.desc.data))
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :eql?, :==
|
52
|
+
|
53
|
+
def hash
|
54
|
+
return [desc.type, desc.data].hash
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# public
|
59
|
+
|
60
|
+
class Alias < FileBase
|
61
|
+
# Wraps AEDescs of typeAlias. Alias objects keep track of filesystem objects as they're moved around the disk or renamed.
|
62
|
+
#
|
63
|
+
# Since Ruby doesn't already bridge the Mac OS's Alias Manager, simplest solution is to always store data internally as an AEDesc of typeAlias, and convert this to other forms on demand (e.g. when casting to string).
|
64
|
+
|
65
|
+
attr_reader :desc
|
66
|
+
private_class_method :new
|
67
|
+
|
68
|
+
def initialize(desc)
|
69
|
+
@desc = desc
|
70
|
+
end
|
71
|
+
|
72
|
+
# Constructors
|
73
|
+
|
74
|
+
def Alias.path(path)
|
75
|
+
# Make Alias object from POSIX path.
|
76
|
+
return new(FileBase._coerce(
|
77
|
+
AE::AEDesc.new(KAE::TypeFileURL, FileBase._path_to_url(path)),
|
78
|
+
KAE::TypeAlias, path))
|
79
|
+
end
|
80
|
+
|
81
|
+
def Alias.desc(desc)
|
82
|
+
# Make Alias object from CarbonX.AE.AEDesc of typeAlias. Note: descriptor type is not checked; clients are responsible for passing the correct type as other types will cause unexpected problems/errors.
|
83
|
+
return new(desc)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Methods
|
87
|
+
|
88
|
+
def desc
|
89
|
+
# Return AEDesc of typeAlias. If clients want a different type, they can subsequently call this AEDesc's coerce method.
|
90
|
+
return @desc
|
91
|
+
end
|
92
|
+
|
93
|
+
def path
|
94
|
+
# Get as POSIX path.
|
95
|
+
return FileBase._url_to_path(FileBase._coerce(@desc, KAE::TypeFileURL).data)
|
96
|
+
end
|
97
|
+
|
98
|
+
alias_method :to_s, :path
|
99
|
+
|
100
|
+
def inspect
|
101
|
+
return "MacTypes::Alias.path(#{to_s.inspect})"
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_alias
|
105
|
+
# Get as MacTypes::Alias.
|
106
|
+
return self
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_file_url
|
110
|
+
# Get as MacTypes::FileURL; note that the resulting FileURL object will always pack as an AEDesc of typeFileURL.
|
111
|
+
return MacTypes::FileURL.desc(FileBase._coerce(@desc, KAE::TypeFileURL))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
|
117
|
+
class FileURL < FileBase
|
118
|
+
# Wraps AEDescs of typeFSRef/typeFSS/typeFileURL to save user from having to deal with them directly. FileURL objects refer to specific locations on the filesystem which may or may not already exist.
|
119
|
+
|
120
|
+
private_class_method :new
|
121
|
+
|
122
|
+
def initialize(path, desc)
|
123
|
+
@path = path
|
124
|
+
@desc = desc
|
125
|
+
end
|
126
|
+
|
127
|
+
# Constructors
|
128
|
+
|
129
|
+
def FileURL.path(path)
|
130
|
+
# Make FileURL object from POSIX path.
|
131
|
+
return new(path, nil)
|
132
|
+
end
|
133
|
+
|
134
|
+
def FileURL.desc(desc)
|
135
|
+
# Make FileURL object from AEDesc of typeFSS, typeFSRef, typeFileURL. Note: descriptor type is not checked; clients are responsible for passing the correct type as other types will cause unexpected problems/errors.
|
136
|
+
return new(nil, desc)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Methods
|
140
|
+
|
141
|
+
def desc
|
142
|
+
# Get as AEDesc. If constructed from Ruby, descriptor's type is always typeFileURL; if returned by aem, its type mat be typeFSS, typeFSRef or typeFileURL.
|
143
|
+
if not @desc
|
144
|
+
@desc = AE::AEDesc.new(KAE::TypeFileURL, FileBase._path_to_url(@path))
|
145
|
+
end
|
146
|
+
return @desc
|
147
|
+
end
|
148
|
+
|
149
|
+
def path
|
150
|
+
# Get as POSIX path.
|
151
|
+
if not @path
|
152
|
+
@path = FileBase._url_to_path(FileBase._coerce(@desc, KAE::TypeFileURL).data)
|
153
|
+
end
|
154
|
+
return @path
|
155
|
+
end
|
156
|
+
|
157
|
+
alias_method :to_s, :path
|
158
|
+
|
159
|
+
def inspect
|
160
|
+
return "MacTypes::FileURL.path(#{to_s.inspect})"
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_alias
|
164
|
+
# Get as MacTypes::Alias.
|
165
|
+
return MacTypes::Alias.desc(FileBase._coerce(desc, KAE::TypeAlias, to_s))
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_file_url
|
169
|
+
# Get as MacTypes::FileURL; note that the resulting FileURL object will always pack as an AEDesc of typeFileURL.
|
170
|
+
return MacTypes::FileURL.desc(FileBase._coerce(desc, KAE::TypeFileURL, to_s))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
|
176
|
+
class FileNotFoundError < RuntimeError
|
177
|
+
# Raised when an operation that only works for an existing filesystem object/location is performed on an Alias/FileURL object that identifies a non-existent object/location.
|
178
|
+
end
|
179
|
+
|
180
|
+
#######
|
181
|
+
|
182
|
+
class Units
|
183
|
+
# Represents a measurement; e.g. 3 inches, 98.5 degrees Fahrenheit.
|
184
|
+
#
|
185
|
+
# The AEM defines a standard set of unit types; some applications may define additional types for their own use. This wrapper stores the raw unit type and value data; aem/appscript Codecs objects will convert this to/from an AEDesc, or raise an error if the unit type is unrecognised.
|
186
|
+
|
187
|
+
attr_reader :value, :type
|
188
|
+
|
189
|
+
def Units.method_missing(name, value)
|
190
|
+
return new(value, name)
|
191
|
+
end
|
192
|
+
|
193
|
+
def initialize(value, type)
|
194
|
+
@value = value
|
195
|
+
@type = type
|
196
|
+
end
|
197
|
+
|
198
|
+
def ==(val)
|
199
|
+
return (self.equal?(val) or (
|
200
|
+
self.class == val.class and
|
201
|
+
@value == val.value and @type == val.type))
|
202
|
+
end
|
203
|
+
|
204
|
+
alias_method :eql?, :==
|
205
|
+
|
206
|
+
def hash
|
207
|
+
return [@value, @type].hash
|
208
|
+
end
|
209
|
+
|
210
|
+
def to_i
|
211
|
+
return @value.to_i
|
212
|
+
end
|
213
|
+
|
214
|
+
def to_f
|
215
|
+
return @value.to_f
|
216
|
+
end
|
217
|
+
|
218
|
+
def to_s
|
219
|
+
return "#{@value.inspect} #{@type.tr('_', ' ')}"
|
220
|
+
end
|
221
|
+
|
222
|
+
def inspect
|
223
|
+
return "MacTypes::Units.new(#{@value.inspect}, #{@type.inspect})"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
@@ -0,0 +1,257 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
# Copyright (C) 2006 HAS.
|
3
|
+
# Released under MIT License.
|
4
|
+
|
5
|
+
module Send
|
6
|
+
|
7
|
+
# Defines the Event class, which represents an Apple event that's packed and ready to send,
|
8
|
+
# and the CommandError class, which contains error information for a failed event.
|
9
|
+
|
10
|
+
require "ae"
|
11
|
+
require "kae"
|
12
|
+
require "_aem/codecs"
|
13
|
+
|
14
|
+
# Most applications don't provide error description strings, so aem defines default descriptions for the common ones.
|
15
|
+
|
16
|
+
# Following default error descriptions are cribbed from the AppleScript Language Guide/MacErrors.h:
|
17
|
+
|
18
|
+
MacOSErrorDescriptions = {
|
19
|
+
# OS errors
|
20
|
+
-34 => "Disk is full.",
|
21
|
+
-35 => "Disk wasn't found.",
|
22
|
+
-37 => "Bad name for file.",
|
23
|
+
-38 => "File wasn't open.",
|
24
|
+
-39 => "End of file error.",
|
25
|
+
-42 => "Too many files open.",
|
26
|
+
-43 => "File wasn't found.",
|
27
|
+
-44 => "Disk is write protected.",
|
28
|
+
-45 => "File is locked.",
|
29
|
+
-46 => "Disk is locked.",
|
30
|
+
-47 => "File is busy.",
|
31
|
+
-48 => "Duplicate file name.",
|
32
|
+
-49 => "File is already open.",
|
33
|
+
-50 => "Parameter error.",
|
34
|
+
-51 => "File reference number error.",
|
35
|
+
-61 => "File not open with write permission.",
|
36
|
+
-108 => "Out of memory.",
|
37
|
+
-120 => "Folder wasn't found.",
|
38
|
+
-124 => "Disk is disconnected.",
|
39
|
+
-128 => "User canceled.",
|
40
|
+
-192 => "A resource wasn't found.",
|
41
|
+
-600 => "Application isn't running.",
|
42
|
+
-601 => "Not enough room to launch application with special requirements.",
|
43
|
+
-602 => "Application is not 32-bit clean.",
|
44
|
+
-605 => "More memory is needed than is specified in the size resource.",
|
45
|
+
-606 => "Application is background-only.",
|
46
|
+
-607 => "Buffer is too small.",
|
47
|
+
-608 => "No outstanding high-level event.",
|
48
|
+
-609 => "Connection is invalid.",
|
49
|
+
-904 => "Not enough system memory to connect to remote application.",
|
50
|
+
-905 => "Remote access is not allowed.",
|
51
|
+
-906 => "Application isn't running or program linking isn't enabled.",
|
52
|
+
-915 => "Can't find remote machine.",
|
53
|
+
-30720 => "Invalid date and time.",
|
54
|
+
# AE errors
|
55
|
+
-1700 => "Can't make some data into the expected type.",
|
56
|
+
-1701 => "Some parameter is missing for command.",
|
57
|
+
-1702 => "Some data could not be read.",
|
58
|
+
-1703 => "Some data was the wrong type.",
|
59
|
+
-1704 => "Some parameter was invalid.",
|
60
|
+
-1705 => "Operation involving a list item failed.",
|
61
|
+
-1706 => "Need a newer version of the Apple Event Manager.",
|
62
|
+
-1707 => "Event isn't an Apple event.",
|
63
|
+
-1708 => "Application could not handle this command.",
|
64
|
+
-1709 => "AEResetTimer was passed an invalid reply.",
|
65
|
+
-1710 => "Invalid sending mode was passed.",
|
66
|
+
-1711 => "User canceled out of wait loop for reply or receipt.",
|
67
|
+
-1712 => "Apple event timed out.",
|
68
|
+
-1713 => "No user interaction allowed.",
|
69
|
+
-1714 => "Wrong keyword for a special function.",
|
70
|
+
-1715 => "Some parameter wasn't understood.",
|
71
|
+
-1716 => "Unknown Apple event address type.",
|
72
|
+
-1717 => "The handler is not defined.",
|
73
|
+
-1718 => "Reply has not yet arrived.",
|
74
|
+
-1719 => "Can't get reference. Invalid index.",
|
75
|
+
-1720 => "Invalid range.",
|
76
|
+
-1721 => "Wrong number of parameters for command.",
|
77
|
+
-1723 => "Can't get reference. Access not allowed.",
|
78
|
+
-1725 => "Illegal logical operator called.",
|
79
|
+
-1726 => "Illegal comparison or logical.",
|
80
|
+
-1727 => "Expected a reference.",
|
81
|
+
-1728 => "Can't get reference.",
|
82
|
+
-1729 => "Object counting procedure returned a negative count.",
|
83
|
+
-1730 => "Container specified was an empty list.",
|
84
|
+
-1731 => "Unknown object type.",
|
85
|
+
-1739 => "Attempting to perform an invalid operation on a null descriptor.",
|
86
|
+
# Application scripting errors
|
87
|
+
-10000 => "Apple event handler failed.",
|
88
|
+
-10001 => "Type error.",
|
89
|
+
-10002 => "Invalid key form.",
|
90
|
+
-10003 => "Can't set reference to given value. Access not allowed.",
|
91
|
+
-10004 => "A privilege violation occurred.",
|
92
|
+
-10005 => "The read operation wasn't allowed.",
|
93
|
+
-10006 => "Can't set reference to given value.",
|
94
|
+
-10007 => "The index of the event is too large to be valid.",
|
95
|
+
-10008 => "The specified object is a property, not an element.",
|
96
|
+
-10009 => "Can't supply the requested descriptor type for the data.",
|
97
|
+
-10010 => "The Apple event handler can't handle objects of this class.",
|
98
|
+
-10011 => "Couldn't handle this command because it wasn't part of the current transaction.",
|
99
|
+
-10012 => "The transaction to which this command belonged isn't a valid transaction.",
|
100
|
+
-10013 => "There is no user selection.",
|
101
|
+
-10014 => "Handler only handles single objects.",
|
102
|
+
-10015 => "Can't undo the previous Apple event or user action.",
|
103
|
+
-10023 => "Enumerated value is not allowed for this property.",
|
104
|
+
-10024 => "Class can't be an element of container.",
|
105
|
+
-10025 => "Illegal combination of properties settings.",
|
106
|
+
}
|
107
|
+
|
108
|
+
# Following Cocoa Scripting error descriptions taken from:
|
109
|
+
# http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSScriptCommand.html
|
110
|
+
# http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSScriptObjectSpecifier.html
|
111
|
+
|
112
|
+
CocoaErrorDescriptions = [
|
113
|
+
["NSReceiverEvaluationScriptError", "The object or objects specified by the direct parameter to a command could not be found."],
|
114
|
+
["NSKeySpecifierEvaluationScriptError", "The object or objects specified by a key (for commands that support key specifiers) could not be found."],
|
115
|
+
["NSArgumentEvaluationScriptError", "The object specified by an argument could not be found."],
|
116
|
+
["NSReceiversCantHandleCommandScriptError", "The receivers don't support the command sent to them."],
|
117
|
+
["NSRequiredArgumentsMissingScriptError", "An argument (or more than one argument) is missing."],
|
118
|
+
["NSArgumentsWrongScriptError", "An argument (or more than one argument) is of the wrong type or is otherwise invalid."],
|
119
|
+
["NSUnknownKeyScriptError", "An unidentified error occurred; indicates an error in the scripting support of the application."],
|
120
|
+
["NSInternalScriptError", "An unidentified internal error occurred; indicates an error in the scripting support of the application."],
|
121
|
+
["NSOperationNotSupportedForKeyScriptError", "The implementation of a scripting command signaled an error."],
|
122
|
+
["NSCannotCreateScriptCommandError", "Could not create the script command; an invalid or unrecognized Apple event was received."],
|
123
|
+
["NSNoSpecifierError", "No error encountered."],
|
124
|
+
["NSNoTopLevelContainersSpecifierError", "Someone called evaluate with nil."],
|
125
|
+
["NSContainerSpecifierError", "Error evaluating container specifier."],
|
126
|
+
["NSUnknownKeySpecifierError", "Receivers do not understand the key."],
|
127
|
+
["NSInvalidIndexSpecifierError", "Index out of bounds."],
|
128
|
+
["NSInternalSpecifierError", "Other internal error."],
|
129
|
+
["NSOperationNotSupportedForKeySpecifierError", "Attempt made to perform an unsupported operation on some key."]
|
130
|
+
]
|
131
|
+
|
132
|
+
class Event
|
133
|
+
# Represents an Apple event.
|
134
|
+
|
135
|
+
# Clients don't instantiate this class directly; instead, new instances are returned by AEM::Application#event.
|
136
|
+
|
137
|
+
attr_reader :AEM_event
|
138
|
+
|
139
|
+
def initialize(address, event_code, params={}, atts={}, transaction=KAE::KAnyTransactionID,
|
140
|
+
return_id= KAE::KAutoGenerateReturnID, codecs=DefaultCodecs)
|
141
|
+
# Create and pack a new Apple event ready for sending.
|
142
|
+
# address : AEAddressDesc -- the target application, identified by PSN, URL, etc.
|
143
|
+
# event_code : string -- 8-letter code indicating event's class and id, e.g. 'coregetd'
|
144
|
+
# params : hash -- a hash of form {AE_code=>anything,...} containing zero or more event parameters (message arguments)
|
145
|
+
# atts : hash -- a hash of form {AE_code=>anything,...} containing zero or more event attributes (event info)
|
146
|
+
# transaction : integer -- transaction number; AEM::Application takes care of this value
|
147
|
+
# return_id : integer -- reply event's ID
|
148
|
+
# codecs : Codecs -- clients can provide custom Codecs object for packing parameters and unpacking result of this event
|
149
|
+
@_event_code = event_code
|
150
|
+
@_codecs = codecs
|
151
|
+
@AEM_event = _create_apple_event(event_code[0, 4], event_code[-4, 4], address, return_id, transaction)
|
152
|
+
atts.each {|key, value| @AEM_event.put_attr(key, codecs.pack(value))}
|
153
|
+
params.each {|key, value| @AEM_event.put_param(key, codecs.pack(value))}
|
154
|
+
end
|
155
|
+
|
156
|
+
def _create_apple_event(event_class, event_id, target, return_id, transaction_id)
|
157
|
+
# Hook method; may be overridden to customise how AppleEvent descriptors are created.
|
158
|
+
return AE::AEDesc.new_apple_event(event_class, event_id, target, return_id, transaction_id)
|
159
|
+
end
|
160
|
+
|
161
|
+
def _send_apple_event(flags, timeout)
|
162
|
+
#�Hook method; may be overridden to customise how events are sent.
|
163
|
+
return @AEM_event.send(flags, timeout)
|
164
|
+
end
|
165
|
+
|
166
|
+
def inspect
|
167
|
+
return "#<AEM::Event @code=#{@_event_code}>"
|
168
|
+
end
|
169
|
+
|
170
|
+
alias_method :to_s, :inspect
|
171
|
+
|
172
|
+
def send(timeout=KAE::KAEDefaultTimeout, flags=KAE::KAECanSwitchLayer + KAE::KAEWaitReply)
|
173
|
+
# Send this Apple event (may be called any number of times).
|
174
|
+
# timeout : int | KAEDefaultTimeout | KNoTimeOut -- number of ticks to wait for target process to reply before raising timeout error
|
175
|
+
# flags : integer -- bitwise flags [1] indicating how target process should handle event
|
176
|
+
# Result : anything -- value returned by application, if any
|
177
|
+
#
|
178
|
+
# [1] Should be the sum of zero or more of the following kae module constants:
|
179
|
+
#
|
180
|
+
# KAENoReply | KAEQueueReply | KAEWaitReply
|
181
|
+
# KAEDontReconnect
|
182
|
+
# KAEWantReceipt
|
183
|
+
# KAENeverInteract | KAECanInteract | KAEAlwaysInteract
|
184
|
+
# KAECanSwitchLayer
|
185
|
+
|
186
|
+
begin
|
187
|
+
reply_event = _send_apple_event(flags, timeout)
|
188
|
+
rescue AE::MacOSError => err # The Apple Event Manager raised an error.
|
189
|
+
if not (@_event_code == 'aevtquit' and err.to_i == -609) # Ignore invalid connection errors (-609) when quitting
|
190
|
+
raise CommandError.new(err.to_i, nil, err)
|
191
|
+
end
|
192
|
+
else # Decode application's reply, if any. May be a return value, error number (and optional message), or nothing.
|
193
|
+
if reply_event.type != KAE::TypeNull
|
194
|
+
event_result = {}
|
195
|
+
reply_event.length.times do |i|
|
196
|
+
key, value = reply_event.get(i + 1, KAE::TypeWildCard)
|
197
|
+
event_result[key] = value
|
198
|
+
end
|
199
|
+
if event_result.has_key?(KAE::KeyErrorNumber) # The application raised an error.
|
200
|
+
# Error info is unpacked using default codecs for reliability.
|
201
|
+
e_num = DefaultCodecs.unpack(event_result[KAE::KeyErrorNumber])
|
202
|
+
if e_num != 0 # Note that some apps (e.g. Finder) may return error code 0 to indicate a successful operation, so ignore this.
|
203
|
+
e_msg = event_result[KAE::KeyErrorString]
|
204
|
+
if e_msg
|
205
|
+
e_msg = DefaultCodecs.unpack(e_msg)
|
206
|
+
end
|
207
|
+
raise CommandError.new(e_num, e_msg, reply_event)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
if event_result.has_key?(KAE::KeyAEResult)
|
211
|
+
# Return values are unpacked using [optionally] client-supplied codecs.
|
212
|
+
# This allows aem clients such as appscript to customise how values are unpacked
|
213
|
+
# (e.g. to unpack object specifier descs as appscript references instead of aem references).
|
214
|
+
return @_codecs.unpack(event_result[KAE::KeyAEResult])
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
class CommandError < RuntimeError
|
223
|
+
# Represents an error raised by the Apple Event Manager or target application when a command fails.
|
224
|
+
#
|
225
|
+
# Methods:
|
226
|
+
# number : integer -- MacOS error number
|
227
|
+
# message : string | nil -- application error message if any, or default error description if available, or nil
|
228
|
+
|
229
|
+
attr_reader :number, :message, :raw # raw method is provided for testing/debugging use only
|
230
|
+
alias_method :to_i, :number
|
231
|
+
|
232
|
+
def initialize(number, message, raw)
|
233
|
+
if message == nil
|
234
|
+
message = MacOSErrorDescriptions[number]
|
235
|
+
end
|
236
|
+
if number > 0
|
237
|
+
CocoaErrorDescriptions.each do |name, description|
|
238
|
+
if message[0, name.length] == name
|
239
|
+
message += " (#{description})"
|
240
|
+
break
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
@number = number
|
245
|
+
@message = message
|
246
|
+
@raw = raw
|
247
|
+
end
|
248
|
+
|
249
|
+
def to_s
|
250
|
+
if message
|
251
|
+
return "CommandError\n\t\tOSERROR: #{number}\n\t\tMESSAGE: #{message}"
|
252
|
+
else
|
253
|
+
return "CommandError\n\t\tOSERROR: #{number}"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
# Copyright (C) 2006 HAS.
|
3
|
+
# Released under MIT License.
|
4
|
+
|
5
|
+
module TypeWrappers
|
6
|
+
|
7
|
+
class AETypeBase
|
8
|
+
|
9
|
+
attr_reader :code
|
10
|
+
|
11
|
+
def initialize(code)
|
12
|
+
if not (code.is_a?(String) and code.length == 4)
|
13
|
+
raise ArgumentError, "Code must be a four-character string: #{code}"
|
14
|
+
end
|
15
|
+
@code = code
|
16
|
+
end
|
17
|
+
|
18
|
+
def hash
|
19
|
+
return @code.hash
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==(val)
|
23
|
+
return (self.equal?(val) or (val.class == self.class and val.code == @code))
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :eql?, :==
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
return "AEM::#{self.class::Name}.new(#{@code.dump})"
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
return to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class AEType < AETypeBase
|
39
|
+
Name = 'AEType'
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
class AEEnum < AETypeBase
|
44
|
+
Name = 'AEEnum'
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
class AEProp < AETypeBase
|
49
|
+
Name = 'AEProp'
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
class AEKey < AETypeBase
|
54
|
+
Name = 'AEKey'
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|