rb-appscript 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +17 -0
- data/README +1 -1
- data/TODO +0 -4
- data/doc/appscript-manual/01_introduction.html +3 -2
- data/doc/appscript-manual/02_aboutappscripting.html +4 -4
- data/doc/appscript-manual/03_quicktutorial.html +14 -12
- data/doc/appscript-manual/04_gettinghelp.html +1 -1
- data/doc/appscript-manual/06_classesandenums.html +3 -3
- data/doc/appscript-manual/07_applicationobjects.html +17 -16
- data/doc/appscript-manual/08_realvsgenericreferences.html +16 -16
- data/doc/appscript-manual/09_referenceforms.html +15 -15
- data/doc/appscript-manual/10_referenceexamples.html +28 -27
- data/doc/appscript-manual/11_applicationcommands.html +14 -14
- data/doc/appscript-manual/12_commandexamples.html +14 -14
- data/doc/appscript-manual/13_performanceissues.html +8 -5
- data/doc/appscript-manual/14_problemapps.html +4 -5
- data/doc/mactypes-manual/index.html +27 -9
- data/doc/osax-manual/index.html +1 -1
- data/rb-appscript-0.2.0.gem +0 -0
- data/rb-appscript.gemspec +1 -1
- data/sample/AB_list_people_with_emails.rb +2 -1
- data/sample/Create_daily_iCal_todos.rb +4 -3
- data/sample/Hello_world.rb +2 -1
- data/sample/List_iTunes_playlist_names.rb +2 -1
- data/sample/Make_Mail_message.rb +2 -1
- data/sample/Open_file_in_TextEdit.rb +2 -1
- data/sample/Organize_Mail_messages.rb +3 -2
- data/sample/Print_folder_tree.rb +2 -1
- data/sample/Select_all_HTML_files.rb +3 -2
- data/sample/Set_iChat_status.rb +4 -3
- data/sample/Simple_Finder_GUI_Scripting.rb +3 -2
- data/sample/Stagger_Finder_windows.rb +2 -1
- data/sample/TextEdit_demo.rb +6 -5
- data/sample/iTunes_top40_to_html.rb +3 -2
- data/src/lib/_aem/mactypes.rb +24 -2
- data/src/lib/_appscript/referencerenderer.rb +7 -7
- data/src/lib/appscript.rb +32 -12
- data/src/lib/osax.rb +21 -19
- data/src/rbae.c +0 -12
- data/test/test_appscriptreference.rb +30 -30
- metadata +3 -2
@@ -29,7 +29,7 @@
|
|
29
29
|
|
30
30
|
<p>Both direct and keyword parameters are optional.</p>
|
31
31
|
|
32
|
-
<p>Application commands are implemented by the application's top-level <code>application</code> object (represented by an <code>
|
32
|
+
<p>Application commands are implemented by the application's top-level <code>application</code> object (represented by an <code>Appscript::Application</code> instance). For convenience, appscript also makes application commands available as methods on every reference. If a command is called upon a reference rather than an Application object, that reference will be taken as the application's direct parameter (with one or two caveats; see the Special Cases section below).</p>
|
33
33
|
|
34
34
|
<p>The command's direct parameter can be any value; the application's dictionary will (usually) indicate the appropriate types. A command can have a maximum of one direct parameter; if more than one is given, an error will occur. Keyword parameters are specified as a <code>Hash</code> object whose keys are symbols representing the parameter names:</p>
|
35
35
|
|
@@ -83,25 +83,25 @@
|
|
83
83
|
<h2>Examples</h2>
|
84
84
|
|
85
85
|
<pre><code># tell application "TextEdit" to activate
|
86
|
-
|
86
|
+
app('TextEdit').activate
|
87
87
|
|
88
88
|
# tell application "TextEdit" to open fileRefList
|
89
|
-
|
89
|
+
app('TextEdit').open(fileRefList)
|
90
90
|
|
91
91
|
# tell application "Finder" to get version
|
92
|
-
|
92
|
+
app('Finder').version.get
|
93
93
|
|
94
94
|
# tell application "Finder" to set name of file "foo.txt" of home to "bar.txt"
|
95
|
-
|
95
|
+
app('Finder').home.files['foo.txt'].name.set('bar.txt')
|
96
96
|
|
97
97
|
# tell application "TextEdit" to count (text of first document) each paragraph
|
98
|
-
|
98
|
+
app('TextEdit').documents.first.text.count(:each => :paragraph)
|
99
99
|
|
100
100
|
# tell application "TextEdit" to make new document at end of documents
|
101
|
-
|
101
|
+
app('TextEdit').documents.end.make(:new => :document)
|
102
102
|
|
103
103
|
# tell application "Finder" to get items of home as alias list
|
104
|
-
|
104
|
+
app('Finder').home.items.get(:result_type => :alias)</code></pre>
|
105
105
|
|
106
106
|
|
107
107
|
|
@@ -155,13 +155,13 @@ AS.app('Finder').home.items.get(:result_type => :alias)</code></pre>
|
|
155
155
|
|
156
156
|
<p>Unlike AppleScript, which implicitly sends a <code>get</code> command to any unresolved application object references at the end of evaluating an expression, appscript only resolves a reference when it receives an appropriate command. For example:</p>
|
157
157
|
|
158
|
-
<pre><code>foo =
|
158
|
+
<pre><code>foo = app('Finder').disks</code></pre>
|
159
159
|
|
160
160
|
<p>is <em>not</em> equivalent to:</p>
|
161
161
|
|
162
162
|
<pre><code>set foo to disks of application "Finder"</code></pre>
|
163
163
|
|
164
|
-
<p>even though the two may look similar. In the first case, the value assigned to variable <code>foo</code> is a reference, <code>
|
164
|
+
<p>even though the two may look similar. In the first case, the value assigned to variable <code>foo</code> is a reference, <code>app('Finder').disks</code>. In the second, AppleScript evaluates the <code>disks of application "Finder"</code> reference by performing an implicit <code>get</code> command before assigning the result, a <em>list</em> containing one or more references (one to each disk currently mounted), to variable <code>foo</code>. To assign an unresolved reference to a variable in AppleScript, the reference must be preceded by the <code>a reference to</code> operator. For example:</p>
|
165
165
|
|
166
166
|
<ul>
|
167
167
|
<li><p>Getting a reference to all disks:</p>
|
@@ -170,9 +170,9 @@ AS.app('Finder').home.items.get(:result_type => :alias)</code></pre>
|
|
170
170
|
return foo
|
171
171
|
--> a reference to every disk of application "Finder"</code></pre>
|
172
172
|
|
173
|
-
<pre><code>foo =
|
173
|
+
<pre><code>foo = app('Finder').disks
|
174
174
|
print foo
|
175
|
-
-->
|
175
|
+
--> app('Finder').disks</code></pre></li>
|
176
176
|
|
177
177
|
|
178
178
|
<li><p>Getting a list of references to each disk:</p>
|
@@ -181,9 +181,9 @@ print foo
|
|
181
181
|
return foo
|
182
182
|
--> {disk "Mac HD" of application "Finder", disk "ZIP-100" of application "Finder"}</code></pre>
|
183
183
|
|
184
|
-
<pre><code>foo =
|
184
|
+
<pre><code>foo = app('Finder').disks.get
|
185
185
|
print foo
|
186
|
-
--> [
|
186
|
+
--> [app('Finder').disks['Mac HD'], app('Finder').disks['ZIP-100']]</code></pre>
|
187
187
|
|
188
188
|
</li>
|
189
189
|
</ul>
|
@@ -28,11 +28,11 @@
|
|
28
28
|
|
29
29
|
<pre><code># tell application "Finder" to get name of every folder of home
|
30
30
|
|
31
|
-
|
31
|
+
app('Finder').get(app.home.folders.name)</code></pre>
|
32
32
|
|
33
33
|
<p>Note that if the direct parameter is omitted from the argument list, the reference that the command is invoked on is used instead. For example, the above example would normally be written as:</p>
|
34
34
|
|
35
|
-
<pre><code>
|
35
|
+
<pre><code>app('Finder').home.folders.name.get</code></pre>
|
36
36
|
|
37
37
|
|
38
38
|
<h2><code>set</code></h2>
|
@@ -41,7 +41,7 @@ AS.app('Finder').get(AS.app.home.folders.name)</code></pre>
|
|
41
41
|
|
42
42
|
<pre><code># tell application "TextEdit" to set text of document 1 to "Hello World"
|
43
43
|
|
44
|
-
|
44
|
+
app('TextEdit').documents[1].text.set('Hello World')</code></pre>
|
45
45
|
|
46
46
|
|
47
47
|
<h2><code>count</code></h2>
|
@@ -50,13 +50,13 @@ AS.app('TextEdit').documents[1].text.set('Hello World')</code></pre>
|
|
50
50
|
|
51
51
|
<pre><code># tell application "TextEdit" to count words of document 1
|
52
52
|
|
53
|
-
|
53
|
+
app('TextEdit').documents[1].words.count</code></pre>
|
54
54
|
|
55
55
|
<p>Count the items in the current user's home folder:</p>
|
56
56
|
|
57
57
|
<pre><code>#tell application "Finder" to count items of home
|
58
58
|
|
59
|
-
|
59
|
+
app('Finder').home.count(:each => :item)</code></pre>
|
60
60
|
|
61
61
|
<p>(Note that the <code>each</code> parameter is required in Finder's <code>count</code> command.)</p>
|
62
62
|
|
@@ -68,7 +68,7 @@ AS.app('Finder').home.count(:each => :item)</code></pre>
|
|
68
68
|
<pre><code># tell application "TextEdit" to make new document ¬
|
69
69
|
# with properties {text:"Hello World\n"}
|
70
70
|
|
71
|
-
|
71
|
+
app('TextEdit').make(
|
72
72
|
:new => :document,
|
73
73
|
:with_properties => {:text => "Hello World\n"})</code></pre>
|
74
74
|
|
@@ -78,14 +78,14 @@ AS.app('TextEdit').make(
|
|
78
78
|
# at end of text of document 1 ¬
|
79
79
|
# with properties {text:"Yesterday\nToday\nTomorrow\n"}
|
80
80
|
|
81
|
-
|
81
|
+
app('TextEdit').make(
|
82
82
|
:new => :paragraph,
|
83
|
-
:at =>
|
83
|
+
:at => app.documents[1].text.end,
|
84
84
|
:with_data => "Yesterday\nToday\nTomorrow\n")</code></pre>
|
85
85
|
|
86
86
|
<p>Note that the <code>make</code> command's <code>at</code> parameter can be omitted for convenience, in which case the reference that the command is invoked on is used instead:</p>
|
87
87
|
|
88
|
-
<pre><code>
|
88
|
+
<pre><code>app('TextEdit').documents[1].text.end.make(
|
89
89
|
:new => :paragraph,
|
90
90
|
:with_data => "Yesterday\nToday\nTomorrow\n")</code></pre>
|
91
91
|
|
@@ -97,8 +97,8 @@ AS.app('TextEdit').make(
|
|
97
97
|
<pre><code># tell application "Finder" to ¬
|
98
98
|
# duplicate folder "Projects" of home to disk "Work" with replacing
|
99
99
|
|
100
|
-
|
101
|
-
:to =>
|
100
|
+
app('Finder').home.folders['Projects'].duplicate(
|
101
|
+
:to => app.disks['Backup'], :replacing => true)</code></pre>
|
102
102
|
|
103
103
|
|
104
104
|
<h2><code>add</code></h2>
|
@@ -109,9 +109,9 @@ AS.app('Finder').home.folders['Projects'].duplicate(
|
|
109
109
|
# every person whose birth date is not missing value ¬
|
110
110
|
# to group "Birthdays"
|
111
111
|
|
112
|
-
|
113
|
-
|
114
|
-
].add(:to =>
|
112
|
+
app('Address Book').people[
|
113
|
+
its.birth_date.ne(:missing_value)
|
114
|
+
].add(:to => app.groups['Birthdays'])</code></pre>
|
115
115
|
|
116
116
|
|
117
117
|
|
@@ -37,7 +37,8 @@
|
|
37
37
|
<h2>The iterative OO-style approach</h2>
|
38
38
|
|
39
39
|
<p>While iterating over application objects and manipulating each in turn is a common technique, it's also the slowest by far:</p>
|
40
|
-
|
40
|
+
|
41
|
+
include Appscript
|
41
42
|
|
42
43
|
desiredEmail = 'sam.brown@foo.com'
|
43
44
|
|
@@ -57,9 +58,10 @@ p foundNames</code></pre>
|
|
57
58
|
|
58
59
|
<p>In this case, the entire search can be performed using a single complex query sent to Address Book via a single Apple event:</p>
|
59
60
|
|
61
|
+
include Appscript
|
60
62
|
|
61
|
-
desiredEmail = 'sam.brown@foo.com'
|
62
|
-
|
63
|
+
desiredEmail = 'sam.brown@foo.com'
|
64
|
+
its.emails.value.contains(desiredEmail)
|
63
65
|
].name.get</code></pre>
|
64
66
|
|
65
67
|
<p>To explain:</p>
|
@@ -78,16 +80,17 @@ desiredEmail = 'sam.brown@foo.com'
|
|
78
80
|
<p>While AEOM queries can be surprisingly powerful, there are still many problems too complex for the application to evaluate entirely by itself. For example, let's say that you want to obtain the name of every person who has an email addresses that uses a particular domain name. Unfortunately, this test is too complex to express as a single AEOM query; however, it can still be solved reasonably efficiently by obtaining all the data from the application up-front and processing it locally. For this we need: 1. the name of every person in the Address Book, and 2. each person's email addresses. Fortunately, each of these can be expressed in a single query, allowing all this data to be retrieved using just two <code>get</code> commands.</p>
|
79
81
|
|
80
82
|
<pre><code>require "appscript"
|
83
|
+
include Appscript
|
81
84
|
|
82
85
|
desiredDomain = 'foo.com'
|
83
86
|
|
84
87
|
domainPattern = Regexp.new(Regexp.escape('@'+desiredDomain) + '$')
|
85
88
|
|
86
89
|
# get a list of name strings
|
87
|
-
names =
|
90
|
+
names = app('Address Book').people.name.get
|
88
91
|
|
89
92
|
# a list of lists of email strings
|
90
|
-
emailsOfEveryPerson =
|
93
|
+
emailsOfEveryPerson = app('Address Book').people.emails.value.get
|
91
94
|
|
92
95
|
result = []
|
93
96
|
names.zip(emailsOfEveryPerson).each do |name, emails|
|
@@ -50,7 +50,7 @@
|
|
50
50
|
|
51
51
|
<p>For example:</p>
|
52
52
|
|
53
|
-
<pre><code>aem_ref =
|
53
|
+
<pre><code>aem_ref = app('TextEdit').documents[1].text.AS_aem_reference
|
54
54
|
|
55
55
|
# aem_ref contains an aem reference:
|
56
56
|
# app.elements('docu').by_index(1).elements('ctxt')
|
@@ -144,7 +144,7 @@ end</code></pre>
|
|
144
144
|
<pre><code>require "appscript"
|
145
145
|
require "ical_terms"
|
146
146
|
|
147
|
-
ical =
|
147
|
+
ical = Appscript.app('iCal', ICalTerminology)
|
148
148
|
...</code></pre>
|
149
149
|
|
150
150
|
<p>Finally, remember to file a bug report with the application's developer so that they can fix the problem in a future release!</p>
|
@@ -167,13 +167,12 @@ ical = AS.app('iCal', ICalTerminology)
|
|
167
167
|
<p>Examples:</p>
|
168
168
|
|
169
169
|
<pre><code>require "appscript"
|
170
|
-
require "macfile"
|
171
170
|
|
172
171
|
# Run
|
173
|
-
|
172
|
+
Appscript.app('Foo', false).run(:wait_reply => false)
|
174
173
|
|
175
174
|
# Open
|
176
|
-
bar =
|
175
|
+
bar = Appscript.app('Bar', false)
|
177
176
|
bar.launch
|
178
177
|
bar.open([MacFile::Alias.path('/path/to/item 1'), ...], :wait_reply => false)</code></pre>
|
179
178
|
|
@@ -37,21 +37,29 @@
|
|
37
37
|
<pre><code>Alias -- a persistent reference to a filesystem object
|
38
38
|
Constructors:
|
39
39
|
|
40
|
-
Alias.path(path) -- make Alias object from POSIX path
|
40
|
+
Alias.path(path) -- make Alias object from POSIX path string
|
41
|
+
|
42
|
+
Alias.url(url) -- make Alias object from a local file:// URL string
|
41
43
|
|
42
44
|
Alias.desc(desc) -- make Alias object from an AE::AEDesc
|
43
45
|
of TypeAlias
|
44
46
|
|
45
47
|
Methods:
|
48
|
+
|
49
|
+
==
|
50
|
+
|
51
|
+
hash
|
52
|
+
|
53
|
+
inspect
|
46
54
|
|
47
55
|
path -- returns POSIX path string to the object's current location
|
48
56
|
|
57
|
+
url -- returns file:// URL string to the object's current location
|
58
|
+
|
49
59
|
desc -- returns AE::AEDesc of TypeAlias
|
50
60
|
|
51
61
|
to_s -- synonym for #path
|
52
62
|
|
53
|
-
inspect -- returns string representation of Alias object
|
54
|
-
|
55
63
|
to_alias -- returns self
|
56
64
|
|
57
65
|
to_file_url -- returns a MacTypes::FileURL object</code></pre>
|
@@ -66,10 +74,13 @@ f = MacTypes::Alias.path('/Users/foo/some file')
|
|
66
74
|
puts f.to_s
|
67
75
|
# /Users/foo/some file
|
68
76
|
|
77
|
+
puts f.url
|
78
|
+
# file://localhost/Users/foo/some%20file
|
79
|
+
|
69
80
|
puts f.inspect
|
70
81
|
# MacTypes::Alias.path("/Users/foo/some file")
|
71
82
|
|
72
|
-
|
83
|
+
Appscript.app('TextEdit').open(f)
|
73
84
|
# opens document in TextEdit</code></pre>
|
74
85
|
|
75
86
|
|
@@ -88,7 +99,9 @@ AS.app('TextEdit').open(f)
|
|
88
99
|
<pre><code>FileURL -- identifies a fixed filesystem location
|
89
100
|
Constructors:
|
90
101
|
|
91
|
-
FileURL.path(path) -- make FileURL object from POSIX path
|
102
|
+
FileURL.path(path) -- make FileURL object from POSIX path string
|
103
|
+
|
104
|
+
FileURL.url(url) -- make FileURL object from a local file:// URL string
|
92
105
|
|
93
106
|
FileURL.desc(desc) -- make FileURL object from an AE::AEDesc
|
94
107
|
of TypeFSS, TypeFSRef or TypeFileURL
|
@@ -99,14 +112,16 @@ AS.app('TextEdit').open(f)
|
|
99
112
|
|
100
113
|
hash
|
101
114
|
|
115
|
+
inspect
|
116
|
+
|
102
117
|
path -- returns POSIX path string
|
103
118
|
|
119
|
+
url -- returns file:// URL string
|
120
|
+
|
104
121
|
desc -- returns AE::AEDesc of TypeFSRef, TypeFSS or TypeFileURL
|
105
122
|
|
106
123
|
to_s -- synonym for #path
|
107
124
|
|
108
|
-
inspect -- returns string representation of FileURL object
|
109
|
-
|
110
125
|
to_alias -- returns a MacTypes::Alias object
|
111
126
|
|
112
127
|
to_file_url -- returns a new MacTypes::FileURL object</code></pre>
|
@@ -121,10 +136,13 @@ f = MacTypes::FileURL.path('/Users/foo/new file')
|
|
121
136
|
puts f.to_s
|
122
137
|
# /Users/foo/new file
|
123
138
|
|
139
|
+
puts f.url
|
140
|
+
# file://localhost/Users/foo/some%20file
|
141
|
+
|
124
142
|
puts f.inspect
|
125
143
|
# MacTypes::FileURL.path("/Users/foo/new file")
|
126
144
|
|
127
|
-
|
145
|
+
Appscript.app('TextEdit').documents[1].save(:in => f)
|
128
146
|
# saves front TextEdit document at the given location</code></pre>
|
129
147
|
|
130
148
|
|
@@ -140,7 +158,7 @@ file_url = MacTypes::FileURL.path('/Users/foo/new file')
|
|
140
158
|
|
141
159
|
fs_spec = file_url.desc.coerce(KAE::TypeFSS)
|
142
160
|
|
143
|
-
|
161
|
+
Appscript.app('older app').documents[1].save(:in => fs_spec)</code></pre>
|
144
162
|
|
145
163
|
<p>When used in an application command, a <code>FileURL</code> object returned by appscript will always pack into the same <code>TypeFSRef</code>, <code>TypeFileURL</code> or <code>TypeFSS</code> AEDesc it was created from. A <code>FileURL</code> object returned by <code>FileURL.path</code>, <code>Alias#to_file_url</code> or <code>FileURL#to_file_url</code> will always pack into an AEDesc of <code>TypeFileURL</code>.</p>
|
146
164
|
|
data/doc/osax-manual/index.html
CHANGED
@@ -135,7 +135,7 @@ p sa.commands
|
|
135
135
|
sa.beep
|
136
136
|
|
137
137
|
p sa.path_to(:scripts_folder)
|
138
|
-
# Result:
|
138
|
+
# Result: MacTypes::Alias.at("/Users/foo/Library/Scripts/")
|
139
139
|
|
140
140
|
p sa.display_dialog("Ruby says hello!",
|
141
141
|
:buttons=>["Hi!", "Howdy!", "Duuuude!"],
|
Binary file
|
data/rb-appscript.gemspec
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
# Lists the name and email(s) of every person in Address Book with one or more email addresses.
|
4
4
|
|
5
5
|
require "appscript"
|
6
|
+
include Appscript
|
6
7
|
|
7
|
-
people_ref =
|
8
|
+
people_ref = app('Address Book').people[its.emails.ne([])]
|
8
9
|
p people_ref.name.get.zip(people_ref.emails.value.get)
|
@@ -26,8 +26,9 @@
|
|
26
26
|
|
27
27
|
|
28
28
|
require "appscript"
|
29
|
+
include Appscript
|
29
30
|
|
30
|
-
ICal =
|
31
|
+
ICal = app("iCal")
|
31
32
|
|
32
33
|
# The calendar in which recurring todo items should appear:
|
33
34
|
ToDoCalendarName = "Personal"
|
@@ -38,8 +39,8 @@ def create_to_do(summary_text)
|
|
38
39
|
midnight = Time.local(now.year(), now.month(), now.day())
|
39
40
|
to_dos = ICal.calendars[ToDoCalendarName].todos
|
40
41
|
# don't create an item if it already exists for today!
|
41
|
-
if to_dos[
|
42
|
-
|
42
|
+
if to_dos[its.due_date.ge(midnight).and(
|
43
|
+
its.summary.eq(summary_text))].count < 1
|
43
44
|
to_dos.end.make(:new=>:todo, :with_properties=>{
|
44
45
|
:due_date=>midnight, :summary=>summary_text})
|
45
46
|
end
|
data/sample/Hello_world.rb
CHANGED
data/sample/Make_Mail_message.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
# Compose an outgoing message in Apple's Mail.app.
|
4
4
|
|
5
5
|
require "appscript"
|
6
|
+
include Appscript
|
6
7
|
|
7
8
|
def make_message(addresses, subject, content, show_window=false)
|
8
9
|
# Make an outgoing message in Mail.
|
@@ -11,7 +12,7 @@ def make_message(addresses, subject, content, show_window=false)
|
|
11
12
|
# content : unicode -- the message content
|
12
13
|
# show_window : Boolean -- show message window in Mail
|
13
14
|
# Result : reference -- reference to the new outgoing message
|
14
|
-
mail =
|
15
|
+
mail = app('Mail')
|
15
16
|
msg = mail.make(
|
16
17
|
:new => :outgoing_message,
|
17
18
|
:with_properties => {:visible => show_window})
|