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,314 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
# Copyright (C) 2006 HAS.
|
3
|
+
# Released under MIT License.
|
4
|
+
|
5
|
+
######################################################################
|
6
|
+
# TERMINOLOGY PARSER
|
7
|
+
######################################################################
|
8
|
+
|
9
|
+
module TerminologyParser
|
10
|
+
|
11
|
+
require "_appscript/reservedkeywords" # names of all existing methods on ASReference::Application
|
12
|
+
|
13
|
+
class BigEndianParser
|
14
|
+
@@_name_cache = {}
|
15
|
+
LegalFirst = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
|
16
|
+
LegalRest = LegalFirst + '0123456789'
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@enumerators = {}
|
20
|
+
@properties = {}
|
21
|
+
@commands = {}
|
22
|
+
@_plural_class_names = {}
|
23
|
+
@_singular_class_names = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def _integer
|
27
|
+
@_ptr += 2
|
28
|
+
return @_str[@_ptr - 2, 2].unpack('S')[0]
|
29
|
+
end
|
30
|
+
|
31
|
+
def _word
|
32
|
+
@_ptr += 4
|
33
|
+
return @_str[@_ptr - 4, 4]
|
34
|
+
end
|
35
|
+
|
36
|
+
def _name
|
37
|
+
count = @_str[@_ptr]
|
38
|
+
@_ptr += 1 + count
|
39
|
+
s = @_str[@_ptr - count, count]
|
40
|
+
if not @@_name_cache.has_key?(s)
|
41
|
+
legal = LegalFirst
|
42
|
+
res = ''
|
43
|
+
s.split(//).each do |c|
|
44
|
+
if legal[c]
|
45
|
+
res += c
|
46
|
+
else
|
47
|
+
case c
|
48
|
+
when ' ', '-', '/'
|
49
|
+
res += '_'
|
50
|
+
when '&'
|
51
|
+
res += 'and'
|
52
|
+
else
|
53
|
+
if res == ''
|
54
|
+
res = '_'
|
55
|
+
end
|
56
|
+
res += "0x#{c.unpack('HXh')}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
legal = LegalRest
|
60
|
+
end
|
61
|
+
if res[0, 3] == 'AS_' or ReservedKeywords.include?(res) or res[0, 1] == '_'
|
62
|
+
res += '_'
|
63
|
+
end
|
64
|
+
@@_name_cache[s] = res
|
65
|
+
end
|
66
|
+
return @@_name_cache[s]
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
|
71
|
+
def parse_command
|
72
|
+
name = _name
|
73
|
+
@_ptr += 1 + @_str[@_ptr]
|
74
|
+
@_ptr += @_ptr & 1
|
75
|
+
code = _word + _word
|
76
|
+
# skip result
|
77
|
+
@_ptr += 4
|
78
|
+
@_ptr += 1 + @_str[@_ptr]
|
79
|
+
@_ptr += @_ptr & 1
|
80
|
+
@_ptr += 2
|
81
|
+
# skip direct
|
82
|
+
@_ptr += 4
|
83
|
+
@_ptr += 1 + @_str[@_ptr]
|
84
|
+
@_ptr += @_ptr & 1
|
85
|
+
@_ptr += 2
|
86
|
+
#
|
87
|
+
current_command_args = []
|
88
|
+
@commands[code] = [name, code, current_command_args]
|
89
|
+
# args
|
90
|
+
_integer.times do
|
91
|
+
parameter_name = _name
|
92
|
+
@_ptr += @_ptr & 1
|
93
|
+
parameter_code = _word
|
94
|
+
@_ptr += 4
|
95
|
+
@_ptr += 1 + @_str[@_ptr]
|
96
|
+
@_ptr += @_ptr & 1
|
97
|
+
@_ptr += 2
|
98
|
+
current_command_args.push([parameter_name, parameter_code])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def parse_class
|
103
|
+
name = _name
|
104
|
+
@_ptr += @_ptr & 1
|
105
|
+
code = _word
|
106
|
+
@_ptr += 1 + @_str[@_ptr]
|
107
|
+
@_ptr += @_ptr & 1
|
108
|
+
is_plural = false
|
109
|
+
_integer.times do
|
110
|
+
propname = _name
|
111
|
+
@_ptr += @_ptr & 1
|
112
|
+
propcode = _word
|
113
|
+
@_ptr += 4
|
114
|
+
@_ptr += 1 + @_str[@_ptr]
|
115
|
+
@_ptr += @_ptr & 1
|
116
|
+
flags = _integer
|
117
|
+
if propcode != 'c@#^'
|
118
|
+
if flags & 1 == 1
|
119
|
+
is_plural = true
|
120
|
+
else
|
121
|
+
@properties[propcode] = [propname, propcode]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
_integer.times do
|
126
|
+
@_ptr += 4
|
127
|
+
count = _integer
|
128
|
+
@_ptr += 4 * count
|
129
|
+
end
|
130
|
+
if is_plural
|
131
|
+
@_plural_class_names[code] = [name, code]
|
132
|
+
else
|
133
|
+
@_singular_class_names[code] = [name, code]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse_comparison
|
138
|
+
@_ptr += 1 + @_str[@_ptr]
|
139
|
+
@_ptr += @_ptr & 1
|
140
|
+
@_ptr += 4
|
141
|
+
@_ptr += 1 + @_str[@_ptr]
|
142
|
+
@_ptr += @_ptr & 1
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse_enumeration
|
146
|
+
@_ptr += 4
|
147
|
+
_integer.times do
|
148
|
+
name = _name
|
149
|
+
@_ptr += @_ptr & 1
|
150
|
+
code = _word
|
151
|
+
@_ptr += 1 + @_str[@_ptr]
|
152
|
+
@_ptr += @_ptr & 1
|
153
|
+
@enumerators[code + name] = [name, code]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def parse_suite
|
158
|
+
@_ptr += 1 + @_str[@_ptr]
|
159
|
+
@_ptr += 1 + @_str[@_ptr]
|
160
|
+
@_ptr += @_ptr & 1
|
161
|
+
@_ptr += 4
|
162
|
+
@_ptr += 4
|
163
|
+
_integer.times { parse_command }
|
164
|
+
_integer.times { parse_class }
|
165
|
+
_integer.times { parse_comparison }
|
166
|
+
_integer.times { parse_enumeration }
|
167
|
+
end
|
168
|
+
|
169
|
+
def parse(aetes)
|
170
|
+
aetes.each do |aete|
|
171
|
+
@_str = aete.data
|
172
|
+
@_ptr = 6
|
173
|
+
_integer.times { parse_suite }
|
174
|
+
if not @_ptr == @_str.length
|
175
|
+
raise RuntimeError, "aete was not fully parsed."
|
176
|
+
end
|
177
|
+
end
|
178
|
+
classes = @_plural_class_names.clone
|
179
|
+
classes.update(@_singular_class_names)
|
180
|
+
elements = @_singular_class_names.clone
|
181
|
+
elements.update(@_plural_class_names)
|
182
|
+
return [classes, @enumerators, @properties, elements, @commands].map! { |d| d.values }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
class LittleEndianParser < BigEndianParser
|
188
|
+
def _word
|
189
|
+
return super.reverse
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
#######
|
195
|
+
# Public
|
196
|
+
|
197
|
+
def TerminologyParser.build_tables_for_aetes(aetes)
|
198
|
+
if [1].pack('S') == "\000\001"
|
199
|
+
return BigEndianParser.new.parse(aetes)
|
200
|
+
else
|
201
|
+
return LittleEndianParser.new.parse(aetes)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
######################################################################
|
209
|
+
# TERMINOLOGY TABLES BUILDER
|
210
|
+
######################################################################
|
211
|
+
|
212
|
+
module Terminology
|
213
|
+
|
214
|
+
require "aem"
|
215
|
+
require "_appscript/defaultterminology"
|
216
|
+
|
217
|
+
@@_terminology_cache = {}
|
218
|
+
|
219
|
+
def Terminology._make_type_table(classes, enums, properties)
|
220
|
+
type_by_code = DefaultTerminology::TypeByCode.clone
|
221
|
+
type_by_name = DefaultTerminology::TypeByName.clone
|
222
|
+
[[AEM::AEType, properties], [AEM::AEEnum, enums], [AEM::AEType, classes]].each do |klass, table|
|
223
|
+
table.each do |name, code|
|
224
|
+
if DefaultTerminology::TypeByName.has_key?(name) and \
|
225
|
+
DefaultTerminology::TypeByName[name].code != code
|
226
|
+
name += '_'
|
227
|
+
end
|
228
|
+
type_by_code[code] = name.intern
|
229
|
+
type_by_name[name.intern] = klass.new(code)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
return [type_by_code, type_by_name]
|
233
|
+
end
|
234
|
+
|
235
|
+
def Terminology._make_reference_table(properties, elements, commands)
|
236
|
+
reference_by_code = DefaultTerminology::ReferenceByCode.clone
|
237
|
+
reference_by_name = DefaultTerminology::ReferenceByName.clone
|
238
|
+
[[:element, elements, 'e'], [:property, properties, 'p']].each do |kind, table, prefix|
|
239
|
+
# note: if property and element names are same (e.g. 'file' in BBEdit), will pack as property specifier unless it's a special case (i.e. see :text below). Note that there is currently no way to override this, i.e. to force appscript to pack it as an all-elements specifier instead (in AS, this would be done by prepending the 'every' keyword), so clients would need to use aem for that (but could add an 'all' method to Reference class if there was demand for a built-in workaround)
|
240
|
+
table.each do |name, code|
|
241
|
+
reference_by_code[prefix + code] = name
|
242
|
+
reference_by_name[name.intern] = [kind, code]
|
243
|
+
end
|
244
|
+
end
|
245
|
+
if reference_by_name.has_key?(:text) # special case: AppleScript always packs 'text of...' as all-elements specifier
|
246
|
+
reference_by_name[:text][0] = :element
|
247
|
+
end
|
248
|
+
commands.reverse.each do |name, code, args|
|
249
|
+
if DefaultTerminology::DefaultCommands.has_key?(name) and \
|
250
|
+
code != DefaultTerminology::DefaultCommands[name]
|
251
|
+
name += '_'
|
252
|
+
end
|
253
|
+
dct = {}
|
254
|
+
args.each { |arg_name, arg_code| dct[arg_name.intern] = arg_code }
|
255
|
+
reference_by_name[name.intern] = [:command, [code, dct]]
|
256
|
+
end
|
257
|
+
return reference_by_code, reference_by_name
|
258
|
+
end
|
259
|
+
|
260
|
+
#######
|
261
|
+
# public
|
262
|
+
|
263
|
+
def Terminology.default_tables
|
264
|
+
return _make_type_table([], [], []) + _make_reference_table([], [], [])
|
265
|
+
end
|
266
|
+
|
267
|
+
def Terminology.tables_for_aetes(aetes)
|
268
|
+
classes, enums, properties, elements, commands = TerminologyParser.build_tables_for_aetes(aetes.delete_if { |aete| not (aete.is_a?(AE::AEDesc) and aete.type == 'aete') })
|
269
|
+
return _make_type_table(classes, enums, properties) + _make_reference_table(properties, elements, commands)
|
270
|
+
end
|
271
|
+
|
272
|
+
##
|
273
|
+
|
274
|
+
def Terminology.tables_for_module(terms)
|
275
|
+
if terms::Version != 1.1
|
276
|
+
raise RuntimeError, "Unsupported terminology module version: #{terms::Version} (requires version 1.1)."
|
277
|
+
end
|
278
|
+
return _make_type_table(terms::Classes, terms::Enumerators, terms::Properties) \
|
279
|
+
+ _make_reference_table(terms::Properties, terms::Elements, terms::Commands)
|
280
|
+
end
|
281
|
+
|
282
|
+
def Terminology.tables_for_app(path, pid, url)
|
283
|
+
if not @@_terminology_cache.has_key?([path, pid, url])
|
284
|
+
begin
|
285
|
+
if path
|
286
|
+
app = AEM::Application.by_path(path)
|
287
|
+
elsif pid
|
288
|
+
app = AEM::Application.by_pid(pid)
|
289
|
+
elsif url
|
290
|
+
app = AEM::Application.by_url(url)
|
291
|
+
else
|
292
|
+
app = AEM::Application.current
|
293
|
+
end
|
294
|
+
begin
|
295
|
+
aetes = app.event('ascrgdte', {'----' => 0}).send(30 * 60)
|
296
|
+
rescue AEM::CommandError => e
|
297
|
+
if e.number == -192 # aete resource not found
|
298
|
+
aetes = []
|
299
|
+
else
|
300
|
+
raise
|
301
|
+
end
|
302
|
+
end
|
303
|
+
if not aetes.is_a?(Array)
|
304
|
+
aetes = [aetes]
|
305
|
+
end
|
306
|
+
rescue => err
|
307
|
+
raise RuntimeError, "Can't get terminology for application (#{path or pid or url}): #{err}"
|
308
|
+
end
|
309
|
+
@@_terminology_cache[[path, pid, url]] = Terminology.tables_for_aetes(aetes)
|
310
|
+
end
|
311
|
+
return @@_terminology_cache[[path, pid, url]]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
data/src/lib/aem.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
# Copyright (C) 2006 HAS.
|
3
|
+
# Released under MIT License.
|
4
|
+
|
5
|
+
require "ae"
|
6
|
+
require "kae"
|
7
|
+
require "_aem/findapp"
|
8
|
+
require "_aem/mactypes"
|
9
|
+
|
10
|
+
module AEM
|
11
|
+
|
12
|
+
# Mid-level wrapper for building and sending Apple events to local and remote applications.
|
13
|
+
|
14
|
+
require "_aem/codecs"
|
15
|
+
require "_aem/aemreference"
|
16
|
+
require "_aem/typewrappers"
|
17
|
+
require "_aem/connect"
|
18
|
+
require "_aem/send"
|
19
|
+
|
20
|
+
#######
|
21
|
+
# Constants
|
22
|
+
|
23
|
+
Codecs = Codecs
|
24
|
+
DefaultCodecs = DefaultCodecs
|
25
|
+
MacOSError = AE::MacOSError
|
26
|
+
|
27
|
+
AEDesc = AE::AEDesc
|
28
|
+
|
29
|
+
AETypeBase = TypeWrappers::AETypeBase
|
30
|
+
AEType = TypeWrappers::AEType
|
31
|
+
AEEnum = TypeWrappers::AEEnum
|
32
|
+
AEProp = TypeWrappers::AEProp
|
33
|
+
AEKey = TypeWrappers::AEKey
|
34
|
+
|
35
|
+
CommandError = Send::CommandError
|
36
|
+
|
37
|
+
#######
|
38
|
+
# Reference roots
|
39
|
+
|
40
|
+
def AEM.app
|
41
|
+
return AEMReference::App
|
42
|
+
end
|
43
|
+
|
44
|
+
def AEM.con
|
45
|
+
return AEMReference::Con
|
46
|
+
end
|
47
|
+
|
48
|
+
def AEM.its
|
49
|
+
return AEMReference::Its
|
50
|
+
end
|
51
|
+
|
52
|
+
#######
|
53
|
+
# Application class
|
54
|
+
|
55
|
+
class Application
|
56
|
+
# Identifies an application and provides an #event method for constructing Apple events targetted at it.
|
57
|
+
|
58
|
+
require "weakref"
|
59
|
+
|
60
|
+
private_class_method :new
|
61
|
+
attr_reader :hash, :identity
|
62
|
+
protected :identity
|
63
|
+
|
64
|
+
#######
|
65
|
+
# Workaround for lack of proper destructors in Ruby; see #initialize method.
|
66
|
+
|
67
|
+
@@_app_number_count = 0
|
68
|
+
@@_transaction_ids_by_app_no = {}
|
69
|
+
|
70
|
+
#######
|
71
|
+
|
72
|
+
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.
|
73
|
+
|
74
|
+
#######
|
75
|
+
|
76
|
+
def initialize(path, address_desc, identity)
|
77
|
+
# called by constructor method
|
78
|
+
# path is used by #reconnect
|
79
|
+
# address_desc is an AEAddressDesc identifying the target application
|
80
|
+
# identity is used by #inspect, #hash, #==
|
81
|
+
@_transaction = KAE::KAnyTransactionID
|
82
|
+
@_path = path
|
83
|
+
@_address = address_desc
|
84
|
+
@identity = identity
|
85
|
+
@hash = identity.hash
|
86
|
+
# 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.
|
87
|
+
@app_number = app_number = (@@_app_number_count += 1)
|
88
|
+
@@_transaction_ids_by_app_no[app_number] = @_transaction
|
89
|
+
ObjectSpace.define_finalizer(WeakRef.new(self), proc do
|
90
|
+
transaction_id = @@_transaction_ids_by_app_no.delete(app_number)
|
91
|
+
if transaction_id != KAE::KAnyTransactionID
|
92
|
+
self.class::Event.new(@_address, 'miscendt', {}, {}, transaction_id).send(60, KAE::KAENoReply)
|
93
|
+
end
|
94
|
+
end)
|
95
|
+
end
|
96
|
+
|
97
|
+
#######
|
98
|
+
# utility class methods; placed here for convenience
|
99
|
+
|
100
|
+
def Application.launch(path)
|
101
|
+
# Launches a local application without sending it the usual 'run' event (aevtoapp).
|
102
|
+
Connect.launch_app(path)
|
103
|
+
end
|
104
|
+
|
105
|
+
def Application.is_running?(path)
|
106
|
+
# Checks if a local application is running.
|
107
|
+
return Connect.is_running?(path)
|
108
|
+
end
|
109
|
+
|
110
|
+
#######
|
111
|
+
# constructors
|
112
|
+
|
113
|
+
def Application.by_path(path)
|
114
|
+
# path : string -- full path to local application
|
115
|
+
#
|
116
|
+
# Note: application will be launched if not already running.
|
117
|
+
return new(path, Connect.local_app(path), [:path, path])
|
118
|
+
end
|
119
|
+
|
120
|
+
def Application.by_url(url)
|
121
|
+
# url : string -- eppc URL for remote process
|
122
|
+
return new(nil, Connect.remote_app(url), [:url, url])
|
123
|
+
end
|
124
|
+
|
125
|
+
def Application.by_pid(pid)
|
126
|
+
# pid : integer -- Unix process id
|
127
|
+
return new(nil, Connect.local_app_by_pid(pid), [:pid, pid])
|
128
|
+
end
|
129
|
+
|
130
|
+
def Application.by_desc(desc)
|
131
|
+
# desc : AEDesc -- an AEAddressDesc
|
132
|
+
return new(nil, desc, [:desc, desc.type, desc.data])
|
133
|
+
end
|
134
|
+
|
135
|
+
def Application.current
|
136
|
+
return new(nil, Connect::CurrentApp, [:current])
|
137
|
+
end
|
138
|
+
|
139
|
+
#######
|
140
|
+
# methods
|
141
|
+
|
142
|
+
def inspect
|
143
|
+
if @identity[0] == :current
|
144
|
+
return 'AEM::Application.current'
|
145
|
+
else
|
146
|
+
con_name = {:path => 'by_path', :url => 'by_url', :pid => 'by_pid', :desc => 'by_desc'}[@identity[0]]
|
147
|
+
return "AEM::Application.#{con_name}(#{@identity[1].inspect})"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
alias_method :to_s, :inspect
|
152
|
+
|
153
|
+
def ==(val)
|
154
|
+
return (self.class == val.class and @identity == val.identity)
|
155
|
+
end
|
156
|
+
|
157
|
+
alias_method :eql?, :==
|
158
|
+
|
159
|
+
# (hash method is provided by attr_reader :hash)
|
160
|
+
|
161
|
+
def reconnect
|
162
|
+
# If application has quit since this Application object was created, its AEAddressDesc
|
163
|
+
# is no longer valid so this Application object will not work even when application is restarted.
|
164
|
+
# #reconnect will update this Application object's AEAddressDesc so it's valid again.
|
165
|
+
#
|
166
|
+
# Note that this only works for Application objects created via the by_path constructor.
|
167
|
+
# Also note that any Event objects created prior to calling #reconnect will still be invalid.
|
168
|
+
if @_path
|
169
|
+
@_address = Connect.local_app(@_path)
|
170
|
+
end
|
171
|
+
return
|
172
|
+
end
|
173
|
+
|
174
|
+
def event(event, params={}, atts={}, return_id=KAE::KAutoGenerateReturnID, codecs=DefaultCodecs)
|
175
|
+
# Construct an Apple event targetted at this application.
|
176
|
+
# event : string -- 8-letter code indicating event's class, e.g. 'coregetd'
|
177
|
+
# params : hash -- a dict of form {AE_code:anything,...} containing zero or more event parameters (message arguments)
|
178
|
+
# atts : hash -- a dict of form {AE_code:anything,...} containing zero or more event attributes (event info)
|
179
|
+
# return_id : integer -- reply event's ID
|
180
|
+
# codecs : Codecs -- codecs object to use when packing/unpacking this event
|
181
|
+
return self.class::Event.new(@_address, event, params, atts, @_transaction, return_id, codecs)
|
182
|
+
end
|
183
|
+
|
184
|
+
def start_transaction(session=nil)
|
185
|
+
# Start a new transaction.
|
186
|
+
if @_transaction != KAE::KAnyTransactionID
|
187
|
+
raise RuntimeError, "Transaction is already active."
|
188
|
+
end
|
189
|
+
@_transaction = self.class::Event.new(@_address, 'miscbegi', session != nil ? {'----' => session} : {}).send
|
190
|
+
@@_transaction_ids_by_app_no[@app_number] = @_transaction
|
191
|
+
return
|
192
|
+
end
|
193
|
+
|
194
|
+
def abort_transaction
|
195
|
+
# Abort the current transaction.
|
196
|
+
if @_transaction == KAE::KAnyTransactionID
|
197
|
+
raise RuntimeError, "No transaction is active."
|
198
|
+
end
|
199
|
+
self.class::Event.new(@_address, 'miscttrm', {}, {}, @_transaction).send
|
200
|
+
@_transaction = KAE::KAnyTransactionID
|
201
|
+
@@_transaction_ids_by_app_no[@app_number] = KAE::KAnyTransactionID
|
202
|
+
return
|
203
|
+
end
|
204
|
+
|
205
|
+
def end_transaction
|
206
|
+
# End the current transaction.
|
207
|
+
if @_transaction == KAE::KAnyTransactionID
|
208
|
+
raise RuntimeError, "No transaction is active."
|
209
|
+
end
|
210
|
+
self.class::Event.new(@_address, 'miscendt', {}, {}, @_transaction).send
|
211
|
+
@_transaction = KAE::KAnyTransactionID
|
212
|
+
@@_transaction_ids_by_app_no[@app_number] = KAE::KAnyTransactionID
|
213
|
+
return
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|