rb-scpt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +497 -0
  3. data/doc/aem-manual/01_introduction.html +60 -0
  4. data/doc/aem-manual/02_apioverview.html +107 -0
  5. data/doc/aem-manual/03_packingandunpackingdata.html +135 -0
  6. data/doc/aem-manual/04_references.html +409 -0
  7. data/doc/aem-manual/05_targetingapplications.html +164 -0
  8. data/doc/aem-manual/06_buildingandsendingevents.html +229 -0
  9. data/doc/aem-manual/07_findapp.html +63 -0
  10. data/doc/aem-manual/08_examples.html +94 -0
  11. data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
  12. data/doc/aem-manual/index.html +56 -0
  13. data/doc/appscript-manual/01_introduction.html +94 -0
  14. data/doc/appscript-manual/02_aboutappscripting.html +247 -0
  15. data/doc/appscript-manual/03_quicktutorial.html +167 -0
  16. data/doc/appscript-manual/04_gettinghelp.html +188 -0
  17. data/doc/appscript-manual/05_keywordconversion.html +106 -0
  18. data/doc/appscript-manual/06_classesandenums.html +192 -0
  19. data/doc/appscript-manual/07_applicationobjects.html +211 -0
  20. data/doc/appscript-manual/08_realvsgenericreferences.html +96 -0
  21. data/doc/appscript-manual/09_referenceforms.html +241 -0
  22. data/doc/appscript-manual/10_referenceexamples.html +154 -0
  23. data/doc/appscript-manual/11_applicationcommands.html +245 -0
  24. data/doc/appscript-manual/12_commandexamples.html +138 -0
  25. data/doc/appscript-manual/13_performanceissues.html +142 -0
  26. data/doc/appscript-manual/14_notes.html +80 -0
  27. data/doc/appscript-manual/application_architecture.gif +0 -0
  28. data/doc/appscript-manual/application_architecture2.gif +0 -0
  29. data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
  30. data/doc/appscript-manual/index.html +62 -0
  31. data/doc/appscript-manual/relationships_example.gif +0 -0
  32. data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
  33. data/doc/full.css +106 -0
  34. data/doc/index.html +45 -0
  35. data/doc/mactypes-manual/01_introduction.html +54 -0
  36. data/doc/mactypes-manual/02_aliasclass.html +124 -0
  37. data/doc/mactypes-manual/03_fileurlclass.html +126 -0
  38. data/doc/mactypes-manual/04_unitsclass.html +100 -0
  39. data/doc/mactypes-manual/index.html +53 -0
  40. data/doc/osax-manual/01_introduction.html +67 -0
  41. data/doc/osax-manual/02_interface.html +147 -0
  42. data/doc/osax-manual/03_examples.html +73 -0
  43. data/doc/osax-manual/04_notes.html +61 -0
  44. data/doc/osax-manual/index.html +53 -0
  45. data/doc/rb-appscript-logo.png +0 -0
  46. data/extconf.rb +65 -0
  47. data/rb-scpt.gemspec +14 -0
  48. data/sample/AB_export_vcard.rb +31 -0
  49. data/sample/AB_list_people_with_emails.rb +13 -0
  50. data/sample/Add_iCal_event.rb +21 -0
  51. data/sample/Create_daily_iCal_todos.rb +75 -0
  52. data/sample/Export_Address_Book_phone_numbers.rb +59 -0
  53. data/sample/Hello_world.rb +21 -0
  54. data/sample/List_iTunes_playlist_names.rb +11 -0
  55. data/sample/Make_Mail_message.rb +33 -0
  56. data/sample/Open_file_in_TextEdit.rb +13 -0
  57. data/sample/Organize_Mail_messages.rb +61 -0
  58. data/sample/Print_folder_tree.rb +16 -0
  59. data/sample/Select_all_HTML_files.rb +14 -0
  60. data/sample/Set_iChat_status.rb +24 -0
  61. data/sample/Simple_Finder_GUI_Scripting.rb +18 -0
  62. data/sample/Stagger_Finder_windows.rb +25 -0
  63. data/sample/TextEdit_demo.rb +130 -0
  64. data/sample/iTunes_top40_to_html.rb +71 -0
  65. data/src/SendThreadSafe.c +380 -0
  66. data/src/SendThreadSafe.h +139 -0
  67. data/src/lib/_aem/aemreference.rb +1022 -0
  68. data/src/lib/_aem/codecs.rb +662 -0
  69. data/src/lib/_aem/connect.rb +205 -0
  70. data/src/lib/_aem/encodingsupport.rb +77 -0
  71. data/src/lib/_aem/findapp.rb +85 -0
  72. data/src/lib/_aem/mactypes.rb +251 -0
  73. data/src/lib/_aem/send.rb +279 -0
  74. data/src/lib/_aem/typewrappers.rb +59 -0
  75. data/src/lib/_appscript/defaultterminology.rb +277 -0
  76. data/src/lib/_appscript/referencerenderer.rb +245 -0
  77. data/src/lib/_appscript/reservedkeywords.rb +116 -0
  78. data/src/lib/_appscript/safeobject.rb +249 -0
  79. data/src/lib/_appscript/terminology.rb +471 -0
  80. data/src/lib/aem.rb +253 -0
  81. data/src/lib/appscript.rb +1075 -0
  82. data/src/lib/kae.rb +1489 -0
  83. data/src/lib/osax.rb +659 -0
  84. data/src/rbae.c +979 -0
  85. data/test/README +3 -0
  86. data/test/test_aemreference.rb +118 -0
  87. data/test/test_appscriptcommands.rb +152 -0
  88. data/test/test_appscriptreference.rb +106 -0
  89. data/test/test_codecs.rb +186 -0
  90. data/test/test_findapp.rb +26 -0
  91. data/test/test_mactypes.rb +79 -0
  92. data/test/test_osax.rb +54 -0
  93. data/test/testall.sh +10 -0
  94. metadata +145 -0
