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.
Files changed (41) hide show
  1. data/CHANGES +17 -0
  2. data/README +1 -1
  3. data/TODO +0 -4
  4. data/doc/appscript-manual/01_introduction.html +3 -2
  5. data/doc/appscript-manual/02_aboutappscripting.html +4 -4
  6. data/doc/appscript-manual/03_quicktutorial.html +14 -12
  7. data/doc/appscript-manual/04_gettinghelp.html +1 -1
  8. data/doc/appscript-manual/06_classesandenums.html +3 -3
  9. data/doc/appscript-manual/07_applicationobjects.html +17 -16
  10. data/doc/appscript-manual/08_realvsgenericreferences.html +16 -16
  11. data/doc/appscript-manual/09_referenceforms.html +15 -15
  12. data/doc/appscript-manual/10_referenceexamples.html +28 -27
  13. data/doc/appscript-manual/11_applicationcommands.html +14 -14
  14. data/doc/appscript-manual/12_commandexamples.html +14 -14
  15. data/doc/appscript-manual/13_performanceissues.html +8 -5
  16. data/doc/appscript-manual/14_problemapps.html +4 -5
  17. data/doc/mactypes-manual/index.html +27 -9
  18. data/doc/osax-manual/index.html +1 -1
  19. data/rb-appscript-0.2.0.gem +0 -0
  20. data/rb-appscript.gemspec +1 -1
  21. data/sample/AB_list_people_with_emails.rb +2 -1
  22. data/sample/Create_daily_iCal_todos.rb +4 -3
  23. data/sample/Hello_world.rb +2 -1
  24. data/sample/List_iTunes_playlist_names.rb +2 -1
  25. data/sample/Make_Mail_message.rb +2 -1
  26. data/sample/Open_file_in_TextEdit.rb +2 -1
  27. data/sample/Organize_Mail_messages.rb +3 -2
  28. data/sample/Print_folder_tree.rb +2 -1
  29. data/sample/Select_all_HTML_files.rb +3 -2
  30. data/sample/Set_iChat_status.rb +4 -3
  31. data/sample/Simple_Finder_GUI_Scripting.rb +3 -2
  32. data/sample/Stagger_Finder_windows.rb +2 -1
  33. data/sample/TextEdit_demo.rb +6 -5
  34. data/sample/iTunes_top40_to_html.rb +3 -2
  35. data/src/lib/_aem/mactypes.rb +24 -2
  36. data/src/lib/_appscript/referencerenderer.rb +7 -7
  37. data/src/lib/appscript.rb +32 -12
  38. data/src/lib/osax.rb +21 -19
  39. data/src/rbae.c +0 -12
  40. data/test/test_appscriptreference.rb +30 -30
  41. metadata +3 -2
@@ -12,8 +12,9 @@
12
12
  # incoming messages from this sender directly into this mailbox.
13
13
 
14
14
  require "appscript"
15
+ include Appscript
15
16
 
16
- mail = AS.app('Mail')
17
+ mail = app('Mail')
17
18
 
18
19
  # get the current selection in Mail and make sure it's an email message
19
20
  selection = mail.selection.get
@@ -47,7 +48,7 @@ if not mail.rules[folder_name].exists
47
48
  new_rule = mail.rules[1].after.make(:new=>:rule, :with_properties=>{
48
49
  :name=>folder_name,
49
50
  :should_move_message=>true,
50
- :move_message=>AS.app.mailboxes[folder_name],
51
+ :move_message=>app.mailboxes[folder_name],
51
52
  :stop_evaluating_rules=>true})
