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,830 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
# Copyright (C) 2006 HAS.
|
3
|
+
# Released under MIT License.
|
4
|
+
|
5
|
+
require "_aem/mactypes"
|
6
|
+
|
7
|
+
module AS
|
8
|
+
# The following methods and classes are of interest to end users:
|
9
|
+
# app, con, its, CommandError, ApplicationNotFoundError
|
10
|
+
# Other classes are only of interest to implementors who need to hook in their own code.
|
11
|
+
|
12
|
+
require "kae"
|
13
|
+
require "aem"
|
14
|
+
require "_appscript/referencerenderer"
|
15
|
+
require "_appscript/terminology"
|
16
|
+
|
17
|
+
######################################################################
|
18
|
+
# APPDATA
|
19
|
+
######################################################################
|
20
|
+
|
21
|
+
module AppDataAccessors
|
22
|
+
attr_reader :target, :type_by_name, :type_by_code, :reference_by_name, :reference_by_code
|
23
|
+
end
|
24
|
+
|
25
|
+
class AppData < AEM::Codecs
|
26
|
+
|
27
|
+
attr_reader :path, :pid, :url
|
28
|
+
|
29
|
+
def initialize(aem_application_class, path, pid, url, terms)
|
30
|
+
super()
|
31
|
+
@_aem_application_class = aem_application_class
|
32
|
+
@_terms = terms
|
33
|
+
@path = path
|
34
|
+
@pid = pid
|
35
|
+
@url = url
|
36
|
+
end
|
37
|
+
|
38
|
+
def _connect # initialize AEM::Application instance and terminology tables the first time they are needed
|
39
|
+
if @path
|
40
|
+
@target = @_aem_application_class.by_path(@path)
|
41
|
+
elsif @pid
|
42
|
+
@target = @_aem_application_class.by_pid(@pid)
|
43
|
+
elsif @url
|
44
|
+
@target = @_aem_application_class.by_url(@url)
|
45
|
+
else
|
46
|
+
@target = @_aem_application_class.current
|
47
|
+
end
|
48
|
+
case @_terms
|
49
|
+
when true # obtain terminology from application
|
50
|
+
@type_by_code, @type_by_name, @reference_by_code, @reference_by_name = Terminology.tables_for_app(@path, @pid, @url)
|
51
|
+
when false # use built-in terminology only (e.g. use this when running AppleScript applets)
|
52
|
+
@type_by_code, @type_by_name, @reference_by_code, @reference_by_name = Terminology.default_tables
|
53
|
+
when nil # [developer-only] make Application#methods return names of built-in methods only (needed to generate reservedkeywords.rb file)
|
54
|
+
@type_by_code, @type_by_name, @reference_by_code, @reference_by_name = {}, {}, {}, {}
|
55
|
+
else # @_terms is [assumed to be] a module containing dumped terminology, so use that
|
56
|
+
@type_by_code, @type_by_name, @reference_by_code, @reference_by_name = Terminology.tables_for_module(@_terms)
|
57
|
+
end
|
58
|
+
extend(AppDataAccessors)
|
59
|
+
end
|
60
|
+
|
61
|
+
#######
|
62
|
+
|
63
|
+
def target
|
64
|
+
_connect
|
65
|
+
return @target
|
66
|
+
end
|
67
|
+
|
68
|
+
def type_by_name
|
69
|
+
_connect
|
70
|
+
return @type_by_name
|
71
|
+
end
|
72
|
+
|
73
|
+
def type_by_code
|
74
|
+
_connect
|
75
|
+
return @type_by_code
|
76
|
+
end
|
77
|
+
|
78
|
+
def reference_by_name
|
79
|
+
_connect
|
80
|
+
return @reference_by_name
|
81
|
+
end
|
82
|
+
|
83
|
+
def reference_by_code
|
84
|
+
_connect
|
85
|
+
return @reference_by_code
|
86
|
+
end
|
87
|
+
|
88
|
+
#######
|
89
|
+
|
90
|
+
def pack(data)
|
91
|
+
if data.is_a?(GenericReference)
|
92
|
+
data = data.AS_resolve(self)
|
93
|
+
end
|
94
|
+
if data.is_a?(Reference)
|
95
|
+
data = data.AS_aem_reference
|
96
|
+
elsif data.is_a?(Symbol)
|
97
|
+
data = self.type_by_name.fetch(data) { raise IndexError, "Unknown Keyword: #{data.inspect}" }
|
98
|
+
end
|
99
|
+
return super(data)
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
|
104
|
+
ClassType = AEM::AEType.new('pcls')
|
105
|
+
|
106
|
+
def pack_hash(val)
|
107
|
+
record = AE::AEDesc.new_list(true)
|
108
|
+
if val.has_key?(:class_) or val.has_key?(ClassType)
|
109
|
+
# if hash contains a 'class' property containing a class name, coerce the AEDesc to that class
|
110
|
+
new_val = Hash[val]
|
111
|
+
if new_val.has_key?(:class_)
|
112
|
+
value = new_val.delete(:class_)
|
113
|
+
else
|
114
|
+
value = new_val.delete(ClassType)
|
115
|
+
end
|
116
|
+
if value.is_a?(Symbol) # get the corresponding AEType (assuming there is one)
|
117
|
+
value = @type_by_name.fetch(value, value)
|
118
|
+
end
|
119
|
+
if value.is_a?(AEM::AEType) # coerce the record to the desired type
|
120
|
+
record = record.coerce(value.code)
|
121
|
+
val = new_val
|
122
|
+
end # else value wasn't a class name, so it'll be packed as a normal record property instead
|
123
|
+
end
|
124
|
+
usrf = nil
|
125
|
+
val.each do | key, value |
|
126
|
+
if key.is_a?(Symbol)
|
127
|
+
key_type = @type_by_name.fetch(key) { raise IndexError, "Unknown keyword: #{key.inspect}" }
|
128
|
+
record.put_param(key_type.code, pack(value))
|
129
|
+
elsif key.is_a?(AEM::AETypeBase)
|
130
|
+
record.put_param(key.code, pack(value))
|
131
|
+
else
|
132
|
+
if usrf == nil
|
133
|
+
usrf = AE::AEDesc.new_list(false)
|
134
|
+
end
|
135
|
+
usrf.put_item(0, pack(key))
|
136
|
+
usrf.put_item(0, pack(value))
|
137
|
+
end
|
138
|
+
end
|
139
|
+
if usrf
|
140
|
+
record.put_param('usrf', usrf)
|
141
|
+
end
|
142
|
+
return record
|
143
|
+
end
|
144
|
+
|
145
|
+
def unpack_type(desc)
|
146
|
+
aem_value = super(desc)
|
147
|
+
return @type_by_code.fetch(aem_value.code, aem_value)
|
148
|
+
end
|
149
|
+
|
150
|
+
def unpack_enumerated(desc)
|
151
|
+
aem_value = super(desc)
|
152
|
+
return @type_by_code.fetch(aem_value.code, aem_value)
|
153
|
+
end
|
154
|
+
|
155
|
+
def unpack_property(desc)
|
156
|
+
aem_value = super(desc)
|
157
|
+
return @type_by_code.fetch(aem_value.code, aem_value)
|
158
|
+
end
|
159
|
+
|
160
|
+
def unpack_aerecord(desc)
|
161
|
+
dct = {}
|
162
|
+
desc.length().times do |i|
|
163
|
+
key, value = desc.get(i + 1, KAE::TypeWildCard)
|
164
|
+
if key == 'usrf'
|
165
|
+
lst = unpack_aelist(value)
|
166
|
+
(lst.length / 2).times do |i|
|
167
|
+
dct[lst[i * 2]] = lst[i * 2 + 1]
|
168
|
+
end
|
169
|
+
else
|
170
|
+
dct[@type_by_code.fetch(key) { AEM::AEType.new(key) }] = unpack(value)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
return dct
|
174
|
+
end
|
175
|
+
|
176
|
+
def unpack_object_specifier(desc)
|
177
|
+
return Reference.new(self, DefaultCodecs.unpack_object_specifier(desc))
|
178
|
+
end
|
179
|
+
|
180
|
+
def unpack_insertion_loc(desc)
|
181
|
+
return Reference.new(self, DefaultCodecs.unpack_insertion_loc(desc))
|
182
|
+
end
|
183
|
+
|
184
|
+
def unpack_contains_comp_descriptor(op1, op2)
|
185
|
+
if op1.is_a?(AS::Reference) and op1.AS_aem_reference.AEM_root == AEMReference::Its
|
186
|
+
return op1.contains(op2)
|
187
|
+
else
|
188
|
+
return super
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
######################################################################
|
195
|
+
# GENERIC REFERENCE
|
196
|
+
######################################################################
|
197
|
+
|
198
|
+
class GenericReference
|
199
|
+
|
200
|
+
attr_reader :_call
|
201
|
+
protected :_call
|
202
|
+
|
203
|
+
def initialize(call)
|
204
|
+
@_call = call
|
205
|
+
end
|
206
|
+
|
207
|
+
def ==(v)
|
208
|
+
return (self.class == v.class and @_call == v._call)
|
209
|
+
end
|
210
|
+
|
211
|
+
alias_method :eql?, :==
|
212
|
+
|
213
|
+
def hash
|
214
|
+
return @_call.hash
|
215
|
+
end
|
216
|
+
|
217
|
+
def method_missing(name, *args)
|
218
|
+
return AS::GenericReference.new(@_call + [[name, args]])
|
219
|
+
end
|
220
|
+
|
221
|
+
def to_s
|
222
|
+
s= 'AS.' + @_call[0]
|
223
|
+
@_call[1, @_call.length].each do |name, args| if name == :[]
|
224
|
+
if args.length == 1
|
225
|
+
s += "[#{args[0]}]"
|
226
|
+
else
|
227
|
+
s += "[#{args[0]}, #{args[1]}]"
|
228
|
+
end
|
229
|
+
else
|
230
|
+
if args.length > 0
|
231
|
+
s += ".#{name.to_s}(#{(args.map { |arg| arg.inspect }).join(', ')})"
|
232
|
+
else
|
233
|
+
s += ".#{name.to_s}"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
return s
|
238
|
+
end
|
239
|
+
|
240
|
+
def inspect
|
241
|
+
return to_s
|
242
|
+
end
|
243
|
+
|
244
|
+
def AS_resolve(app_data)
|
245
|
+
ref = Reference.new(app_data, {'app' => AEM.app, 'con' => AEM.con, 'its' => AEM.its}[@_call[0]])
|
246
|
+
@_call[1, @_call.length].each do |name, args|
|
247
|
+
ref = ref.send(name, *args)
|
248
|
+
end
|
249
|
+
return ref
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
######################################################################
|
255
|
+
# REFERENCE
|
256
|
+
######################################################################
|
257
|
+
|
258
|
+
class Reference
|
259
|
+
|
260
|
+
# users may occasionally require access to the following for creating workarounds to problem apps
|
261
|
+
# note: calling #AS_app_data on a newly created application object will return an AppData instance
|
262
|
+
# that is not yet fully initialised, so remember to call its #_connect method before use
|
263
|
+
attr_reader :AS_aem_reference, :AS_app_data
|
264
|
+
attr_writer :AS_aem_reference, :AS_app_data
|
265
|
+
|
266
|
+
def initialize(app_data, aem_reference)
|
267
|
+
@AS_app_data = app_data
|
268
|
+
@AS_aem_reference = aem_reference
|
269
|
+
end
|
270
|
+
|
271
|
+
def _resolve_range_boundary(selector, value_if_none)
|
272
|
+
if selector == nil
|
273
|
+
selector = value_if_none
|
274
|
+
end
|
275
|
+
if selector.is_a?(AS::GenericReference)
|
276
|
+
return selector.AS_resolve(@AS_app_data).AS_aem_reference
|
277
|
+
elsif selector.is_a?(Reference)
|
278
|
+
return selector.AS_aem_reference
|
279
|
+
elsif selector.is_a?(String)
|
280
|
+
return AEM.con.elements(@AS_aem_reference.AEM_want).by_name(selector)
|
281
|
+
else
|
282
|
+
return AEM.con.elements(@AS_aem_reference.AEM_want).by_index(selector)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
#######
|
287
|
+
|
288
|
+
def Reference._pack_uint32(n) # used to pack csig attributes
|
289
|
+
return AE::AEDesc.new(KAE::TypeUInt32, [n].pack('L'))
|
290
|
+
end
|
291
|
+
|
292
|
+
# 'csig' attribute flags (see ASRegistry.h; note: there's no option for 'numeric strings' in 10.4)
|
293
|
+
|
294
|
+
IgnoreEnums = [
|
295
|
+
[:case, KAE::KAECaseConsiderMask, KAE::KAECaseIgnoreMask],
|
296
|
+
[:diacriticals, KAE::KAEDiacriticConsiderMask, KAE::KAEDiacriticIgnoreMask],
|
297
|
+
[:whitespace, KAE::KAEWhiteSpaceConsiderMask, KAE::KAEWhiteSpaceIgnoreMask],
|
298
|
+
[:hyphens, KAE::KAEHyphensConsiderMask, KAE::KAEHyphensIgnoreMask],
|
299
|
+
[:expansion, KAE::KAEExpansionConsiderMask, KAE::KAEExpansionIgnoreMask],
|
300
|
+
[:punctuation, KAE::KAEPunctuationConsiderMask, KAE::KAEPunctuationIgnoreMask],
|
301
|
+
]
|
302
|
+
|
303
|
+
# default cons, csig attributes
|
304
|
+
|
305
|
+
DefaultConsiderations = AEM::DefaultCodecs.pack([AEM::AEType.new('case')])
|
306
|
+
DefaultConsidersAndIgnores = _pack_uint32(KAE::KAECaseIgnoreMask)
|
307
|
+
|
308
|
+
##
|
309
|
+
|
310
|
+
def _send_command(args, name, code, labelled_arg_terms)
|
311
|
+
# puts "Calling command #{name} \n\twith args #{args.inspect},\n\treference #{self}\n\tinfo #{code.inspect}, #{labelled_arg_terms.inspect}\n\n"
|
312
|
+
# begin # TO DO: enable error handling block once debugging is completed
|
313
|
+
atts = {'subj' => nil}
|
314
|
+
params = {}
|
315
|
+
case args.length
|
316
|
+
when 0
|
317
|
+
keyword_args = {}
|
318
|
+
when 1 # note: if a command takes a hash as its direct parameter, user must pass {} as a second arg otherwise hash will be assumed to be keyword parameters
|
319
|
+
if args[0].is_a?(Hash)
|
320
|
+
keyword_args = args[0]
|
321
|
+
else
|
322
|
+
params['----'] = args[0]
|
323
|
+
keyword_args = {}
|
324
|
+
end
|
325
|
+
when 2
|
326
|
+
params['----'], keyword_args = args
|
327
|
+
else
|
328
|
+
raise ArgumentError, "Too many direct parameters."
|
329
|
+
end
|
330
|
+
if not keyword_args.is_a?(Hash)
|
331
|
+
raise ArgumentError, "Second argument must be a Hash containing zero or more keyword parameters."
|
332
|
+
end
|
333
|
+
# get user-specified timeout, if any
|
334
|
+
timeout = (keyword_args.delete(:timeout) {60}).to_i
|
335
|
+
if timeout <= 0
|
336
|
+
timeout = KAE::KNoTimeOut
|
337
|
+
else
|
338
|
+
timeout *= 60
|
339
|
+
end
|
340
|
+
# default send flags
|
341
|
+
send_flags = KAE::KAECanSwitchLayer
|
342
|
+
# ignore application's reply?
|
343
|
+
send_flags += keyword_args.delete(:wait_reply) == false ? KAE::KAENoReply : KAE::KAEWaitReply
|
344
|
+
# add considering/ignoring attributes
|
345
|
+
ignore_options = keyword_args.delete(:ignore)
|
346
|
+
if ignore_options == nil
|
347
|
+
atts['cons'] = DefaultConsiderations
|
348
|
+
atts['csig'] = DefaultConsidersAndIgnores
|
349
|
+
else
|
350
|
+
atts['cons'] = ignore_options
|
351
|
+
csig = 0
|
352
|
+
IgnoreEnums.each do |option, consider_mask, ignore_mask|
|
353
|
+
csig += ignore_options.include?(option) ? ignore_mask : consider_mask
|
354
|
+
end
|
355
|
+
atts['csig'] = Reference._pack_uint32(csig)
|
356
|
+
end
|
357
|
+
# optionally specify return value type
|
358
|
+
if keyword_args.has_key?(:result_type)
|
359
|
+
params['rtyp'] = keyword_args.delete(:result_type)
|
360
|
+
end
|
361
|
+
# extract labelled parameters, if any
|
362
|
+
keyword_args.each do |param_name, param_value|
|
363
|
+
param_code = labelled_arg_terms[param_name]
|
364
|
+
if param_code == nil
|
365
|
+
raise ArgumentError, "Unknown keyword parameter: #{param_name.inspect}"
|
366
|
+
end
|
367
|
+
params[param_code] = param_value
|
368
|
+
end
|
369
|
+
# apply special cases
|
370
|
+
# Note: appscript does not replicate every little AppleScript quirk when packing event attributes and parameters (e.g. AS always packs a make command's tell block as the subject attribute, and always includes an each parameter in count commands), but should provide sufficient consistency with AS's habits and give good usability in their own right.
|
371
|
+
if @AS_aem_reference != AEM.app # If command is called on a Reference, rather than an Application...
|
372
|
+
if code == 'coresetd'
|
373
|
+
# if ref.set(...) contains no 'to' argument, use direct argument for 'to' parameter and target reference for direct parameter
|
374
|
+
if params.has_key?('----') and not params.has_key?('data')
|
375
|
+
params['data'] = params['----']
|
376
|
+
params['----'] = @AS_aem_reference
|
377
|
+
elsif not params.has_key?('----')
|
378
|
+
params['----'] = @AS_aem_reference
|
379
|
+
else
|
380
|
+
atts['subj'] = @AS_aem_reference
|
381
|
+
end
|
382
|
+
elsif code == 'corecrel'
|
383
|
+
# this next bit is a bit tricky:
|
384
|
+
# - While it should be possible to pack the target reference as a subject attribute, when the target is of typeInsertionLoc, CocoaScripting stupidly tries to coerce it to typeObjectSpecifier, which causes a coercion error.
|
385
|
+
# - While it should be possible to pack the target reference as the 'at' parameter, some less-well-designed applications won't accept this and require it to be supplied as a subject attribute (i.e. how AppleScript supplies it).
|
386
|
+
# One option is to follow the AppleScript approach and force users to always supply subject attributes as target references and 'at' parameters as 'at' parameters, but the syntax for the latter is clumsy and not backwards-compatible with a lot of existing appscript code (since earlier versions allowed the 'at' parameter to be given as the target reference). So for now we split the difference when deciding what to do with a target reference: if it's an insertion location then pack it as the 'at' parameter (where possible), otherwise pack it as the subject attribute (and if the application doesn't like that then it's up to the client to pack it as an 'at' parameter themselves).
|
387
|
+
#
|
388
|
+
# if ref.make(...) contains no 'at' argument and target is an insertion reference, use target reference for 'at' parameter...
|
389
|
+
if @AS_aem_reference.is_a?(AEMReference::InsertionSpecifier) and not params.has_key?('insh')
|
390
|
+
params['insh'] = @AS_aem_reference
|
391
|
+
else # ...otherwise pack the target reference as the subject attribute
|
392
|
+
atts['subj'] = @AS_aem_reference
|
393
|
+
end
|
394
|
+
elsif params.has_key?('----')
|
395
|
+
# if user has already supplied a direct parameter, pack that reference as the subject attribute
|
396
|
+
atts['subj'] = @AS_aem_reference
|
397
|
+
else
|
398
|
+
# pack that reference as the direct parameter
|
399
|
+
params['----'] = @AS_aem_reference
|
400
|
+
end
|
401
|
+
end
|
402
|
+
# rescue => e
|
403
|
+
# raise AS::CommandError.new(self, name, args, e)
|
404
|
+
# end
|
405
|
+
# build and send the Apple event, returning its result, if any
|
406
|
+
begin
|
407
|
+
# puts 'SENDING EVENT: ' + [code, params, atts, timeout, send_flags].inspect
|
408
|
+
return @AS_app_data.target.event(code, params, atts,
|
409
|
+
KAE::KAutoGenerateReturnID, @AS_app_data).send(timeout, send_flags)
|
410
|
+
rescue => e
|
411
|
+
if e.is_a?(AEM::CommandError)
|
412
|
+
if [-600, -609].include?(e.number) and @AS_app_data.path
|
413
|
+
if not AEM::Application.is_running?(@AS_app_data.path)
|
414
|
+
if code == 'ascrnoop'
|
415
|
+
AEM::Application.launch(@AS_app_data.path)
|
416
|
+
elsif code != 'aevtoapp'
|
417
|
+
raise CommandError.new(self, name, args, e)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
@AS_app_data.target.reconnect
|
421
|
+
begin
|
422
|
+
return @AS_app_data.target.event(code, params, atts,
|
423
|
+
KAE::KAutoGenerateReturnID, @AS_app_data).send(timeout, send_flags)
|
424
|
+
rescue
|
425
|
+
end
|
426
|
+
elsif e.number == -1708 and code == 'ascrnoop'
|
427
|
+
return
|
428
|
+
end
|
429
|
+
end
|
430
|
+
raise CommandError.new(self, name, args, e)
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
|
435
|
+
#######
|
436
|
+
# introspection
|
437
|
+
|
438
|
+
def respond_to?(name)
|
439
|
+
if super
|
440
|
+
return true
|
441
|
+
else
|
442
|
+
return @AS_app_data.reference_by_name.has_key?(name.is_a?(String) ? name.intern : name)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def methods
|
447
|
+
return super + @AS_app_data.reference_by_name.keys.collect { |name| name.to_s }
|
448
|
+
end
|
449
|
+
|
450
|
+
def commands
|
451
|
+
return (@AS_app_data.reference_by_name.collect { |name, info| info[0] == :command ? name.to_s : nil }).compact.sort
|
452
|
+
end
|
453
|
+
|
454
|
+
def parameters(command_name)
|
455
|
+
if not @AS_app_data.reference_by_name.has_key?(command_name.intern)
|
456
|
+
raise ArgumentError, "Command not found: #{command_name}"
|
457
|
+
end
|
458
|
+
return (@AS_app_data.reference_by_name[command_name.intern][1][1].keys.collect { |name| name.to_s }).sort
|
459
|
+
end
|
460
|
+
|
461
|
+
def properties
|
462
|
+
return (@AS_app_data.reference_by_name.collect { |name, info| info[0] == :property ? name.to_s : nil }).compact.sort
|
463
|
+
end
|
464
|
+
|
465
|
+
def elements
|
466
|
+
return (@AS_app_data.reference_by_name.collect { |name, info| info[0] == :element ? name.to_s : nil }).compact.sort
|
467
|
+
end
|
468
|
+
|
469
|
+
def keywords
|
470
|
+
return (@AS_app_data.type_by_name.collect { |name, code| name.to_s }).sort
|
471
|
+
end
|
472
|
+
|
473
|
+
#######
|
474
|
+
# standard object methods
|
475
|
+
|
476
|
+
def ==(val)
|
477
|
+
return (self.class == val.class and @AS_app_data.target == val.AS_app_data.target \
|
478
|
+
and @AS_aem_reference == val.AS_aem_reference)
|
479
|
+
end
|
480
|
+
|
481
|
+
alias_method :eql?, :==
|
482
|
+
|
483
|
+
def hash
|
484
|
+
if not defined? @_hash
|
485
|
+
@_hash = [@AS_app_data.target, @AS_aem_reference].hash
|
486
|
+
end
|
487
|
+
return @_hash
|
488
|
+
end
|
489
|
+
|
490
|
+
def to_s
|
491
|
+
if not defined? @_to_s
|
492
|
+
@_to_s = ReferenceRenderer.render(@AS_app_data, @AS_aem_reference)
|
493
|
+
end
|
494
|
+
return @_to_s
|
495
|
+
end
|
496
|
+
|
497
|
+
alias_method :inspect, :to_s
|
498
|
+
|
499
|
+
#######
|
500
|
+
# Public properties and methods; these are called by end-user and other clients (e.g. generic references)
|
501
|
+
|
502
|
+
def method_missing(name, *args)
|
503
|
+
selector_type, code = @AS_app_data.reference_by_name[name]
|
504
|
+
case selector_type
|
505
|
+
when :property
|
506
|
+
return Reference.new(self.AS_app_data, self.AS_aem_reference.property(code))
|
507
|
+
when :element
|
508
|
+
return Reference.new(self.AS_app_data, self.AS_aem_reference.elements(code))
|
509
|
+
when :command
|
510
|
+
return _send_command(args, name, code[0], code[1])
|
511
|
+
else
|
512
|
+
# convenience shortcuts # TO DO: decide if these should be kept or not; note: if enabled, reserved keywords list will need to be expanded to include methods whose names end in '?' and '='
|
513
|
+
# - if name ends with '?', remove that and look up again; if property/element found, make new reference and send it a 'get' event, else raise 'unknown' error
|
514
|
+
# - if name ends with '=', remove that and look up again; if property/element found, make new reference and send it a 'set' event, else raise 'unknown' error
|
515
|
+
#name_str = name.to_s
|
516
|
+
#modifier = name_str[-1, 1]
|
517
|
+
#if modifier == '?' or modifier == '=' and name_str.length > 0
|
518
|
+
# new_name = name_str.chop.intern
|
519
|
+
# selector_type, code = @AS_app_data.reference_by_name[new_name]
|
520
|
+
# if [:property, :element].include?(selector_type) and
|
521
|
+
# case modifier
|
522
|
+
# when '?'
|
523
|
+
# return self.send(new_name, *args).get
|
524
|
+
# when '='
|
525
|
+
# return self.send(new_name, *args).set(*args)
|
526
|
+
# end
|
527
|
+
# end
|
528
|
+
#end
|
529
|
+
msg = "Unknown property, element or command: '#{name}'"
|
530
|
+
if @AS_app_data.reference_by_name.has_key?("#{name}_".intern)
|
531
|
+
msg += " (Did you mean '#{name}_'?)"
|
532
|
+
end
|
533
|
+
raise RuntimeError, msg
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def [](selector, end_range_selector=nil)
|
538
|
+
if end_range_selector != nil
|
539
|
+
new_ref = self.AS_aem_reference.by_range(
|
540
|
+
self._resolve_range_boundary(selector, 1),
|
541
|
+
self._resolve_range_boundary(end_range_selector, -1))
|
542
|
+
elsif selector.is_a?(String)
|
543
|
+
new_ref = @AS_aem_reference.by_name(selector)
|
544
|
+
elsif selector.is_a?(AS::GenericReference)
|
545
|
+
new_ref = @AS_aem_reference.by_filter(
|
546
|
+
selector.AS_resolve(@AS_app_data).AS_aem_reference)
|
547
|
+
else
|
548
|
+
new_ref = @AS_aem_reference.by_index(selector)
|
549
|
+
end
|
550
|
+
return Reference.new(self.AS_app_data, new_ref)
|
551
|
+
end
|
552
|
+
|
553
|
+
def first
|
554
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.first)
|
555
|
+
end
|
556
|
+
|
557
|
+
def middle
|
558
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.middle)
|
559
|
+
end
|
560
|
+
|
561
|
+
def last
|
562
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.last)
|
563
|
+
end
|
564
|
+
|
565
|
+
def any
|
566
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.any)
|
567
|
+
end
|
568
|
+
|
569
|
+
def start
|
570
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.start)
|
571
|
+
end
|
572
|
+
|
573
|
+
def end
|
574
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.end)
|
575
|
+
end
|
576
|
+
|
577
|
+
def before
|
578
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.before)
|
579
|
+
end
|
580
|
+
|
581
|
+
def after
|
582
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.after)
|
583
|
+
end
|
584
|
+
|
585
|
+
def previous(klass)
|
586
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.previous(
|
587
|
+
@AS_app_data.type_by_name.fetch(klass).code))
|
588
|
+
end
|
589
|
+
|
590
|
+
def next(klass)
|
591
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.next(
|
592
|
+
@AS_app_data.type_by_name.fetch(klass).code))
|
593
|
+
end
|
594
|
+
|
595
|
+
def ID(id)
|
596
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.by_id(id))
|
597
|
+
end
|
598
|
+
|
599
|
+
# Following methods will be called by its-based generic references
|
600
|
+
# Note that rb-appscript's comparison 'operator' names are gt/ge/eq/ne/lt/le, not >/>=/==/!=/</<= as in py-appscript. Unlike Python, Ruby's != operator isn't overridable, and a mixture of styles would be confusing to users. On the plus side, it does mean that rb-appscript's generic refs can be compared for equality.
|
601
|
+
|
602
|
+
def gt(operand)
|
603
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.gt(operand))
|
604
|
+
end
|
605
|
+
|
606
|
+
def ge(operand)
|
607
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.ge(operand))
|
608
|
+
end
|
609
|
+
|
610
|
+
def eq(operand) # avoid colliding with comparison operators, which are normally used to compare two references
|
611
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.eq(operand))
|
612
|
+
end
|
613
|
+
|
614
|
+
def ne(operand)
|
615
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.ne(operand))
|
616
|
+
end
|
617
|
+
|
618
|
+
def lt(operand)
|
619
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.lt(operand))
|
620
|
+
end
|
621
|
+
|
622
|
+
def le(operand)
|
623
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.le(operand))
|
624
|
+
end
|
625
|
+
|
626
|
+
def starts_with(operand)
|
627
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.starts_with(operand))
|
628
|
+
end
|
629
|
+
|
630
|
+
def ends_with(operand)
|
631
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.ends_with(operand))
|
632
|
+
end
|
633
|
+
|
634
|
+
def contains(operand)
|
635
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.contains(operand))
|
636
|
+
end
|
637
|
+
|
638
|
+
def is_in(operand)
|
639
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.is_in(operand))
|
640
|
+
end
|
641
|
+
|
642
|
+
def does_not_start_with(operand)
|
643
|
+
return self.starts_with(operand).not
|
644
|
+
end
|
645
|
+
|
646
|
+
def does_not_end_with(operand)
|
647
|
+
return self.ends_with(operand).not
|
648
|
+
end
|
649
|
+
|
650
|
+
def does_not_contain(operand)
|
651
|
+
return self.contains(operand).not
|
652
|
+
end
|
653
|
+
|
654
|
+
def is_not_in(operand)
|
655
|
+
return self.is_in(operand).not
|
656
|
+
end
|
657
|
+
|
658
|
+
def and(*operands)
|
659
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.and(*operands))
|
660
|
+
end
|
661
|
+
|
662
|
+
def or(*operands)
|
663
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.or(*operands))
|
664
|
+
end
|
665
|
+
|
666
|
+
def not
|
667
|
+
return Reference.new(@AS_app_data, @AS_aem_reference.not)
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
|
672
|
+
######################################################################
|
673
|
+
# APPLICATION
|
674
|
+
######################################################################
|
675
|
+
|
676
|
+
class Application < Reference
|
677
|
+
|
678
|
+
private_class_method :new
|
679
|
+
|
680
|
+
def _aem_application_class # hook
|
681
|
+
return AEM::Application
|
682
|
+
end
|
683
|
+
|
684
|
+
def initialize(path, pid, url, terms)
|
685
|
+
super(AppData.new(_aem_application_class, path, pid, url, terms), AEM.app)
|
686
|
+
end
|
687
|
+
|
688
|
+
# constructors
|
689
|
+
|
690
|
+
def Application.by_name(name, terms=true)
|
691
|
+
return new(FindApp.by_name(name), nil, nil, terms)
|
692
|
+
end
|
693
|
+
|
694
|
+
def Application.by_id(id, terms=true)
|
695
|
+
return new(FindApp.by_id(id), nil, nil, terms)
|
696
|
+
end
|
697
|
+
|
698
|
+
def Application.by_creator(creator, terms=true)
|
699
|
+
return new(FindApp.by_creator(creator), nil, nil, terms)
|
700
|
+
end
|
701
|
+
|
702
|
+
def Application.by_pid(pid, terms=true)
|
703
|
+
return new(nil, pid, nil, terms)
|
704
|
+
end
|
705
|
+
|
706
|
+
def Application.by_url(url, terms=true)
|
707
|
+
return new(nil, nil, url, terms)
|
708
|
+
end
|
709
|
+
|
710
|
+
def Application.current(terms=true)
|
711
|
+
return new(nil, nil, nil, terms)
|
712
|
+
end
|
713
|
+
|
714
|
+
#
|
715
|
+
|
716
|
+
def start_transaction(session=nil)
|
717
|
+
@AS_app_data.target.start_transaction(session)
|
718
|
+
end
|
719
|
+
|
720
|
+
def abort_transaction
|
721
|
+
@AS_app_data.target.abort_transaction
|
722
|
+
end
|
723
|
+
|
724
|
+
def end_transaction
|
725
|
+
@AS_app_data.target.end_transaction
|
726
|
+
end
|
727
|
+
|
728
|
+
def launch
|
729
|
+
if @AS_app_data.path
|
730
|
+
AEM::Application.launch(@AS_app_data.path)
|
731
|
+
@AS_app_data.target.reconnect
|
732
|
+
else
|
733
|
+
@AS_app_data.target.event('ascrnoop').send # will send launch event to app if already running; else will error
|
734
|
+
end
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
738
|
+
##
|
739
|
+
|
740
|
+
class GenericApplication < GenericReference
|
741
|
+
|
742
|
+
def initialize(app_class)
|
743
|
+
@_app_class = app_class
|
744
|
+
super(['app'])
|
745
|
+
end
|
746
|
+
|
747
|
+
def by_name(name, terms=true)
|
748
|
+
return @_app_class.by_name(name, terms)
|
749
|
+
end
|
750
|
+
|
751
|
+
def by_id(id, terms=true)
|
752
|
+
return @_app_class.by_id(id, terms)
|
753
|
+
end
|
754
|
+
|
755
|
+
def by_creator(creator, terms=true)
|
756
|
+
return @_app_class.by_creator(creator, terms)
|
757
|
+
end
|
758
|
+
|
759
|
+
def by_pid(pid, terms=true)
|
760
|
+
return @_app_class.by_pid(pid, terms)
|
761
|
+
end
|
762
|
+
|
763
|
+
def by_url(url, terms=true)
|
764
|
+
return @_app_class.by_url(url, terms)
|
765
|
+
end
|
766
|
+
|
767
|
+
def current(terms=true)
|
768
|
+
return @_app_class.current(terms)
|
769
|
+
end
|
770
|
+
end
|
771
|
+
|
772
|
+
#######
|
773
|
+
|
774
|
+
AS_App = AS::GenericApplication.new(Application)
|
775
|
+
AS_Con = AS::GenericReference.new(['con'])
|
776
|
+
AS_Its = AS::GenericReference.new(['its'])
|
777
|
+
|
778
|
+
|
779
|
+
######################################################################
|
780
|
+
# REFERENCE ROOTS
|
781
|
+
######################################################################
|
782
|
+
# public (note: Application & GenericApplication classes may also be accessed if subclassing Application class is required)
|
783
|
+
|
784
|
+
def AS.app(*args)
|
785
|
+
if args == []
|
786
|
+
return AS_App
|
787
|
+
else
|
788
|
+
return AS_App.by_name(*args)
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
792
|
+
def AS.con
|
793
|
+
return AS_Con
|
794
|
+
end
|
795
|
+
|
796
|
+
def AS.its
|
797
|
+
return AS_Its
|
798
|
+
end
|
799
|
+
|
800
|
+
|
801
|
+
######################################################################
|
802
|
+
# COMMAND ERROR
|
803
|
+
######################################################################
|
804
|
+
# public
|
805
|
+
|
806
|
+
class CommandError < RuntimeError
|
807
|
+
|
808
|
+
attr_reader :reference, :name, :parameters, :real_error
|
809
|
+
|
810
|
+
def initialize(reference, command_name, parameters, real_error)
|
811
|
+
@reference, @command_name, @parameters, @real_error = reference, command_name, parameters, real_error
|
812
|
+
super()
|
813
|
+
end
|
814
|
+
|
815
|
+
def to_i
|
816
|
+
if @real_error.is_a?(AE::MacOSError) or @real_error.is_a?(AEM::CommandError)
|
817
|
+
return @real_error.to_i
|
818
|
+
else
|
819
|
+
return -2700
|
820
|
+
end
|
821
|
+
end
|
822
|
+
|
823
|
+
def to_s
|
824
|
+
return "#{@real_error}\n\t\tCOMMAND: #{@reference}.#{@command_name}(#{(@parameters.collect { |item| item.inspect }).join(', ')})\n"
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
|
829
|
+
ApplicationNotFoundError = FindApp::ApplicationNotFoundError
|
830
|
+
end
|