rb-appscript 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +37 -0
- data/LICENSE +6 -2
- data/README +7 -4
- data/TODO +6 -2
- data/doc/aem-manual/01_introduction.html +1 -1
- data/doc/aem-manual/02_apioverview.html +1 -1
- data/doc/aem-manual/03_packingandunpackingdata.html +3 -3
- data/doc/aem-manual/04_references.html +18 -13
- data/doc/aem-manual/05_targettingapplications.html +33 -13
- data/doc/aem-manual/06_buildingandsendingevents.html +1 -1
- data/doc/aem-manual/07_findapp.html +1 -1
- data/doc/aem-manual/08_examples.html +1 -1
- data/doc/aem-manual/index.html +1 -1
- data/doc/appscript-manual/01_introduction.html +1 -1
- data/doc/appscript-manual/02_aboutappscripting.html +4 -4
- data/doc/appscript-manual/03_quicktutorial.html +1 -1
- data/doc/appscript-manual/04_gettinghelp.html +13 -42
- data/doc/appscript-manual/05_keywordconversion.html +1 -1
- data/doc/appscript-manual/06_classesandenums.html +1 -1
- data/doc/appscript-manual/07_applicationobjects.html +19 -1
- data/doc/appscript-manual/08_realvsgenericreferences.html +1 -1
- data/doc/appscript-manual/09_referenceforms.html +1 -1
- data/doc/appscript-manual/10_referenceexamples.html +1 -1
- data/doc/appscript-manual/11_applicationcommands.html +1 -1
- data/doc/appscript-manual/12_commandexamples.html +1 -1
- data/doc/appscript-manual/13_performanceissues.html +1 -1
- data/doc/appscript-manual/14_notes.html +1 -1
- data/doc/appscript-manual/index.html +1 -1
- data/doc/index.html +1 -1
- data/doc/mactypes-manual/index.html +13 -1
- data/doc/osax-manual/index.html +37 -6
- data/extconf.rb +2 -2
- data/rb-appscript.gemspec +1 -1
- data/sample/AB_export_vcard.rb +31 -0
- data/sample/Add_iCal_event.rb +4 -4
- data/src/lib/_aem/connect.rb +88 -8
- data/src/lib/_aem/mactypes.rb +27 -21
- data/src/lib/_appscript/reservedkeywords.rb +1 -0
- data/src/lib/aem.rb +40 -5
- data/src/lib/appscript.rb +104 -30
- data/src/lib/kae.rb +1 -0
- data/src/lib/osax.rb +35 -19
- data/src/rbae.c +115 -1
- data/test/README +3 -1
- data/test/test_appscriptcommands.rb +5 -4
- data/test/test_codecs.rb +7 -1
- data/test/testall.sh +1 -1
- metadata +24 -23
@@ -152,6 +152,24 @@ set(reference, :to => value) -- Set an object's data.
|
|
152
152
|
<p>Appscript identifies running applications by their process ids so it's possible to control multiple versions of an application running at the same time if their Application objects are created using process ids or eppc URLs.</p>
|
153
153
|
|
154
154
|
|
155
|
+
<h3>Checking if an application is running</h3>
|
156
|
+
|
157
|
+
<p>You can check if the application specified by an Application object is currently running by calling its <code>#is_running?</code> method. This is useful if you don't want to perform commands on an application that isn't already running. For example:</p>
|
158
|
+
|
159
|
+
<pre><code>te = app('TextEdit')
|
160
|
+
# Only perform TextEdit-related commands if it's already running:
|
161
|
+
if te.is_running?
|
162
|
+
# all TextEdit-related code goes here...
|
163
|
+
end</code></pre>
|
164
|
+
|
165
|
+
<p class="hilitebox">Remember that appscript automatically launches a non-running application the first time your script makes reference to any of its properties, elements or commands. To avoid accidental launches, <em>all</em> code relating to that application must be included in a conditional block that only executes if <code>#is_running?</code> returns <code>true</code>.</p>
|
166
|
+
|
167
|
+
|
168
|
+
<h3>Launch errors</h3>
|
169
|
+
|
170
|
+
<p>If the application can't be launched for some reason (e.g. if it's in the Trash), an <code>Appscript::CantLaunchApplicationError</code> error will be raised. This provides a description of the problem (if it's a standard LaunchServices error) along with the original OS error number, which you can also obtain via the <code>CantLaunchApplicationError#to_i</code> method.</p>
|
171
|
+
|
172
|
+
|
155
173
|
<h3>Using <code>launch</code> vs <code>run</code></h3>
|
156
174
|
|
157
175
|
<p>When appscript launches a non-running application, it normally sends it a <code>run</code> command as part of the launching process. If you wish to avoid this, you should start the application by sending it a <code>launch</code> command before doing anything else. For example:</p>
|
@@ -186,6 +204,6 @@ te.launch
|
|
186
204
|
</div>
|
187
205
|
|
188
206
|
<!--footer-->
|
189
|
-
<p class="footer">©
|
207
|
+
<p class="footer">© 2007 HAS</p>
|
190
208
|
</body>
|
191
209
|
</html>
|
data/doc/index.html
CHANGED
@@ -39,6 +39,8 @@
|
|
39
39
|
|
40
40
|
Alias.path(path) -- make Alias object from POSIX path string
|
41
41
|
|
42
|
+
Alias.hfs_path(path) -- make Alias object from HFS path string
|
43
|
+
|
42
44
|
Alias.url(url) -- make Alias object from a local file:// URL string
|
43
45
|
|
44
46
|
Alias.desc(desc) -- make Alias object from an AE::AEDesc
|
@@ -53,6 +55,8 @@
|
|
53
55
|
inspect
|
54
56
|
|
55
57
|
path -- returns POSIX path string to the object's current location
|
58
|
+
|
59
|
+
hfs_path -- returns HFS path string to the object's current location
|
56
60
|
|
57
61
|
url -- returns file:// URL string to the object's current location
|
58
62
|
|
@@ -88,6 +92,8 @@ Appscript.app('TextEdit').open(f)
|
|
88
92
|
|
89
93
|
<p>Comparing an <code>Alias</code> object against a <code>FileURL</code> object always returns false, even if both point to the same location.</p>
|
90
94
|
|
95
|
+
<p>Remember that aliases can change when the corresponding filesystem object is moved, so take care when using <code>Alias</code> objects in situations that involve comparing or hashing them (e.g. <code>Hash</code> keys).</p>
|
96
|
+
|
91
97
|
|
92
98
|
<h2><code>MacTypes::FileURL</code></h2>
|
93
99
|
|
@@ -100,6 +106,8 @@ Appscript.app('TextEdit').open(f)
|
|
100
106
|
|
101
107
|
FileURL.path(path) -- make FileURL object from POSIX path string
|
102
108
|
|
109
|
+
FileURL.hfs_path(path) -- make FileURL object from HFS path string
|
110
|
+
|
103
111
|
FileURL.url(url) -- make FileURL object from a local file:// URL string
|
104
112
|
|
105
113
|
FileURL.desc(desc) -- make FileURL object from an AE::AEDesc
|
@@ -114,6 +122,8 @@ Appscript.app('TextEdit').open(f)
|
|
114
122
|
inspect
|
115
123
|
|
116
124
|
path -- returns POSIX path string
|
125
|
+
|
126
|
+
hfs_path -- returns HFS path string
|
117
127
|
|
118
128
|
url -- returns file:// URL string
|
119
129
|
|
@@ -165,6 +175,8 @@ Appscript.app('older app').documents[1].save(:in => fs_spec)</code></pre>
|
|
165
175
|
|
166
176
|
<p>Note that AEDescs of <code>TypeFSRef</code> can represent existing filesystem locations only. AEDescs of <code>TypeFileURL</code> can represent both existing and non-existing locations. AEDescs of <code>TypeFSS</code> (FSSpecs) are deprecated on Mac OS X due to lack of proper Unicode and long filename support, and are retained for backwards compatibility with older applications only.</p>
|
167
177
|
|
178
|
+
<p>Be aware that <code>FileURL#==</code> does not normalize file URLs; thus minor differences in capitalization, etc. can result in <code>FileURL#==</code> returning <code>false</code> even if both objects happen to identify the same filesystem location.</p>
|
179
|
+
|
168
180
|
|
169
181
|
|
170
182
|
|
@@ -239,6 +251,6 @@ MacTypes::Alias.path('/some/non/existent/location')
|
|
239
251
|
</div>
|
240
252
|
|
241
253
|
<!--footer-->
|
242
|
-
<p class="footer">©
|
254
|
+
<p class="footer">© 2007 HAS</p>
|
243
255
|
</body>
|
244
256
|
</html>
|
data/doc/osax-manual/index.html
CHANGED
@@ -107,9 +107,18 @@ osax.another_command</code></pre>
|
|
107
107
|
|
108
108
|
Constructors:
|
109
109
|
|
110
|
-
ScriptingAddition.new(name) -- make a ScriptingAddition
|
111
|
-
for the specified scripting addition, targetted
|
112
|
-
current application
|
110
|
+
ScriptingAddition.new(name, terms=nil) -- make a ScriptingAddition
|
111
|
+
object for the specified scripting addition, targetted
|
112
|
+
at the current application
|
113
|
+
|
114
|
+
name: string -- a scripting addition's name,
|
115
|
+
e.g. "StandardAdditions"; basically its filename
|
116
|
+
minus the '.osax' suffix
|
117
|
+
|
118
|
+
terms : module or nil -- an optional terminology glue
|
119
|
+
module,as exported by Terminology.dump; if
|
120
|
+
given, ScriptingAddition will use this instead
|
121
|
+
of retrieving the terminology dynamically
|
113
122
|
|
114
123
|
Methods:
|
115
124
|
|
@@ -117,7 +126,7 @@ osax.another_command</code></pre>
|
|
117
126
|
|
118
127
|
commands -- returns names of all available commands
|
119
128
|
|
120
|
-
parameters(
|
129
|
+
parameters(command_name) -- returns a command's parameter names
|
121
130
|
|
122
131
|
# Specifying a different target application:
|
123
132
|
|
@@ -167,6 +176,8 @@ p sa.display_dialog("Ruby says hello!",
|
|
167
176
|
|
168
177
|
<h2>Notes</h2>
|
169
178
|
|
179
|
+
<h3>GUI interaction</h3>
|
180
|
+
|
170
181
|
<p>When using scripting addition commands that require GUI access (e.g. <code>display_dialog</code>) targetted at the command-line Ruby interpreter, the osax module will automatically convert the non-GUI interpreter process into a full GUI process to allow these commands to operate correctly. If you want to avoid this, target these commands at a faceless GUI application such as System Events instead:</p>
|
171
182
|
|
172
183
|
<pre><code>sa = OSAX.osax("StandardAdditions", "System Events")
|
@@ -177,7 +188,27 @@ p sa.display_dialog("Ruby says hello!",
|
|
177
188
|
# Result: {:button_returned=>"Duuuude!"}</code></pre>
|
178
189
|
|
179
190
|
|
180
|
-
<
|
191
|
+
<h3>64-bit limitations</h3>
|
192
|
+
|
193
|
+
<p>The <code>osax</code> module currently only supports dynamic retrieval of scripting addition terminology when running in 32-bit processes. To use it in 64-bit processes, use the <code>Terminology</code> module's <code>dump</code> method to export a static terminology 'glue' module for the desired scripting addition (running it in a 32-bit process), then import that module and pass it as the second argument to the <code>ScriptingAddition</code> class's initialiser. For example, to export a glue module for Standard Additions:</p>
|
194
|
+
|
195
|
+
<pre><code>require 'appscript'
|
196
|
+
|
197
|
+
Terminology.dump('/System/Library/ScriptingAdditions/StandardAdditions.osax',
|
198
|
+
'StandardAdditions', 'standard_additions.rb')</code></pre>
|
199
|
+
|
200
|
+
<p>To create a new <code>ScriptingAddition</code> instance using the terminology provided by this glue module:</p>
|
201
|
+
|
202
|
+
<pre><code>require 'osax'
|
203
|
+
require 'standard_additions'
|
204
|
+
|
205
|
+
sa = OSAX::ScriptingAddition.new('StandardAdditions', StandardAdditions)</code></pre>
|
206
|
+
|
207
|
+
|
208
|
+
<h3>Known problems</h3>
|
209
|
+
|
210
|
+
<p>When using the <code>osax</code> module within RubyCocoa-based applications, avoid creating <code>ScriptingAddition</code> instances before the main event loop is started as this can result in the application behaving strangely (minimised windows don't expand correctly) due to a bug in OS X's <code>OSAGetAppTerminology</code> function.</p>
|
211
|
+
|
181
212
|
|
182
213
|
|
183
214
|
</div>
|
@@ -188,6 +219,6 @@ p sa.display_dialog("Ruby says hello!",
|
|
188
219
|
</div>
|
189
220
|
|
190
221
|
<!--footer-->
|
191
|
-
<p class="footer">©
|
222
|
+
<p class="footer">© 2007 HAS</p>
|
192
223
|
</body>
|
193
224
|
</html>
|
data/extconf.rb
CHANGED
@@ -30,8 +30,8 @@
|
|
30
30
|
|
31
31
|
require 'mkmf'
|
32
32
|
|
33
|
-
$CFLAGS << ' -Wall'
|
34
|
-
$LDFLAGS
|
33
|
+
$CFLAGS << ' -Wall'
|
34
|
+
$LDFLAGS << ' -framework Carbon -framework ApplicationServices'
|
35
35
|
|
36
36
|
# Avoid `ID' and `T_DATA' symbol collisions between Ruby and Carbon.
|
37
37
|
# (adapted code from RubyAEOSA - FUJIMOTO Hisakuni <hisa -at- fobj - com>)
|
data/rb-appscript.gemspec
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Exports all Address Book entries as vcards to current working folder.
|
4
|
+
#
|
5
|
+
# Files are named as 'NAME.vcard' (note: existing files will be overwritten).
|
6
|
+
# If the name is missing, 'unknown' is used instead. If two or more people
|
7
|
+
# share the same name, files will be named 'NAME.vcard', 'NAME 1.vcard',
|
8
|
+
# 'NAME 2.vcard', etc.
|
9
|
+
|
10
|
+
|
11
|
+
# Note: if using the appscript gem, rubygems must be required first:
|
12
|
+
begin; require 'rubygems'; rescue LoadError; end
|
13
|
+
|
14
|
+
require 'appscript'
|
15
|
+
include Appscript
|
16
|
+
|
17
|
+
people = app('Address Book').people
|
18
|
+
|
19
|
+
found_names = []
|
20
|
+
people.name.get.zip(people.vcard.get).each do |name, vcard|
|
21
|
+
name = 'unknown' if name == ''
|
22
|
+
name = name.gsub('/', ':')
|
23
|
+
filename = "#{name}.vcard"
|
24
|
+
i = 1
|
25
|
+
while found_names.include?(filename.downcase)
|
26
|
+
filename = "#{name} #{i}.vcard"
|
27
|
+
i += 1
|
28
|
+
end
|
29
|
+
found_names.push(filename.downcase)
|
30
|
+
File.open(filename, 'w') { |f| f.puts vcard }
|
31
|
+
end
|
data/sample/Add_iCal_event.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
#!/usr/
|
2
|
-
|
3
|
-
require 'appscript'
|
4
|
-
include Appscript
|
1
|
+
#!/usr/bin/env ruby
|
5
2
|
|
6
3
|
# Add an event to Home calendar that runs from 7am to 9 am tomorrow
|
7
4
|
|
8
5
|
# Note: if using the appscript gem, rubygems must be required first:
|
9
6
|
begin; require 'rubygems'; rescue LoadError; end
|
10
7
|
|
8
|
+
require 'appscript'
|
9
|
+
include Appscript
|
10
|
+
|
11
11
|
calendar_name = 'Home'
|
12
12
|
t = Time.now + 60 * 60 * 24
|
13
13
|
start = Time.local(t.year, t.month, t.day, 7)
|
data/src/lib/_aem/connect.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/
|
1
|
+
#!/usr/bin/ruby
|
2
2
|
# Copyright (C) 2006 HAS.
|
3
3
|
# Released under MIT License.
|
4
4
|
|
@@ -28,7 +28,48 @@ module Connect
|
|
28
28
|
#######
|
29
29
|
# public
|
30
30
|
|
31
|
-
|
31
|
+
class CantLaunchApplicationError < RuntimeError
|
32
|
+
|
33
|
+
# Taken from <http://developer.apple.com/documentation/Carbon/Reference/LaunchServicesReference>:
|
34
|
+
LSErrors = {
|
35
|
+
-10660 => "The application cannot be run because it is inside a Trash folder.",
|
36
|
+
-10810 => "An unknown error has occurred.",
|
37
|
+
-10811 => "The item to be registered is not an application.",
|
38
|
+
-10813 => "Data of the desired type is not available (for example, there is no kind string).",
|
39
|
+
-10814 => "No application in the Launch Services database matches the input criteria.",
|
40
|
+
-10817 => "Data is structured improperly (for example, an item's information property list is malformed).",
|
41
|
+
-10818 => "A launch of the application is already in progress.",
|
42
|
+
-10822 => "There is a problem communicating with the server process that maintains the Launch Services database.",
|
43
|
+
-10823 => "The filename extension to be hidden cannot be hidden.",
|
44
|
+
-10825 => "The application to be launched cannot run on the current Mac OS version.",
|
45
|
+
-10826 => "The user does not have permission to launch the application (on a managed network).",
|
46
|
+
-10827 => "The executable file is missing or has an unusable format.",
|
47
|
+
-10828 => "The Classic emulation environment was required but is not available.",
|
48
|
+
-10829 => "The application to be launched cannot run simultaneously in two different user sessions.",
|
49
|
+
}
|
50
|
+
|
51
|
+
def initialize(error_number)
|
52
|
+
@error_number = error_number
|
53
|
+
super("#{ LSErrors.fetch(@error_number, 'OS error') } (#{ @error_number })")
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_i
|
57
|
+
return @error_number
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
|
63
|
+
def Connect.launch_application(path, event)
|
64
|
+
begin
|
65
|
+
return AE.launch_application(path, event,
|
66
|
+
LaunchContinue + LaunchNoFileFlags + LaunchDontSwitch)
|
67
|
+
rescue AE::MacOSError => err
|
68
|
+
raise CantLaunchApplicationError, err.to_i
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def Connect.launch_app_with_launch_event(path)
|
32
73
|
# Send a 'launch' event to an application. If application is not already running, it will be launched in background first.
|
33
74
|
begin
|
34
75
|
# If app is already running, calling AE.launch_application will send a 'reopen' event, so need to check for this first:
|
@@ -36,8 +77,7 @@ module Connect
|
|
36
77
|
rescue AE::MacOSError => err
|
37
78
|
if err.to_i == -600 # Application isn't running, so launch it and send it a 'launch' event
|
38
79
|
sleep(1)
|
39
|
-
|
40
|
-
LaunchContinue + LaunchNoFileFlags + LaunchDontSwitch)
|
80
|
+
launch_application(path, LaunchEvent)
|
41
81
|
else
|
42
82
|
raise
|
43
83
|
end
|
@@ -46,8 +86,11 @@ module Connect
|
|
46
86
|
end
|
47
87
|
end
|
48
88
|
|
49
|
-
|
50
|
-
|
89
|
+
##
|
90
|
+
|
91
|
+
def Connect.process_exists_for_path?(path)
|
92
|
+
# Does a local process launched from the specified application file exist?
|
93
|
+
# Note: if path is invalid, an AE::MacOSError is raised.
|
51
94
|
begin
|
52
95
|
AE.psn_for_application_path(path)
|
53
96
|
return true
|
@@ -59,6 +102,44 @@ module Connect
|
|
59
102
|
end
|
60
103
|
end
|
61
104
|
end
|
105
|
+
|
106
|
+
def Connect.process_exists_for_pid?(pid)
|
107
|
+
# Is there a local application process with the given unix process id?
|
108
|
+
begin
|
109
|
+
AE.psn_for_process_id(pid)
|
110
|
+
return true
|
111
|
+
rescue AE::MacOSError => err
|
112
|
+
if err.to_i == -600
|
113
|
+
return false
|
114
|
+
else
|
115
|
+
raise
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def Connect.process_exists_for_url?(url)
|
121
|
+
# Does an application process specified by the given eppc:// URL exist?
|
122
|
+
# Note: this will send a 'launch' Apple event to the target application.
|
123
|
+
return process_exists_for_desc?(AE::AEDesc.new(KAE::TypeApplicationURL, url))
|
124
|
+
end
|
125
|
+
|
126
|
+
def Connect.process_exists_for_desc?(desc)
|
127
|
+
# Does an application process specified by the given AEAddressDesc exist?
|
128
|
+
# Returns false if process doesn't exist OR remote Apple events aren't allowed.
|
129
|
+
# Note: this will send a 'launch' Apple event to the target application.
|
130
|
+
begin
|
131
|
+
# This will usually raise error -1708 if process is running, and various errors
|
132
|
+
# if the process doesn't exist/can't be reached. If app is running but busy,
|
133
|
+
# AESendMessage may return a timeout error (this should be -1712, but
|
134
|
+
# -609 is often returned instead for some reason).
|
135
|
+
Send::Event.new(desc, 'ascrnoop').send
|
136
|
+
rescue Send::CommandError => err
|
137
|
+
return (not [-600, -905].include?(err.to_i)) # not running/no network access
|
138
|
+
end
|
139
|
+
return true
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
62
143
|
|
63
144
|
CurrentApp = make_address_desc([0, KCurrentProcess])
|
64
145
|
|
@@ -73,8 +154,7 @@ module Connect
|
|
73
154
|
rescue AE::MacOSError => err
|
74
155
|
if err.to_i == -600 # Application isn't running, so launch it in background and send it a standard 'run' event.
|
75
156
|
sleep(1)
|
76
|
-
psn =
|
77
|
-
LaunchContinue + LaunchNoFileFlags + LaunchDontSwitch)
|
157
|
+
psn = launch_application(path, RunEvent)
|
78
158
|
else
|
79
159
|
raise
|
80
160
|
end
|