52
53
  new_rule.make(:new=>:rule_condition, :with_properties=>{
53
54
  :expression=>address,
@@ -3,10 +3,11 @@
3
3
  # Prints the sub-folder hierarchy of a given folder as a list of folder names indented according to depth.
4
4
 
5
5
  require "appscript"
6
+ include Appscript
6
7
 
7
8
  def print_folder_tree(folder, indent='')
8
9
  puts indent + folder.name.get
9
10
  folder.folders.get.each { |folder| print_folder_tree(folder, indent + "\t") }
10
11
  end
11
12
 
12
- print_folder_tree(AS.app('Finder').home.folders['Documents'])
13
+ print_folder_tree(app('Finder').home.folders['Documents'])
@@ -3,6 +3,7 @@
3
3
  # Selects all .htm/.html files in the top Finder window.
4
4
 
5
5
  require "appscript"
6
+ include Appscript
6
7
 
7
- w = AS.app('Finder').Finder_windows[1].target.get
8
- w.files[AS.its.name_extension.is_in(['htm', 'html'])].select
8
+ w = app('Finder').Finder_windows[1].target.get
9
+ w.files[its.name_extension.is_in(['htm', 'html'])].select
@@ -7,14 +7,15 @@
7
7
  # <http://developer.apple.com/cocoa/applescriptforapps.html>
8
8
 
9
9
  require "appscript"
10
+ include Appscript
10
11
 
11
12
  begin
12
- track_name = AS.app("iTunes").current_track.name.get
13
- rescue AS::CommandError => e
13
+ track_name = app("iTunes").current_track.name.get
14
+ rescue CommandError => e
14
15
  if e.to_i == -1728 # Can't get reference.
15
16
  track_name = 'No track selected.'
16
17
  else
17
18
  raise
18
19
  end
19
20
  end
20
- AS.app("iChat").status_message.set(track_name)
21
+ app("iChat").status_message.set(track_name)
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'appscript'
4
+ include Appscript
4
5
 
5
6
  # Opens a new Finder smart folder that searches for 'ruby', via GUI Scripting
6
7
 
7
8
  # (Note: to use GUI Scripting, 'Enable access for assistive devices' option must
8
9
  # be enabled in the Universal Access panel of System Preferences.)
9
10
 
10
- se = AS.app('System Events')
11
+ se = app('System Events')
11
12
 
12
- AS.app('Finder').activate
13
+ app('Finder').activate
13
14
  se.keystroke('n', :using=>[:command_down, :option_down])
14
15
  se.keystroke('ruby')
@@ -5,12 +5,13 @@
5
5
  # that uses standard window class terminology.)
6
6
 
7
7
  require "appscript"
8
+ include Appscript
8
9
 
9
10
  x, y = 0, 44
10
11
  offset = 22
11
12
 
12
13
  # Get list of window references, ignoring any minimised windows
13
- window_list = AS.app('Finder').windows[AS.its.collapsed.not].get
14
+ window_list = app('Finder').windows[its.collapsed.not].get
14
15
 
15
16
  # Move windows while preserving their original sizes
16
17
  window_list.reverse.each do |window|
@@ -3,8 +3,9 @@
3
3
  # Demonstrates various references and commands in action.
4
4
 
5
5
  require "appscript"
6
+ include Appscript
6
7
 
7
- textedit = AS.app('TextEdit') # get an application object for TextEdit
8
+ textedit = app('TextEdit') # get an application object for TextEdit
8
9
 
9
10
 
10
11
  # tell application "TextEdit" to activate
@@ -102,12 +103,12 @@ p textedit.documents[1].text.paragraphs.get
102
103
 
103
104
  # tell application "TextEdit" to get every paragraph of document 1
104
105
  # where it is not "\n" -- get non-empty paragraphs
105
- p textedit.documents[1].paragraphs[AS.its.ne("\n")].get
106
+ p textedit.documents[1].paragraphs[its.ne("\n")].get
106
107
 
107
108
 
108
109
  # tell application "TextEdit" to get text of every document
109
110
  # whose text begins with "H"
110
- p textedit.documents[AS.its.text.starts_with('H')].text.get
111
+ p textedit.documents[its.text.starts_with('H')].text.get
111
112
 
112
113
 
113
114
 
@@ -117,10 +118,10 @@ p textedit.documents[AS.its.text.starts_with('H')].text.get
117
118
  # # tell application "Tex-Edit Plus" to get words (character 5)
118
119
  # # thru (paragraph 9) of document 1
119
120
  # p app('Tex-Edit Plus').documents[1] \
120
- # .words[AS.con.characters[5], AS.con.paragraphs[9]].get
121
+ # .words[con.characters[5], con.paragraphs[9]].get
121
122
  #
122
123
  #
123
124
  # # tell application "Tex-Edit Plus" to get every word of text
124
125
  # # of document 1 whose color is {0, 0, 0}
125
126
  # p app('Tex-Edit Plus').documents[1].text.paragraphs \
126
- # [AS.its.color.equals((0, 0, 0))].get
127
+ # [its.color.equals((0, 0, 0))].get
@@ -6,6 +6,7 @@
6
6
  # Requires the Amrita templating engine: http://amrita.sourceforge.jp
7
7
 
8
8
  require 'appscript'
9
+ include Appscript
9
10
  require 'osax'
10
11
  require "amrita/template"
11
12
  include Amrita
@@ -42,7 +43,7 @@ sa = OSAX.osax('StandardAdditions')
42
43
  out_file = sa.choose_file_name(:default_name=>'My iTunes Top 40.html')
43
44
 
44
45
  # Get the played count, name and album for every track in iTunes
45
- tracks = AS.app('iTunes').library_playlists[1].tracks
46
+ tracks = app('iTunes').library_playlists[1].tracks
46
47
  info = tracks.played_count.get.zip(tracks.name.get, tracks.album.get)
47
48
  # Extract the top 40 most played entries
48
49
  top40 = info.sort.reverse[0, 40]
@@ -57,7 +58,7 @@ tmpl.prettyprint = true
57
58
  File.open(out_file.to_s, 'w') { |f| tmpl.expand(f, data) }
58
59
 
59
60
  # Open file in Safari for viewing
60
- safari = AS.app('Safari')
61
+ safari = app('Safari')
61
62
  safari.activate
62
63
  safari.open(out_file)
63
64
 
@@ -13,16 +13,18 @@ module MacTypes
13
13
  class FileBase
14
14
 
15
15
  URLPrefix = 'file://localhost'
16
+ URLPatt = Regexp.new('file://(?:.*?)(/.*)', Regexp::IGNORECASE)
16
17
 
17
18
  def FileBase._path_to_url(path)
18
19
  return URLPrefix + path.gsub(/[^a-zA-Z0-9_.-\/]/) { |c| "%%%02x" % c[0] }
19
20
  end
20
21
 
21
22
  def FileBase._url_to_path(url)
22
- if url[0, URLPrefix.length] != URLPrefix
23
+ match = url.match(URLPatt)
24
+ if not match
23
25
  raise ArgumentError, "Not a file:// URL."
24
26
  end
25
- return url[URLPrefix.length, url.length].gsub(/%../) { |s| "%c" % s[1,2].hex }
27
+ return match[1].gsub(/%../) { |s| "%c" % s[1,2].hex }
26
28
  end
27
29
 
28
30
  def FileBase._coerce(desc, type, path=nil)
@@ -78,6 +80,11 @@ module MacTypes
78
80
  KAE::TypeAlias, path))
