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