rb-appscript 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/CHANGES +243 -0
  2. data/LICENSE +1 -0
  3. data/README +42 -0
  4. data/TODO +31 -0
  5. data/doc/aem-manual/01_introduction.html +48 -0
  6. data/doc/aem-manual/02_apioverview.html +89 -0
  7. data/doc/aem-manual/03_packingandunpackingdata.html +98 -0
  8. data/doc/aem-manual/04_references.html +401 -0
  9. data/doc/aem-manual/05_targettingapplications.html +133 -0
  10. data/doc/aem-manual/06_buildingandsendingevents.html +175 -0
  11. data/doc/aem-manual/07_findapp.html +54 -0
  12. data/doc/aem-manual/08_examples.html +85 -0
  13. data/doc/aem-manual/09_notes.html +41 -0
  14. data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
  15. data/doc/aem-manual/full.css +21 -0
  16. data/doc/aem-manual/index.html +43 -0
  17. data/doc/appscript-manual/01_introduction.html +82 -0
  18. data/doc/appscript-manual/02_aboutappscripting.html +244 -0
  19. data/doc/appscript-manual/03_quicktutorial.html +154 -0
  20. data/doc/appscript-manual/04_gettinghelp.html +101 -0
  21. data/doc/appscript-manual/05_keywordconversion.html +91 -0
  22. data/doc/appscript-manual/06_classesandenums.html +174 -0
  23. data/doc/appscript-manual/07_applicationobjects.html +181 -0
  24. data/doc/appscript-manual/08_realvsgenericreferences.html +86 -0
  25. data/doc/appscript-manual/09_referenceforms.html +232 -0
  26. data/doc/appscript-manual/10_referenceexamples.html +142 -0
  27. data/doc/appscript-manual/11_applicationcommands.html +204 -0
  28. data/doc/appscript-manual/12_commandexamples.html +129 -0
  29. data/doc/appscript-manual/13_performanceissues.html +115 -0
  30. data/doc/appscript-manual/14_problemapps.html +193 -0
  31. data/doc/appscript-manual/15_notes.html +84 -0
  32. data/doc/appscript-manual/application_architecture.gif +0 -0
  33. data/doc/appscript-manual/application_architecture2.gif +0 -0
  34. data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
  35. data/doc/appscript-manual/full.css +21 -0
  36. data/doc/appscript-manual/index.html +49 -0
  37. data/doc/appscript-manual/relationships_example.gif +0 -0
  38. data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
  39. data/doc/index.html +30 -0
  40. data/doc/mactypes-manual/index.html +216 -0
  41. data/doc/osax-manual/index.html +169 -0
  42. data/extconf.rb +54 -0
  43. data/misc/adobeunittypes.rb +14 -0
  44. data/misc/dump.rb +72 -0
  45. data/rb-appscript.gemspec +20 -0
  46. data/sample/AB_list_people_with_emails.rb +8 -0
  47. data/sample/Create_daily_iCal_todos.rb +72 -0
  48. data/sample/Hello_world.rb +9 -0
  49. data/sample/List_iTunes_playlist_names.rb +7 -0
  50. data/sample/Make_Mail_message.rb +29 -0
  51. data/sample/Open_file_in_TextEdit.rb +9 -0
  52. data/sample/Organize_Mail_messages.rb +57 -0
  53. data/sample/Print_folder_tree.rb +12 -0
  54. data/sample/Select_all_HTML_files.rb +8 -0
  55. data/sample/Set_iChat_status.rb +20 -0
  56. data/sample/Simple_Finder_GUI_Scripting.rb +14 -0
  57. data/sample/Stagger_Finder_windows.rb +21 -0
  58. data/sample/TextEdit_demo.rb +126 -0
  59. data/sample/iTunes_top40_to_html.rb +64 -0
  60. data/src/lib/_aem/aemreference.rb +1006 -0
  61. data/src/lib/_aem/codecs.rb +617 -0
  62. data/src/lib/_aem/connect.rb +100 -0
  63. data/src/lib/_aem/findapp.rb +83 -0
  64. data/src/lib/_aem/mactypes.rb +228 -0
  65. data/src/lib/_aem/send.rb +257 -0
  66. data/src/lib/_aem/typewrappers.rb +57 -0
  67. data/src/lib/_appscript/defaultterminology.rb +245 -0
  68. data/src/lib/_appscript/referencerenderer.rb +132 -0
  69. data/src/lib/_appscript/reservedkeywords.rb +107 -0
  70. data/src/lib/_appscript/terminology.rb +314 -0
  71. data/src/lib/aem.rb +216 -0
  72. data/src/lib/appscript.rb +830 -0
  73. data/src/lib/kae.rb +1484 -0
  74. data/src/lib/osax.rb +171 -0
  75. data/src/rbae.c +766 -0
  76. data/test/README +1 -0
  77. data/test/test_aemreference.rb +112 -0
  78. data/test/test_appscriptreference.rb +102 -0
  79. data/test/test_codecs.rb +159 -0
  80. data/test/test_findapp.rb +24 -0
  81. data/test/test_mactypes.rb +67 -0
  82. data/test/testall.sh +9 -0
  83. 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