79
81
  end
80
82
 
83
+ def Alias.url(url)
84
+ # Make Alias object from file URL. Note: only the path portion of the URL is used; the domain will always be localhost.
85
+ return Alias.path(_url_to_path(url))
86
+ end
87
+
81
88
  def Alias.desc(desc)
82
89
  # 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
90
  return new(desc)
@@ -90,6 +97,11 @@ module MacTypes
90
97
  return @desc
91
98
  end
92
99
 
100
+ def url
101
+ # Get as URL string.
102
+ return desc.coerce(KAE::TypeFileURL).data
103
+ end
104
+
93
105
  def path
94
106
  # Get as POSIX path.
95
107
  return FileBase._url_to_path(FileBase._coerce(@desc, KAE::TypeFileURL).data)
@@ -131,6 +143,11 @@ module MacTypes
131
143
  return new(path, nil)
132
144
  end
133
145
 
146
+ def FileURL.url(url)
147
+ # Make FileURL object from file URL. Note: only the path portion of the URL is used; the domain will always be localhost.
148
+ return FileURL.path(_url_to_path(url))
149
+ end
150
+
134
151
  def FileURL.desc(desc)
135
152
  # 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
153
  return new(nil, desc)
@@ -146,6 +163,11 @@ module MacTypes
146
163
  return @desc