@@ -0,0 +1,253 @@
1
+ #
2
+ # rb-appscript
3
+ #
4
+ # aem -- a mid-level object-oriented API for creating and sending Apple events
5
+ # using raw AE codes; may be used directly or via high-level appscript wrapper
6
+ #
7
+
8
+ require "ae"
9
+ require "kae"
10
+ require "_aem/findapp"
11
+ require "_aem/mactypes"
12
+
13
+ module AEM
14
+
15
+ # Mid-level wrapper for building and sending Apple events to local and remote applications.
16
+
17
+ require "_aem/codecs"
18
+ require "_aem/aemreference"
19
+ require "_aem/typewrappers"
20
+ require "_aem/connect"
21
+ require "_aem/send"
22
+
23
+ #######
24
+ # Constants
25
+
26
+ Codecs = Codecs
27
+ DefaultCodecs = DefaultCodecs
28
+ MacOSError = AE::MacOSError
29
+ CantLaunchApplicationError = Connect::CantLaunchApplicationError
30
+
31
+
32
+ AEDesc = AE::AEDesc
33
+
34
+ AETypeBase = TypeWrappers::AETypeBase
35
+ AEType = TypeWrappers::AEType
36
+ AEEnum = TypeWrappers::AEEnum
37
+ AEProp = TypeWrappers::AEProp
38
+ AEKey = TypeWrappers::AEKey
39
+
40
+ EventError = Send::EventError
41
+ CommandError = Send::EventError # deprecated class name; kept for backwards compatibility
42
+
43
+ #######
44
+ # Reference roots
45
+
46
+ def AEM.app
47
+ return AEMReference::App
48
+ end
49
+
50
+ def AEM.con
51
+ return AEMReference::Con
52
+ end
53
+
54
+ def AEM.its
55
+ return AEMReference::Its
56
+ end
57
+
58
+ def AEM.custom_root(value)
59
+ return AEMReference::CustomRoot.new(value)
60
+ end
61
+
62
+ #######
63
+ # Application class
64
+
65
+ class Application < AEMReference::Query
66
+ # Identifies an application and provides an #event method for constructing Apple events targetted at it.
67
+
68
+ require "weakref"
69
+
70
+ private_class_method :new
71
+ attr_reader :hash, :identity, :address_desc
72
+
73
+ #######
74
+ # Workaround for lack of proper destructors in Ruby; see #initialize method.
75
+
76
+ @@_app_number_count = 0
77
+ @@_transaction_ids_by_app_no = {}
78
+
79
+ #######
80
+
81
+ Event = Send::Event # Application subclasses can override this class constant (usually with a subclass of Send::Event) to modify how Apple events are created and/or sent.
82
+
83
+ #######
84
+
85
+ def initialize(path, address_desc, identity)
86
+ # called by constructor method
87
+ # path is used by #reconnect
88
+ # address_desc is an AEAddressDesc identifying the target application
89
+ # identity is used by #inspect, #hash, #==
90
+ @_transaction = KAE::KAnyTransactionID
91
+ @_path = path
92
+ @address_desc = address_desc
93
+ @identity = identity
94
+ @hash = identity.hash
95
+ # workaround for lack of proper destructors; if a transaction is still open when Application instance is garbage collected, the following finalizer will automatically close it. Note: object IDs were different for some reason, so class maintains its own unique ids.
96
+ @app_number = app_number = (@@_app_number_count += 1)
97
+ @@_transaction_ids_by_app_no[app_number] = @_transaction
98
+ ObjectSpace.define_finalizer(WeakRef.new(self), proc do
99
+ transaction_id = @@_transaction_ids_by_app_no.delete(app_number)
100
+ if transaction_id != KAE::KAnyTransactionID
101
+ self.class::Event.new(@address_desc, 'miscendt', {}, {}, transaction_id).send(60, KAE::KAENoReply)
102
+ end
103
+ end)
104
+ end
105
+
106
+ #######
107
+ # utility class methods; placed here for convenience
108
+
109
+ def Application.launch(path)
110
+ # Launches a local application without sending it the usual 'run' event (aevtoapp).
111
+ Connect.launch_app_with_launch_event(path)
112
+ end
113
+
114
+ def Application.process_exists_for_path?(path)
115
+ # Does a local process launched from the specified application file exist?
116
+ # Note: if path is invalid, an AE::MacOSError is raised.
117
+ return Connect.process_exists_for_path?(path)
118
+ end
119
+
120
+ def Application.process_exists_for_pid?(pid)
121
+ # Is there a local application process with the given unix process id?
122
+ return Connect.process_exists_for_pid?(pid)
123
+ end
124
+
125
+ def Application.process_exists_for_url?(url)
126
+ # Does an application process specified by the given eppc:// URL exist?
127
+ # Returns false if process doesn't exist or if access to it isn't allowed.
128
+ # (Implementation note: this method sends an Apple event to the specified process and checks for errors.)
129
+ return Connect.process_exists_for_url?(url)
130
+ end
131
+
132
+ def Application.process_exists_for_desc?(desc)
133
+ # Does an application process specified by the given AEAddressDesc exist?
134
+ # Returns false if process doesn't exist or if access to it isn't allowed.
135
+ # (Implementation note: this method sends an Apple event to the specified process and checks for errors.)
136
+ return Connect.process_exists_for_desc?(desc)
137
+ end
138
+
139
+ #######
140
+ # constructors
141
+
142
+ def Application.by_path(path)
143
+ # path : string -- full path to local application
144
+ #
145
+ # Note: application will be launched if not already running.
146
+ return new(path, Connect.local_app(path), [:path, path])
147
+ end
148
+
149
+ def Application.by_url(url)
150
+ # url : string -- eppc URL for remote process
151
+ return new(nil, Connect.remote_app(url), [:url, url])
152
+ end
153
+
154
+ def Application.by_pid(pid)
155
+ # pid : integer -- Unix process id
156
+ return new(nil, Connect.local_app_by_pid(pid), [:pid, pid])
157
+ end
158
+
159
+ def Application.by_desc(desc)
160
+ # desc : AEDesc -- an AEAddressDesc
161
+ return new(nil, desc, [:desc, desc.type, desc.data])
162
+ end
163
+
164
+ def Application.current
165
+ return new(nil, Connect::CurrentApp, [:current])
166
+ end
167
+
168
+ #######
169
+ # methods
170
+
171
+ def inspect
172
+ if @identity[0] == :current
173
+ return "#{self.class}.current"
174
+ else
175
+ con_name = {:path => 'by_path', :url => 'by_url', :pid => 'by_pid', :desc => 'by_desc'}[@identity[0]]
176
+ return "#{self.class}.#{con_name}(#{@identity[1].inspect})"
177
+ end
178
+ end
179
+
180
+ alias_method :to_s, :inspect
181
+
182
+ def ==(val)
183
+ return (self.class == val.class and @identity == val.identity)
184
+ end
185
+
186
+ alias_method :eql?, :==
187
+
188
+ # (hash method is provided by attr_reader :hash)
189
+
190
+ def AEM_comparable
191
+ return ['AEMApplication', @identity]
192
+ end
193
+
194
+ def AEM_pack_self(codecs)
195
+ return @address_desc
196
+ end
197
+
198
+ def reconnect
199
+ # If application has quit since this Application object was created, its AEAddressDesc
200
+ # is no longer valid so this Application object will not work even when application is restarted.
201
+ # #reconnect will update this Application object's AEAddressDesc so it's valid again.
202
+ #
203
+ # Note that this only works for Application objects created via the by_path constructor.
204
+ # Also note that any Event objects created prior to calling #reconnect will still be invalid.
205
+ if @_path
206
+ @address_desc = Connect.local_app(@_path)
207
+ end
208
+ return
209
+ end
210
+
211
+ def event(event, params={}, atts={}, return_id=KAE::KAutoGenerateReturnID, codecs=DefaultCodecs)
212
+ # Construct an Apple event targetted at this application.
213
+ # event : string -- 8-letter code indicating event's class, e.g. 'coregetd'
214
+ # params : hash -- a dict of form {AE_code:anything,...} containing zero or more event parameters (message arguments)
215
+ # atts : hash -- a dict of form {AE_code:anything,...} containing zero or more event attributes (event info)
216
+ # return_id : integer -- reply event's ID
217
+ # codecs : Codecs -- codecs object to use when packing/unpacking this event
218
+ return self.class::Event.new(@address_desc, event, params, atts, @_transaction, return_id, codecs)
219
+ end
220
+
221
+ def begin_transaction(session=nil)
222
+ # Begin a new transaction.
223
+ if @_transaction != KAE::KAnyTransactionID
224
+ raise RuntimeError, "Transaction is already active."
225
+ end
226
+ @_transaction = self.class::Event.new(@address_desc, 'miscbegi', session != nil ? {'----' => session} : {}).send
227
+ @@_transaction_ids_by_app_no[@app_number] = @_transaction
228
+ return
229
+ end
230
+
231
+ def abort_transaction
232
+ # Abort the current transaction.
233
+ if @_transaction == KAE::KAnyTransactionID
234
+ raise RuntimeError, "No transaction is active."
235
+ end
236
+ self.class::Event.new(@address_desc, 'miscttrm', {}, {}, @_transaction).send
237
+ @_transaction = KAE::KAnyTransactionID
238
+ @@_transaction_ids_by_app_no[@app_number] = KAE::KAnyTransactionID
239
+ return
240
+ end
241
+
242
+ def end_transaction
243
+ # End the current transaction.
244
+ if @_transaction == KAE::KAnyTransactionID
245
+ raise RuntimeError, "No transaction is active."
246
+ end
247
+ self.class::Event.new(@address_desc, 'miscendt', {}, {}, @_transaction).send
248
+ @_transaction = KAE::KAnyTransactionID
249
+ @@_transaction_ids_by_app_no[@app_number] = KAE::KAnyTransactionID
250
+ return
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,1075 @@
1
+ #
2
+ # rb-appscript
3
+ #
4
+ # appscript -- syntactically sugared wrapper around the mid-level aem API;
5
+ # provides a high-level, easy-to-use API for creating and sending Apple events
6
+ #
7
+
8
+ require "_aem/mactypes"
9
+
10
+ module Appscript
11
+
12
+ Version = '0.5.3'
13
+
14
+ # The following methods and classes are of interest to end users:
15
+ # app, con, its, CommandError, ApplicationNotFoundError, CantLaunchApplicationError
16
+ # Other classes are only of interest to implementors who need to hook in their own code.
17
+
18
+ require "kae"
19
+ require "aem"
20
+ require "_aem/aemreference"
21
+ require "_appscript/referencerenderer"
22
+ require "_appscript/terminology"
23
+ require "_appscript/safeobject"
24
+
25
+ ######################################################################
26
+ # APPDATA
27
+ ######################################################################
28
+
29
+ module AppDataAccessors
30
+ attr_reader :target, :type_by_name, :type_by_code, :reference_by_name, :reference_by_code
31
+ end
32
+
33
+ class AppData < AEM::Codecs
34
+
35
+ ASDictionaryBundleID = 'net.sourceforge.appscript.asdictionary'
36
+
37
+ attr_reader :constructor, :identifier, :reference_codecs
38
+ attr_writer :reference_codecs
39
+
40
+ def initialize(aem_application_class, constructor, identifier, terms)
41
+ super()
42
+ @_aem_application_class = aem_application_class # AEM::Application class or subclass to use when constructing target
43
+ @_terms = terms # user-supplied terminology tables/true/false
44
+ @constructor = constructor # name of AEM::Application constructor to use/:by_aem_app
45
+ @identifier = identifier # argument for AEM::Application constructor
46
+ @reference_codecs = AEM::Codecs.new # low-level Codecs object used to unpack references; used by AppData#unpack_object_specifier, AppData#unpack_insertion_loc. Note: this is a bit kludgy, and it's be better to use AppData for all unpacking, but it should be 'good enough' in practice.
47
+ @_help_agent = nil
48
+ end
49
+
50
+ def connect # initialize AEM::Application instance and terminology tables the first time they are needed
51
+ case @constructor
52
+ when :by_aem_app
53
+ @target = @identifier
54
+ when :current
55
+ @target = @_aem_application_class.current
56
+ else
57
+ @target = @_aem_application_class.send(@constructor, @identifier)
58
+ end
59
+ case @_terms
60
+ when true # obtain terminology from application
61
+ @type_by_code, @type_by_name, @reference_by_code, @reference_by_name = Terminology.tables_for_app(@target)
62
+ when false # use built-in terminology only (e.g. use this when running AppleScript applets)
63
+ @type_by_code, @type_by_name, @reference_by_code, @reference_by_name = Terminology.default_tables
64
+ when nil # [developer-only] make Application#methods return names of built-in methods only (needed to generate reservedkeywords.rb file)
65
+ @type_by_code, @type_by_name, @reference_by_code, @reference_by_name = {}, {}, {}, {}
66
+ when Array # ready-to-use terminology tables
67
+ @type_by_code, @type_by_name, @reference_by_code, @reference_by_name = @_terms
68
+ else # @_terms is [assumed to be] a module containing dumped terminology, so use that
69
+ @type_by_code, @type_by_name, @reference_by_code, @reference_by_name = Terminology.tables_for_module(@_terms)
70
+ end
71
+ extend(AppDataAccessors)
72
+ end
73
+
74
+ #######
75
+
76
+ def dont_cache_unpacked_specifiers
77
+ @reference_codecs.dont_cache_unpacked_specifiers
78
+ end
79
+
80
+ #######
81
+
82
+ Constructors = {
83
+ :by_path => 'path',
84
+ :by_pid => 'pid',
85
+ :by_url => 'url',
86
+ :by_aem_app => 'aemapp',
87
+ :current => 'current',
88
+ }
89
+
90
+ def _init_help_agent
91
+ begin
92
+ apppath = FindApp.by_id(ASDictionaryBundleID)
93
+ asdictionary_is_running = AEM::Application.process_exists_for_path?(apppath)
94
+ @_help_agent = AEM::Application.by_path(apppath)
95
+ if not asdictionary_is_running # hide ASDictionary after launching it
96
+ AEM::Application.by_path(FindApp.by_id('com.apple.systemevents')).event('coresetd', {
97
+ '----' => AEM.app.elements('prcs').by_name('ASDictionary').property('pvis'),
98
+ 'data' => false}).send
99
+ end
100
+ return true
101
+ rescue FindApp::ApplicationNotFoundError => e
102
+ $stderr.puts("No help available: ASDictionary application not found (#{e}).")
103
+ rescue AEM::CantLaunchApplicationError => e
104
+ $stderr.puts("No help available: can't launch ASDictionary application (#{e}).")
105
+ end
106
+ return false
107
+ end
108
+
109
+ def _display_help(flags, ref)
110
+ begin
111
+ $stderr.puts(@_help_agent.event('AppSHelp', {
112
+ 'Cons' => Constructors[@constructor],
113
+ 'Iden' => @identifier,
114
+ 'Styl' => 'rb-appscript',
115
+ 'Flag' => flags,
116
+ 'aRef' => pack(ref),
117
+ }).send)
118
+ return nil
119
+ rescue AEM::EventError => e
120
+ return e
121
+ end
122
+ end
123
+
124
+ def help(flags, ref)
125
+ begin
126
+ if not @_help_agent
127
+ return ref if not _init_help_agent
128
+ end
129
+ e = _display_help(flags, ref)
130
+ if e and [-600, -609].include?(e.number) # not running
131
+ return ref if not _init_help_agent
132
+ e = _display_help(flags, ref)
133
+ end
134
+ $stderr.puts("No help available: ASDictionary raised an error: #{e}.") if e
135
+ rescue => err
136
+ $stderr.puts("No help available: unknown error: #{err}")
137
+ end
138
+ return ref
139
+ end
140
+
141
+ #######
142
+
143
+ def target
144
+ connect
145
+ return @target
146
+ end
147
+
148
+ def type_by_name
149
+ connect
150
+ return @type_by_name
151
+ end
152
+
153
+ def type_by_code
154
+ connect
155
+ return @type_by_code
156
+ end
157
+
158
+ def reference_by_name
159
+ connect
160
+ return @reference_by_name
161
+ end
162
+
163
+ def reference_by_code
164
+ connect
165
+ return @reference_by_code
166
+ end
167
+
168
+ #######
169
+
170
+ def pack(data)
171
+ if data.is_a?(GenericReference)
172
+ data = data.AS_resolve(self)
173
+ end
174
+ if data.is_a?(Reference)
175
+ data = data.AS_aem_reference
176
+ elsif data.is_a?(Symbol)
177
+ data = self.type_by_name.fetch(data) { raise IndexError, "Unknown Keyword: #{data.inspect}" }
178
+ end
179
+ return super(data)
180
+ end
181
+
182
+ ##
183
+
184
+ ClassType = AEM::AEType.new(KAE::PClass)
185
+
186
+ def pack_hash(val)
187
+ record = AE::AEDesc.new_list(true)
188
+ if val.has_key?(:class_) or val.has_key?(ClassType)
189
+ # if hash contains a 'class' property containing a class name, coerce the AEDesc to that class
190
+ new_val = Hash[val]
191
+ if new_val.has_key?(:class_)
192
+ val2 = new_val.delete(:class_)
193
+ else
194
+ val2 = new_val.delete(ClassType)
195
+ end
196
+ if val2.is_a?(Symbol) # get the corresponding AEType (assuming there is one)
197
+ val2 = @type_by_name.fetch(val2, val2)
198
+ end
199
+ if val2.is_a?(AEM::AEType) # coerce the record to the desired type
200
+ record = record.coerce(val2.code)
201
+ val = new_val
202
+ end # else value wasn't a class name, so it'll be packed as a normal record property instead
203
+ end
204
+ usrf = nil
205
+ val.each do | key, value |
206
+ if key.is_a?(Symbol)
207
+ key_type = @type_by_name.fetch(key) { raise IndexError, "Unknown keyword: #{key.inspect}" }
208
+ record.put_param(key_type.code, pack(value))
209
+ elsif key.is_a?(AEM::AETypeBase)
210
+ record.put_param(key.code, pack(value))
211
+ else
212
+ if usrf == nil
213
+ usrf = AE::AEDesc.new_list(false)
214
+ end
215
+ usrf.put_item(0, pack(key))
216
+ usrf.put_item(0, pack(value))
217
+ end
218
+ end
219
+ if usrf
220
+ record.put_param(KAE::KeyASUserRecordFields, usrf)
221
+ end
222
+ return record
223
+ end
224
+
225
+ def unpack_type(desc)
226
+ aem_value = super(desc)
227
+ return @type_by_code.fetch(aem_value.code, aem_value)
228
+ end
229
+
230
+ def unpack_enumerated(desc)
231
+ aem_value = super(desc)
232
+ return @type_by_code.fetch(aem_value.code, aem_value)
233
+ end
234
+
235
+ def unpack_property(desc)
236
+ aem_value = super(desc)
237
+ return @type_by_code.fetch(aem_value.code, aem_value)
238
+ end
239
+
240
+ def unpack_aerecord(desc)
241
+ dct = {}
242
+ desc.length().times do |i|
243
+ key, value = desc.get_item(i + 1, KAE::TypeWildCard)
244
+ if key == KAE::KeyASUserRecordFields
245
+ lst = unpack_aelist(value)
246
+ (lst.length / 2).times do |j|
247
+ dct[lst[j * 2]] = lst[j * 2 + 1]
248
+ end
249
+ else
250
+ dct[@type_by_code.fetch(key) { AEM::AEType.new(key) }] = unpack(value)
251
+ end
252
+ end
253
+ return dct
254
+ end
255
+
256
+ def unpack_object_specifier(desc)
257
+ return Reference.new(self, @reference_codecs.unpack(desc))
258
+ end
259
+
260
+ def unpack_insertion_loc(desc)
261
+ return Reference.new(self, @reference_codecs.unpack(desc))
262
+ end
263
+
264
+ def unpack_contains_comp_descriptor(op1, op2)
265
+ if op1.is_a?(Appscript::Reference) and op1.AS_aem_reference.AEM_root == AEMReference::Its
266
+ return op1.contains(op2)
267
+ else
268
+ return super
269
+ end
270
+ end
271
+
272
+ def unpack_unknown(desc)
273
+ return case desc.type
274
+ when KAE::TypeApplicationBundleID
275
+ Appscript.app.by_id(desc.data)
276
+ when KAE::TypeApplicationURL
277
+ if desc.data[0, 4] == 'file' # workaround for converting AEAddressDescs containing file:// URLs to application paths, since AEAddressDescs containing file URLs don't seem to work correctly
278
+ Appscript.app(MacTypes::FileURL.url(desc.data).path)
279
+ else # presumably contains an eppc:// URL
280
+ Appscript.app.by_url(desc.data)
281
+ end
282
+ when KAE::TypeApplSignature
283
+ Appscript.app.by_creator(AEM::Codecs.four_char_code(desc.data))
284
+ when KAE::TypeKernelProcessID
285
+ Appscript.app.by_pid(desc.data.unpack('L')[0])
286
+ when KAE::TypeMachPort, KAE::TypeProcessSerialNumber
287
+ Appscript.app.by_aem_app(AEM::Application.by_desc(desc))
288
+ else
289
+ super
290
+ end
291
+ end
292
+ end
293
+
294
+
295
+ ######################################################################
296
+ # GENERIC REFERENCE
297
+ ######################################################################
298
+
299
+ class GenericReference < AS_SafeObject
300
+
301
+ attr_reader :_call
302
+ protected :_call
303
+
304
+ def initialize(call)
305
+ super()
306
+ @_call = call
307
+ end
308
+
309
+ def ==(v)
310
+ return (self.class == v.class and @_call == v._call)
311
+ end
312
+
313
+ alias_method :eql?, :==
314
+
315
+ def hash
316
+ return @_call.hash
317
+ end
318
+
319
+ def method_missing(name, *args)
320
+ return Appscript::GenericReference.new(@_call + [[name, args]])
321
+ end
322
+
323
+ def to_s
324
+ s= @_call[0]
325
+ @_call[1, @_call.length].each do |name, args|
326
+ if name == :[]
327
+ if args.length == 1
328
+ s += "[#{args[0].inspect}]"
329
+ else
330
+ s += "[#{args[0].inspect}, #{args[1].inspect}]"
331
+ end
332
+ else
333
+ if args.length > 0
334
+ s += ".#{name.to_s}(#{(args.map { |arg| arg.inspect }).join(', ')})"
335
+ else
336
+ s += ".#{name.to_s}"
337
+ end
338
+ end
339
+ end
340
+ return s
341
+ end
342
+
343
+ def inspect
344
+ return to_s
345
+ end
346
+
347
+ def AS_resolve(app_data)
348
+ ref = Reference.new(app_data, {'app' => AEM.app, 'con' => AEM.con, 'its' => AEM.its}[@_call[0]])
349
+ @_call[1, @_call.length].each do |name, args|
350
+ ref = ref.send(name, *args)
351
+ end
352
+ return ref
353
+ end
354
+ end
355
+
356
+
357
+ ######################################################################
358
+ # REFERENCE
359
+ ######################################################################
360
+
361
+ class Reference < AS_SafeObject
362
+
363
+ # users may occasionally require access to the following for creating workarounds to problem apps
364
+ # note: calling #AS_app_data on a newly created application object will return an AppData instance
365
+ # that is not yet fully initialised, so remember to call its #connect method before use
366
+ attr_reader :AS_aem_reference, :AS_app_data
367
+ attr_writer :AS_aem_reference, :AS_app_data
368
+
369
+ def initialize(app_data, aem_reference)
370
+ super()
371
+ @AS_app_data = app_data
372
+ @AS_aem_reference = aem_reference
373
+ end
374
+
375
+ def _resolve_range_boundary(selector)
376
+ if selector.is_a?(Appscript::GenericReference)
377
+ return selector.AS_resolve(@AS_app_data).AS_aem_reference
378
+ elsif selector.is_a?(Reference)
379
+ return selector.AS_aem_reference
380
+ else
381
+ return selector
382
+ end
383
+ end
384
+
385
+ #######
386
+
387
+ def help(flags='-t')
388
+ return @AS_app_data.help(flags, self)
389
+ end
390
+
391
+ def Reference._pack_uint32(n) # used to pack csig attributes
392
+ return AE::AEDesc.new(KAE::TypeUInt32, [n].pack('L'))
393
+ end
394
+
395
+ # 'csig' attribute flags (see ASRegistry.h; note: there's no option for 'numeric strings' in 10.4)
396
+
397
+ IgnoreEnums = [
398
+ [:case, KAE::KAECaseConsiderMask, KAE::KAECaseIgnoreMask],
399
+ [:diacriticals, KAE::KAEDiacriticConsiderMask, KAE::KAEDiacriticIgnoreMask],
400
+ [:whitespace, KAE::KAEWhiteSpaceConsiderMask, KAE::KAEWhiteSpaceIgnoreMask],
401
+ [:hyphens, KAE::KAEHyphensConsiderMask, KAE::KAEHyphensIgnoreMask],
402
+ [:expansion, KAE::KAEExpansionConsiderMask, KAE::KAEExpansionIgnoreMask],
403
+ [:punctuation, KAE::KAEPunctuationConsiderMask, KAE::KAEPunctuationIgnoreMask],
404
+ ]
405
+
406
+ # default cons, csig attributes
407
+
408
+ DefaultConsiderations = AEM::DefaultCodecs.pack([AEM::AEEnum.new(KAE::KAECase)])
409
+ DefaultConsidersAndIgnores = _pack_uint32(KAE::KAECaseIgnoreMask)
410
+
411
+ ##
412
+
413
+ def _send_command(args, name, code, labelled_arg_terms)
414
+ atts = {KAE::KeySubjectAttr => nil}
415
+ params = {}
416
+ case args.length
417
+ when 0
418
+ keyword_args = {}
419
+ 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
420
+ if args[0].is_a?(Hash)
421
+ keyword_args = args[0]
422
+ else
423
+ params[KAE::KeyDirectObject] = args[0]
424
+ keyword_args = {}
425
+ end
426
+ when 2
427
+ params[KAE::KeyDirectObject], keyword_args = args
428
+ else
429
+ raise ArgumentError, "Too many direct parameters."
430
+ end
431
+ if not keyword_args.is_a?(Hash)
432
+ raise ArgumentError, "Second argument must be a Hash containing zero or more keyword parameters."
433
+ end
434
+ # get user-specified timeout, if any
435
+ timeout = (keyword_args.delete(:timeout) {60}).to_i
436
+ if timeout <= 0
437
+ timeout = KAE::KNoTimeOut
438
+ else
439
+ timeout *= 60
440
+ end
441
+ # default send flags
442
+ send_flags = KAE::KAECanSwitchLayer
443
+ # ignore application's reply?
444
+ send_flags += keyword_args.delete(:wait_reply) == false ? KAE::KAENoReply : KAE::KAEWaitReply
445
+ # add considering/ignoring attributes
446
+ ignore_options = keyword_args.delete(:ignore)
447
+ if ignore_options == nil
448
+ atts[KAE::EnumConsiderations] = DefaultConsiderations
449
+ atts[KAE::EnumConsidsAndIgnores] = DefaultConsidersAndIgnores
450
+ else
451
+ atts[KAE::EnumConsiderations] = ignore_options
452
+ csig = 0
453
+ IgnoreEnums.each do |option, consider_mask, ignore_mask|
454
+ csig += ignore_options.include?(option) ? ignore_mask : consider_mask
455
+ end
456
+ atts[KAE::EnumConsidsAndIgnores] = Reference._pack_uint32(csig)
457
+ end
458
+ # optionally specify return value type
459
+ if keyword_args.has_key?(:result_type)
460
+ params[KAE::KeyAERequestedType] = keyword_args.delete(:result_type)
461
+ end
462
+ # extract labelled parameters, if any
463
+ keyword_args.each do |param_name, param_value|
464
+ param_code = labelled_arg_terms[param_name]
465
+ if param_code == nil
466
+ raise ArgumentError, "Unknown keyword parameter: #{param_name.inspect}"
467
+ end
468
+ params[param_code] = param_value
469
+ end
470
+ # apply special cases
471
+ # 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.
472
+ if @AS_aem_reference != AEM.app # If command is called on a Reference, rather than an Application...
473
+ if code == 'coresetd'
474
+ # if ref.set(...) contains no 'to' argument, use direct argument for 'to' parameter and target reference for direct parameter
475
+ if params.has_key?(KAE::KeyDirectObject) and not params.has_key?(KAE::KeyAEData)
476
+ params[KAE::KeyAEData] = params[KAE::KeyDirectObject]
477
+ params[KAE::KeyDirectObject] = @AS_aem_reference
478
+ elsif not params.has_key?(KAE::KeyDirectObject)
479
+ params[KAE::KeyDirectObject] = @AS_aem_reference
480
+ else
481
+ atts[KAE::KeySubjectAttr] = @AS_aem_reference
482
+ end
483
+ elsif code == 'corecrel'
484
+ # this next bit is a bit tricky:
485
+ # - 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.
486
+ # - 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).
487
+ # 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).
488
+ #
489
+ # if ref.make(...) contains no 'at' argument and target is an insertion reference, use target reference for 'at' parameter...
490
+ if @AS_aem_reference.is_a?(AEMReference::InsertionSpecifier) \
491
+ and not params.has_key?(KAE::KeyAEInsertHere)
492
+ params[KAE::KeyAEInsertHere] = @AS_aem_reference
493
+ else # ...otherwise pack the target reference as the subject attribute
494
+ atts[KAE::KeySubjectAttr] = @AS_aem_reference
495
+ end
496
+ elsif params.has_key?(KAE::KeyDirectObject)
497
+ # if user has already supplied a direct parameter, pack that reference as the subject attribute
498
+ atts[KAE::KeySubjectAttr] = @AS_aem_reference
499
+ else
500
+ # pack that reference as the direct parameter
501
+ params[KAE::KeyDirectObject] = @AS_aem_reference
502
+ end
503
+ end
504
+ # build and send the Apple event, returning its result, if any
505
+ begin
506
+ return @AS_app_data.target.event(code, params, atts,
507
+ KAE::KAutoGenerateReturnID, @AS_app_data).send(timeout, send_flags)
508
+ rescue AEM::EventError => e
509
+ if e.number == -1708 and code == 'ascrnoop'
510
+ return # 'launch' events always return 'not handled' errors; just ignore these
511
+ elsif [-600, -609].include?(e.number) and @AS_app_data.constructor == :by_path
512
+ #
513
+ # Event was sent to a local app for which we no longer have a valid address
514
+ # (i.e. the application has quit since this AEM::Application object was made).
515
+ #
516
+ # - If application is running under a new process id, we just update the
517
+ # AEM::Application object and resend the event.
518
+ #
519
+ # - If application isn't running, then we see if the event being sent is one of
520
+ # those allowed to relaunch the application (i.e. 'run' or 'launch'). If it is, the
521
+ # application is relaunched, the process id updated and the event resent;
522
+ # if not, the error is rethrown.
523
+ #
524
+ if not AEM::Application.process_exists_for_path?(@AS_app_data.identifier)
525
+ if code == 'ascrnoop'
526
+ AEM::Application.launch(@AS_app_data.identifier)
527
+ elsif code != 'aevtoapp'
528
+ raise CommandError.new(self, name, args, e, @AS_app_data)
529
+ end
530
+ end
531
+ # update AEMApplication object's AEAddressDesc
532
+ @AS_app_data.target.reconnect
533
+ # re-send command
534
+ begin
535
+ return @AS_app_data.target.event(code, params, atts,
536
+ KAE::KAutoGenerateReturnID, @AS_app_data).send(timeout, send_flags)
537
+ rescue AEM::EventError => e
538
+ raise CommandError.new(self, name, args, e, @AS_app_data)
539
+ end
540
+ end
541
+ end
542
+ raise CommandError.new(self, name, args, e, @AS_app_data)
543
+ end
544
+
545
+
546
+ #######
547
+ # introspection
548
+
549
+ def respond_to?(name, includePriv=false)
550
+ if Object.respond_to?(name)
551
+ return true
552
+ else
553
+ return @AS_app_data.reference_by_name.has_key?(name.is_a?(String) ? name.intern : name)
554
+ end
555
+ end
556
+
557
+ def methods
558
+ return (Object.instance_methods + @AS_app_data.reference_by_name.keys.collect { |name| name.to_s }).uniq
559
+ end
560
+
561
+ def commands
562
+ return (@AS_app_data.reference_by_name.collect { |name, info| info[0] == :command ? name.to_s : nil }).compact.sort
563
+ end
564
+
565
+ def parameters(command_name)
566
+ if not @AS_app_data.reference_by_name.has_key?(command_name.intern)
567
+ raise ArgumentError, "Command not found: #{command_name}"
568
+ end
569
+ return (@AS_app_data.reference_by_name[command_name.intern][1][1].keys.collect { |name| name.to_s }).sort
570
+ end
571
+
572
+ def properties
573
+ return (@AS_app_data.reference_by_name.collect { |name, info| info[0] == :property ? name.to_s : nil }).compact.sort
574
+ end
575
+
576
+ def elements
577
+ return (@AS_app_data.reference_by_name.collect { |name, info| info[0] == :element ? name.to_s : nil }).compact.sort
578
+ end
579
+
580
+ def keywords
581
+ return (@AS_app_data.type_by_name.collect { |name, code| name.to_s }).sort
582
+ end
583
+
584
+ #######
585
+ # standard object methods
586
+
587
+ def ==(val)
588
+ return (self.class == val.class and @AS_app_data.target == val.AS_app_data.target \
589
+ and @AS_aem_reference == val.AS_aem_reference)
590
+ end
591
+
592
+ alias_method :eql?, :==
593
+
594
+ def hash
595
+ if not defined? @_hash
596
+ @_hash = [@AS_app_data.target, @AS_aem_reference].hash
597
+ end
598
+ return @_hash
599
+ end
600
+
601
+ def to_s
602
+ if not defined? @_to_s
603
+ @_to_s = ReferenceRenderer.render(@AS_app_data, @AS_aem_reference)
604
+ end
605
+ return @_to_s
606
+ end
607
+
608
+ alias_method :inspect, :to_s
609
+
610
+ #######
611
+ # Utility methods
612
+
613
+ def is_running?
614
+ identifier = @AS_app_data.identifier
615
+ case @AS_app_data.constructor
616
+ when :by_path
617
+ return AEM::Application.process_exists_for_path?(identifier)
618
+ when :by_pid
619
+ return AEM::Application.process_exists_for_pid?(identifier)
620
+ when :by_url
621
+ return AEM::Application.process_exists_for_url?(identifier)
622
+ when :by_aem_app
623
+ return AEM::Application.process_exists_for_desc?(identifier.address_desc)
624
+ else # when :current
625
+ return true
626
+ end
627
+ end
628
+
629
+ #######
630
+ # Public properties and methods; these are called by end-user and other clients (e.g. generic references)
631
+
632
+ def method_missing(name, *args)
633
+ selector_type, code = @AS_app_data.reference_by_name[name]
634
+ case selector_type # check if name is a property/element/command name, and if it is handle accordingly
635
+ when :property
636
+ raise ArgumentError, "wrong number of arguments for '#{name}' property (1 for 0)" if args != []
637
+ return Reference.new(@AS_app_data, @AS_aem_reference.property(code))
638
+ when :element
639
+ raise ArgumentError, "wrong number of arguments for '#{name}' elements (1 for 0)" if args != []
640
+ return Reference.new(@AS_app_data, @AS_aem_reference.elements(code))
641
+ when :command
642
+ return _send_command(args, name, code[0], code[1])
643
+ else
644
+ # see if it's a method that has been added to Object class [presumably] at runtime, but excluded
645
+ # by AS_SafeObject to avoid potential conflicts with property/element/command names
646
+ begin
647
+ # Notes:
648
+ # rb-appscript has to prevent arbitrary methods that are added to Ruby's base Object class
649
+ # by client code from automatically appearing in Appscript::Reference as well, as these new
650
+ # methods may inadvertently mask property/element/command names, causing appscript to
651
+ # behave incorrectly. However, once it is confirmed that a given method will not mask an existing
652
+ # property/element/command name, it can be added retrospectively to the Reference instance
653
+ # upon which it was called, which is what happens here.
654
+ #
655
+ # This means that methods such as #pretty_print and #pretty_inspect, which are
656
+ # injected into Object when the 'pp' module is loaded, will still be available in appscript
657
+ # references, even though they are not on AS_SafeObject's official list of permitted methods,
658
+ # *as long as* properties/elements/commands of the same name do not already exist for that
659
+ # reference.
660
+ #
661
+ # Where properties/elements/commands of the same name do already exist, appscript
662
+ # will still defer to those, of course, and this may cause problems for the caller if
663
+ # they were wanting the other behaviour. (But, that's the risk one runs with any sort
664
+ # of subclassing exercise when the contents of the superclass are not known for certain
665
+ # beforehand.) Clients that require access to these methods will need to add their names
666
+ # to the ReservedKeywords list (see _appscript/reservedkeywords.rb) at runtime, thereby
667
+ # forcing appscript to append underscores to the conflicting property/element/command
668
+ # names in order to disambiguate them, and modifying any code that refers to those
669
+ # properties/elements/commands accordingly.
670
+ meth = Object.instance_method(name)
671
+ rescue NameError # message not handled
672
+ msg = "Unknown property, element or command: '#{name}'"
673
+ if @AS_app_data.reference_by_name.has_key?("#{name}_".intern)
674
+ msg += " (Did you mean '#{name}_'?)"
675
+ end
676
+ raise RuntimeError, msg
677
+ end
678
+ return meth.bind(self).call(*args)
679
+ end
680
+ end
681
+
682
+ def [](selector, end_range_selector=nil)
683
+ raise TypeError, "Bad selector: nil not allowed." if selector == nil
684
+ if end_range_selector != nil
685
+ new_ref = @AS_aem_reference.by_range(
686
+ self._resolve_range_boundary(selector),
687
+ self._resolve_range_boundary(end_range_selector))
688
+ else
689
+ case selector
690
+ when String
691
+ new_ref = @AS_aem_reference.by_name(selector)
692
+ when Appscript::GenericReference, Appscript::Reference, AEMReference::Test
693
+ case selector
694
+ when Appscript::GenericReference
695
+ test_clause = selector.AS_resolve(@AS_app_data)
696
+ begin
697
+ test_clause = test_clause.AS_aem_reference
698
+ rescue NoMethodError
699
+ raise ArgumentError, "Not a valid its-based test: #{selector}"
700
+ end
701
+ when Appscript::Reference
702
+ test_clause = selector.AS_aem_reference
703
+ else
704
+ test_clause = selector
705
+ end
706
+ if not test_clause.is_a?(AEMReference::Test)
707
+ raise TypeError, "Not an its-based test: #{selector}"
708
+ end
709
+ new_ref = @AS_aem_reference.by_filter(test_clause)
710
+ else
711
+ new_ref = @AS_aem_reference.by_index(selector)
712
+ end
713
+ end
714
+ return Reference.new(@AS_app_data, new_ref)
715
+ end
716
+
717
+ def first
718
+ return Reference.new(@AS_app_data, @AS_aem_reference.first)
719
+ end
720
+
721
+ def middle
722
+ return Reference.new(@AS_app_data, @AS_aem_reference.middle)
723
+ end
724
+
725
+ def last
726
+ return Reference.new(@AS_app_data, @AS_aem_reference.last)
727
+ end
728
+
729
+ def any
730
+ return Reference.new(@AS_app_data, @AS_aem_reference.any)
731
+ end
732
+
733
+ def beginning
734
+ return Reference.new(@AS_app_data, @AS_aem_reference.beginning)
735
+ end
736
+
737
+ def end
738
+ return Reference.new(@AS_app_data, @AS_aem_reference.end)
739
+ end
740
+
741
+ def before
742
+ return Reference.new(@AS_app_data, @AS_aem_reference.before)
743
+ end
744
+
745
+ def after
746
+ return Reference.new(@AS_app_data, @AS_aem_reference.after)
747
+ end
748
+
749
+ def previous(klass)
750
+ return Reference.new(@AS_app_data, @AS_aem_reference.previous(
751
+ @AS_app_data.type_by_name.fetch(klass).code))
752
+ end
753
+
754
+ def next(klass)
755
+ return Reference.new(@AS_app_data, @AS_aem_reference.next(
756
+ @AS_app_data.type_by_name.fetch(klass).code))
757
+ end
758
+
759
+ def ID(id)
760
+ return Reference.new(@AS_app_data, @AS_aem_reference.by_id(id))
761
+ end
762
+
763
+ # Following methods will be called by its-based generic references
764
+ # 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.
765
+
766
+ def gt(operand)
767
+ return Reference.new(@AS_app_data, @AS_aem_reference.gt(operand))
768
+ end
769
+
770
+ def ge(operand)
771
+ return Reference.new(@AS_app_data, @AS_aem_reference.ge(operand))
772
+ end
773
+
774
+ def eq(operand) # avoid colliding with comparison operators, which are normally used to compare two references
775
+ return Reference.new(@AS_app_data, @AS_aem_reference.eq(operand))
776
+ end
777
+
778
+ def ne(operand)
779
+ return Reference.new(@AS_app_data, @AS_aem_reference.ne(operand))
780
+ end
781
+
782
+ def lt(operand)
783
+ return Reference.new(@AS_app_data, @AS_aem_reference.lt(operand))
784
+ end
785
+
786
+ def le(operand)
787
+ return Reference.new(@AS_app_data, @AS_aem_reference.le(operand))
788
+ end
789
+
790
+ def begins_with(operand)
791
+ return Reference.new(@AS_app_data, @AS_aem_reference.begins_with(operand))
792
+ end
793
+
794
+ def ends_with(operand)
795
+ return Reference.new(@AS_app_data, @AS_aem_reference.ends_with(operand))
796
+ end
797
+
798
+ def contains(operand)
799
+ return Reference.new(@AS_app_data, @AS_aem_reference.contains(operand))
800
+ end
801
+
802
+ def is_in(operand)
803
+ return Reference.new(@AS_app_data, @AS_aem_reference.is_in(operand))
804
+ end
805
+
806
+ def does_not_begin_with(operand)
807
+ return self.begins_with(operand).not
808
+ end
809
+
810
+ def does_not_end_with(operand)
811
+ return self.ends_with(operand).not
812
+ end
813
+
814
+ def does_not_contain(operand)
815
+ return self.contains(operand).not
816
+ end
817
+
818
+ def is_not_in(operand)
819
+ return self.is_in(operand).not
820
+ end
821
+
822
+ def and(*operands)
823
+ return Reference.new(@AS_app_data, @AS_aem_reference.and(*operands))
824
+ end
825
+
826
+ def or(*operands)
827
+ return Reference.new(@AS_app_data, @AS_aem_reference.or(*operands))
828
+ end
829
+
830
+ def not
831
+ return Reference.new(@AS_app_data, @AS_aem_reference.not)
832
+ end
833
+ end
834
+
835
+
836
+ ######################################################################
837
+ # APPLICATION
838
+ ######################################################################
839
+
840
+ class Application < Reference
841
+
842
+ private_class_method :new
843
+
844
+ def _aem_application_class # hook
845
+ return AEM::Application
846
+ end
847
+
848
+ def initialize(constructor, identifier, terms)
849
+ super(AppData.new(_aem_application_class, constructor, identifier, terms), AEM.app)
850
+ end
851
+
852
+ # constructors
853
+
854
+ def Application.by_name(name, terms=true)
855
+ return new(:by_path, FindApp.by_name(name), terms)
856
+ end
857
+
858
+ def Application.by_id(id, terms=true)
859
+ return new(:by_path, FindApp.by_id(id), terms)
860
+ end
861
+
862
+ def Application.by_creator(creator, terms=true)
863
+ return new(:by_path, FindApp.by_creator(creator), terms)
864
+ end
865
+
866
+ def Application.by_pid(pid, terms=true)
867
+ return new(:by_pid, pid, terms)
868
+ end
869
+
870
+ def Application.by_url(url, terms=true)
871
+ return new(:by_url, url, terms)
872
+ end
873
+
874
+ def Application.by_aem_app(aem_app, terms=true)
875
+ return new(:by_aem_app, aem_app, terms)
876
+ end
877
+
878
+ def Application.current(terms=true)
879
+ return new(:current, nil, terms)
880
+ end
881
+
882
+ #
883
+
884
+ def AS_new_reference(ref)
885
+ if ref.is_a?(Appscript::GenericReference)
886
+ return ref.AS_resolve(@AS_app_data)
887
+ elsif ref.is_a?(AEMReference::Query)
888
+ return Reference.new(@AS_app_data, ref)
889
+ elsif ref == nil
890
+ return Reference.new(@AS_app_data, AEM.app)
891
+ else
892
+ return Reference.new(@AS_app_data, AEM.custom_root(ref))
893
+ end
894
+ end
895
+
896
+ def begin_transaction(session=nil)
897
+ @AS_app_data.target.begin_transaction(session)
898
+ end
899
+
900
+ def abort_transaction
901
+ @AS_app_data.target.abort_transaction
902
+ end
903
+
904
+ def end_transaction
905
+ @AS_app_data.target.end_transaction
906
+ end
907
+
908
+ def launch
909
+ if @AS_app_data.constructor == :by_path
910
+ AEM::Application.launch(@AS_app_data.identifier)
911
+ @AS_app_data.target.reconnect
912
+ else
913
+ begin
914
+ @AS_app_data.target.event('ascrnoop').send # will send launch event to app if already running; else will error
915
+ rescue AEM::EventError => e
916
+ raise if e.to_i != -1708
917
+ end
918
+ end
919
+ end
920
+ end
921
+
922
+ ##
923
+
924
+ class GenericApplication < GenericReference
925
+
926
+ def initialize(app_class)
927
+ @_app_class = app_class
928
+ super(['app'])
929
+ end
930
+
931
+ def by_name(name, terms=true)
932
+ return @_app_class.by_name(name, terms)
933
+ end
934
+
935
+ def by_id(id, terms=true)
936
+ return @_app_class.by_id(id, terms)
937
+ end
938
+
939
+ def by_creator(creator, terms=true)
940
+ return @_app_class.by_creator(creator, terms)
941
+ end
942
+
943
+ def by_pid(pid, terms=true)
944
+ return @_app_class.by_pid(pid, terms)
945
+ end
946
+
947
+ def by_url(url, terms=true)
948
+ return @_app_class.by_url(url, terms)
949
+ end
950
+
951
+ def by_aem_app(aem_app, terms=true)
952
+ return @_app_class.by_aem_app(aem_app, terms)
953
+ end
954
+
955
+ def current(terms=true)
956
+ return @_app_class.current(terms)
957
+ end
958
+ end
959
+
960
+ #######
961
+
962
+ AS_App = Appscript::GenericApplication.new(Application)
963
+ AS_Con = Appscript::GenericReference.new(['con'])
964
+ AS_Its = Appscript::GenericReference.new(['its'])
965
+
966
+
967
+ ######################################################################
968
+ # REFERENCE ROOTS
969
+ ######################################################################
970
+ # public (note: Application & GenericApplication classes may also be accessed if subclassing Application class is required)
971
+
972
+ def Appscript.app(*args)
973
+ if args == []
974
+ return AS_App
975
+ else
976
+ return AS_App.by_name(*args)
977
+ end
978
+ end
979
+
980
+ def Appscript.con
981
+ return AS_Con
982
+ end
983
+
984
+ def Appscript.its
985
+ return AS_Its
986
+ end
987
+
988
+ # also define app, con, its as instance methods so that clients can 'include Appscript'
989
+
990
+ def app(*args)
991
+ if args == []
992
+ return AS_App
993
+ else
994
+ return AS_App.by_name(*args)
995
+ end
996
+ end
997
+
998
+ def con
999
+ return AS_Con
1000
+ end
1001
+
1002
+ def its
1003
+ return AS_Its
1004
+ end
1005
+
1006
+
1007
+ ######################################################################
1008
+ # COMMAND ERROR
1009
+ ######################################################################
1010
+ # public
1011
+
1012
+ class CommandError < RuntimeError
1013
+
1014
+ attr_reader :reference, :name, :parameters, :real_error
1015
+
1016
+ def initialize(reference, command_name, parameters, real_error, codecs)
1017
+ @reference, @command_name, @parameters = reference, command_name, parameters
1018
+ @real_error, @codecs = real_error, codecs
1019
+ super()
1020
+ end
1021
+
1022
+ def to_s
1023
+ if @real_error.is_a?(AEM::EventError)
1024
+ err = "CommandError\n\t\tOSERROR: #{error_number}"
1025
+ err += "\n\t\tMESSAGE: #{error_message}" if error_message != ''
1026
+ [
1027
+ ["\n\t\tOFFENDING OBJECT", KAE::KOSAErrorOffendingObject],
1028
+ ["\n\t\tEXPECTED TYPE", KAE::KOSAErrorExpectedType],
1029
+ ["\n\t\tPARTIAL RESULT", KAE::KOSAErrorPartialResult],
1030
+ ].each do |label, key|
1031
+ desc = @real_error.raw[key]
1032
+ err += "#{label}: #{@codecs.unpack(desc).inspect}" if desc
1033
+ end
1034
+ else
1035
+ err = @real_error
1036
+ end
1037
+ return "#{err}\n\t\tCOMMAND: #{@reference}.#{@command_name}(#{(@parameters.collect { |item| item.inspect }).join(', ')})\n"
1038
+ end
1039
+
1040
+ def error_number
1041
+ if @real_error.is_a?(AE::MacOSError) or @real_error.is_a?(AEM::EventError)
1042
+ return @real_error.to_i
1043
+ else
1044
+ return -2700
1045
+ end
1046
+ end
1047
+
1048
+ alias_method :to_i, :error_number
1049
+
1050
+ def error_message
1051
+ return @real_error.message
1052
+ end
1053
+
1054
+ def offending_object
1055
+ return nil if not @real_error.is_a?(AEM::EventError)
1056
+ desc = @real_error.raw[KAE::KOSAErrorOffendingObject]
1057
+ return desc ? @codecs.unpack(desc) : nil
1058
+ end
1059
+
1060
+ def expected_type
1061
+ return nil if not @real_error.is_a?(AEM::EventError)
1062
+ desc = @real_error.raw[KAE::KOSAErrorExpectedType]
1063
+ return desc ? @codecs.unpack(desc) : nil
1064
+ end
1065
+
1066
+ def partial_result
1067
+ return nil if not @real_error.is_a?(AEM::EventError)
1068
+ desc = @real_error.raw[KAE::KOSAErrorPartialResult]
1069
+ return desc ? @codecs.unpack(desc) : nil
1070
+ end
1071
+ end
1072
+
1073
+ ApplicationNotFoundError = FindApp::ApplicationNotFoundError
1074
+ CantLaunchApplicationError = Connect::CantLaunchApplicationError
1075
+ end