147
164
  end
148
165
 
166
+ def url
167
+ # Get as URL string.
168
+ return desc.coerce(KAE::TypeFileURL).data
169
+ end
170
+
149
171
  def path
150
172
  # Get as POSIX path.
151
173
  if not @path
@@ -13,7 +13,7 @@ class ReferenceRenderer
13
13
 
14
14
  def initialize(app_data)
15
15
  @_app_data = app_data
16
- @result = "AS"
16
+ @result = ""
17
17
  end
18
18
 
19
19
  def _format(val)
@@ -73,24 +73,24 @@ class ReferenceRenderer
73
73
 
74
74
  def app
75
75
  if @_app_data.path
76
- @result += ".app(#{@_app_data.path.inspect})"
76
+ @result = "app(#{@_app_data.path.inspect})"
77
77
  elsif @_app_data.pid
78
- @result += ".app.by_pid(#{@_app_data.pid.inspect})"
78
+ @result = "app.by_pid(#{@_app_data.pid.inspect})"
79
79
  elsif @_app_data.url
80
- @result += ".app.by_url(#{@_app_data.url.inspect})"
80
+ @result = "app.by_url(#{@_app_data.url.inspect})"
81
81
  else
82
- @result += ".app.current"
82
+ @result = "app.current"
83
83
  end
84
84
  return self
85
85
  end
86
86
 
87
87
  def con
88
- @result += ".con"
88
+ @result = "con"
89
89
  return self
90
90
  end
91
91
 
92
92
  def its
93
- @result += ".its"
93
+ @result = "its"
94
94
  return self
95
95
  end
96
96
 
data/src/lib/appscript.rb CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  require "_aem/mactypes"
6
6
 
7
- module AS
7
+ module Appscript
8
8
  # The following methods and classes are of interest to end users:
9
9
  # app, con, its, CommandError, ApplicationNotFoundError
10
10
  # Other classes are only of interest to implementors who need to hook in their own code.
@@ -182,7 +182,7 @@ module AS
182
182
  end
183
183
 
184
184
  def unpack_contains_comp_descriptor(op1, op2)
185
- if op1.is_a?(AS::Reference) and op1.AS_aem_reference.AEM_root == AEMReference::Its
185
+ if op1.is_a?(Appscript::Reference) and op1.AS_aem_reference.AEM_root == AEMReference::Its
186
186
  return op1.contains(op2)
187
187
  else
188
188
  return super
@@ -219,7 +219,7 @@ module AS
219
219
  end
220
220
 
221
221
  def to_s
222
- s= 'AS.' + @_call[0]
222
+ s= @_call[0]
223
223
  @_call[1, @_call.length].each do |name, args| if name == :[]
224
224
  if args.length == 1
225
225
  s += "[#{args[0]}]"
@@ -272,7 +272,7 @@ module AS
272
272
  if selector == nil
273
273
  selector = value_if_none
274
274
  end
275
- if selector.is_a?(AS::GenericReference)
275
+ if selector.is_a?(Appscript::GenericReference)
276
276
  return selector.AS_resolve(@AS_app_data).AS_aem_reference
277
277
  elsif selector.is_a?(Reference)
278
278
  return selector.AS_aem_reference
@@ -400,7 +400,7 @@ module AS
400
400
  end
401
401
  end
402
402
  # rescue => e
403
- # raise AS::CommandError.new(self, name, args, e)
403
+ # raise Appscript::CommandError.new(self, name, args, e)
404
404
  # end
405
405
  # build and send the Apple event, returning its result, if any
406
406
  begin
@@ -541,7 +541,7 @@ module AS
541
541
  self._resolve_range_boundary(end_range_selector, -1))
542
542
  elsif selector.is_a?(String)
543
543
  new_ref = @AS_aem_reference.by_name(selector)
544
- elsif selector.is_a?(AS::GenericReference)
544
+ elsif selector.is_a?(Appscript::GenericReference)
545
545
  new_ref = @AS_aem_reference.by_filter(
546
546
  selector.AS_resolve(@AS_app_data).AS_aem_reference)
547
547
  else
@@ -771,9 +771,9 @@ module AS
771
771
 
772
772
  #######
773
773
 
774
- AS_App = AS::GenericApplication.new(Application)
775
- AS_Con = AS::GenericReference.new(['con'])
776
- AS_Its = AS::GenericReference.new(['its'])
774
+ AS_App = Appscript::GenericApplication.new(Application)
775
+ AS_Con = Appscript::GenericReference.new(['con'])
776
+ AS_Its = Appscript::GenericReference.new(['its'])
777
777
 
778
778
 
779
779
  ######################################################################
@@ -781,7 +781,7 @@ module AS
781
781
  ######################################################################
782
782
  # public (note: Application & GenericApplication classes may also be accessed if subclassing Application class is required)
783
783
 
784
- def AS.app(*args)
784
+ def Appscript.app(*args)
785
785
  if args == []
786
786
  return AS_App
787
787
  else
@@ -789,11 +789,29 @@ module AS
789
789
  end
790
790
  end
791
791
 
792
- def AS.con
792
+ def Appscript.con
793
793
  return AS_Con
794
794
  end
795
795
 
796
- def AS.its
796
+ def Appscript.its
797
+ return AS_Its
798
+ end
799
+
800
+ # also define app, con, its as instance methods so that clients can 'include Appscript'
801
+
802
+ def app(*args)
803
+ if args == []
804
+ return AS_App
805
+ else
806
+ return AS_App.by_name(*args)
807
+ end
808
+ end
809
+
810
+ def con
811
+ return AS_Con
812
+ end
813
+
814
+ def its
797
815
  return AS_Its
798
816
  end
799
817
 
@@ -828,3 +846,5 @@ module AS
828
846
 
829
847
  ApplicationNotFoundError = FindApp::ApplicationNotFoundError
830
848
  end
849
+
850
+ AS = Appscript # backwards compatibility # TO DO: remove in 0.3.0
data/src/lib/osax.rb CHANGED
@@ -20,22 +20,22 @@ module OSAX
20
20
  OSAXCache = {}
21
21
  OSAXNames = []
22
22
 
23
- se = AS.app('System Events')
23
+ se = Appscript.app('System Events')
24
24
  [se.system_domain, se.local_domain, se.user_domain].each do |domain|
25
25
  osaxen = domain.scripting_additions_folder.files[
26
- AS.its.file_type.eq('osax').or(AS.its.name_extension.eq('osax'))]
26
+ Appscript.its.file_type.eq('osax').or(Appscript.its.name_extension.eq('osax'))]
27
27
  osaxen.name.get.zip(osaxen.POSIX_path.get).each do |name, path|
28
28
  name = name.sub(/(?i)\.osax$/, '') # remove name extension, if any
29
29
  OSAXNames.push(name)
30
30
  OSAXCache[name.downcase] = [path, nil]
31
31
  end
32
32
  end
33
- OSAXNames.sort.uniq
33
+ OSAXNames.sort!.uniq!
34
34
 
35
35
  #######
36
36
  # modified AppData class
37
37
 
38
- class OSAXData < AS::AppData
38
+ class OSAXData < Appscript::AppData
39
39
 
40
40
  def initialize(name, pid, url, terms)
41
41
  super(AEM::Application, name, pid, url, terms)
@@ -59,7 +59,7 @@ module OSAX
59
59
  end
60
60
  end
61
61
  @type_by_code, @type_by_name, @reference_by_code, @reference_by_name = @_terms
62
- extend(AS::AppDataAccessors)
62
+ extend(Appscript::AppDataAccessors)
63
63
  end
64
64
 
65
65
  end
@@ -85,19 +85,18 @@ module OSAX
85
85
  end
86
86
 
87
87
 
88
- class ScriptingAddition < AS::Reference
88
+ class ScriptingAddition < Appscript::Reference
89
89
  # Represents a single scripting addition.
90
90
 
91
- def initialize(name)
91
+ def initialize(name, osax_data=nil)
92
92
  # name: string -- a scripting addition's name, e.g. "StandardAdditions";
93
93
  # basically its filename minus the '.osax' suffix
94
94
  #
95
95
  # Note that name is case-insensitive and an '.osax' suffix is ignored if given.
96
96
  @_osax_name = name
97
- if name.is_a?(OSAXData)
98
- osax_data = name
99
- else
100
- path, terms = OSAXCache[name.downcase.sub(/(?i)\.osax$/, '')]
97
+ if not osax_data
98
+ osax_name = name.downcase.sub(/(?i)\.osax$/, '')
99
+ path, terms = OSAXCache[osax_name]
101
100
  if not path
102
101
  raise ArgumentError, "Scripting addition not found: #{name.inspect}"
103
102
  end
@@ -105,7 +104,7 @@ module OSAX
105
104
  @_terms = terms
106
105
  else
107
106
  desc = AE.get_app_terminology(path).coerce(KAE::TypeAEList)
108
- @_terms = OSAXCache[name.downcase][1] = \
107
+ @_terms = OSAXCache[osax_name][1] = \
109
108
  Terminology.tables_for_aetes(DefaultCodecs.unpack(desc))
110
109
  end
111
110
  osax_data = OSAXData.new(nil, nil, nil, @_terms)
@@ -124,7 +123,7 @@ module OSAX
124
123
  def method_missing(name, *args)
125
124
  begin
126
125
  super
127
- rescue AS::CommandError => e
126
+ rescue Appscript::CommandError => e
128
127
  if e.to_i == -1713 # 'No user interaction allowed' error (e.g. user tried to send a 'display dialog' command to a non-GUI ruby process), so convert the target process to a full GUI process and try again
129
128
  AE.transform_process_to_foreground_application
130
129
  activate
@@ -140,31 +139,34 @@ module OSAX
140
139
 
141
140
  def by_name(name)
142
141
  # name : string -- name or full path to application
143
- return ScriptingAddition.new(OSAXData.new(FindApp.by_name(name), nil, nil, @_terms))
142
+ return ScriptingAddition.new(@_osax_name,
143
+ OSAXData.new(FindApp.by_name(name), nil, nil, @_terms))
144
144
  end
145
145
 
146
146
  def by_id(id)
147
147
  # id : string -- bundle id of application
148
- return ScriptingAddition.new(OSAXData.new(FindApp.by_id(id), nil, nil, @_terms))
148
+ return ScriptingAddition.new(@_osax_name,
149
+ OSAXData.new(FindApp.by_id(id), nil, nil, @_terms))
149
150
  end
150
151
 
151
152
  def by_creator(creator)
152
153
  # creator : string -- four-character creator code of application
153
- return ScriptingAddition.new(OSAXData.new(FindApp.by_creator(creator), nil, nil, @_terms))
154
+ return ScriptingAddition.new(@_osax_name,
155
+ OSAXData.new(FindApp.by_creator(creator), nil, nil, @_terms))
154
156
  end
155
157
 
156
158
  def by_pid(pid)
157
159
  # pid : integer -- Unix process id
158
- return ScriptingAddition.new(OSAXData.new(nil, pid, nil, @_terms))
160
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(nil, pid, nil, @_terms))
159
161
  end
160
162
 
161
163
  def by_url(url)
162
164
  # url : string -- eppc URL of application
163
- return ScriptingAddition.new(OSAXData.new(nil, nil, url, @_terms))
165
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(nil, nil, url, @_terms))
164
166
  end
165
167
 
166
168
  def current
167
- return ScriptingAddition.new(OSAXData.new(nil, nil, nil, @_terms))
169
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(nil, nil, nil, @_terms))
168
170
  end
169
171
  end
170
172