rb-appscript 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/CHANGES +59 -0
  2. data/LICENSE +65 -0
  3. data/README +1 -1
  4. data/TODO +13 -13
  5. data/doc/aem-manual/04_references.html +13 -13
  6. data/doc/aem-manual/05_targettingapplications.html +7 -5
  7. data/doc/aem-manual/06_buildingandsendingevents.html +1 -1
  8. data/doc/aem-manual/08_examples.html +6 -6
  9. data/doc/aem-manual/index.html +3 -4
  10. data/doc/appscript-manual/02_aboutappscripting.html +2 -10
  11. data/doc/appscript-manual/04_gettinghelp.html +32 -18
  12. data/doc/appscript-manual/05_keywordconversion.html +7 -7
  13. data/doc/appscript-manual/06_classesandenums.html +2 -21
  14. data/doc/appscript-manual/07_applicationobjects.html +11 -2
  15. data/doc/appscript-manual/08_realvsgenericreferences.html +1 -1
  16. data/doc/appscript-manual/09_referenceforms.html +13 -13
  17. data/doc/appscript-manual/10_referenceexamples.html +7 -7
  18. data/doc/appscript-manual/11_applicationcommands.html +30 -28
  19. data/doc/appscript-manual/13_performanceissues.html +3 -3
  20. data/doc/appscript-manual/{15_notes.html → 14_notes.html} +18 -13
  21. data/doc/appscript-manual/full.css +1 -2
  22. data/doc/appscript-manual/index.html +3 -4
  23. data/doc/index.html +2 -1
  24. data/doc/mactypes-manual/index.html +23 -13
  25. data/doc/osax-manual/index.html +27 -5
  26. data/rb-appscript.gemspec +1 -1
  27. data/sample/AB_list_people_with_emails.rb +2 -1
  28. data/sample/Add_iCal_event.rb +18 -0
  29. data/sample/Export_Address_Book_phone_numbers.rb +56 -0
  30. data/sample/Hello_world.rb +9 -1
  31. data/sample/Select_all_HTML_files.rb +4 -2
  32. data/sample/iTunes_top40_to_html.rb +7 -4
  33. data/src/lib/_aem/aemreference.rb +50 -51
  34. data/src/lib/_aem/codecs.rb +148 -178
  35. data/src/lib/_aem/connect.rb +0 -2
  36. data/src/lib/_aem/findapp.rb +1 -1
  37. data/src/lib/_aem/mactypes.rb +2 -9
  38. data/src/lib/_aem/send.rb +2 -2
  39. data/src/lib/_appscript/defaultterminology.rb +2 -2
  40. data/src/lib/_appscript/referencerenderer.rb +119 -14
  41. data/src/lib/_appscript/reservedkeywords.rb +5 -0
  42. data/src/lib/_appscript/safeobject.rb +190 -0
  43. data/src/lib/_appscript/terminology.rb +195 -90
  44. data/src/lib/aem.rb +8 -9
  45. data/src/lib/appscript.rb +175 -159
  46. data/src/lib/osax.rb +65 -29
  47. data/src/rbae.c +42 -2
  48. data/test/test_aemreference.rb +3 -3
  49. data/test/test_appscriptcommands.rb +135 -0
  50. data/test/test_appscriptreference.rb +10 -8
  51. data/test/test_mactypes.rb +7 -1
  52. data/test/test_osax.rb +57 -0
  53. data/test/testall.sh +2 -1
  54. metadata +10 -9
  55. data/doc/aem-manual/09_notes.html +0 -41
  56. data/doc/appscript-manual/14_problemapps.html +0 -192
  57. data/misc/adobeunittypes.rb +0 -14
  58. data/misc/dump.rb +0 -72
  59. data/rb-appscript-0.2.0.gem +0 -0
data/src/lib/osax.rb CHANGED
@@ -20,7 +20,7 @@ module OSAX
20
20
  OSAXCache = {}
21
21
  OSAXNames = []
22
22
 
23
- se = Appscript.app('System Events')
23
+ se = Appscript.app.by_id('com.apple.systemevents')
24
24
  [se.system_domain, se.local_domain, se.user_domain].each do |domain|
25
25
  osaxen = domain.scripting_additions_folder.files[
26
26
  Appscript.its.file_type.eq('osax').or(Appscript.its.name_extension.eq('osax'))]
@@ -37,33 +37,25 @@ module OSAX
37
37
 
38
38
  class OSAXData < Appscript::AppData
39
39
 
40
- def initialize(name, pid, url, terms)
41
- super(AEM::Application, name, pid, url, terms)
40
+ def initialize(constructor, identifier, terms)
41
+ super(AEM::Application, constructor, identifier, terms)
42
42
  end
43
43
 
44
- def _connect
45
- if @path
46
- @target = @_aem_application_class.by_path(@path)
47
- elsif @pid
48
- @target = @_aem_application_class.by_pid(@pid)
49
- elsif @url
50
- @target = @_aem_application_class.by_url(@url)
51
- else
52
- @target = @_aem_application_class.current
53
- end
44
+ def connect
45
+ super
54
46
  begin
55
- @target.event('ascrgdut').send(300) # make sure target application has loaded event handlers for all installed OSAXen
47
+ @target.event('ascrgdut').send(60 * 60) # make sure target application has loaded event handlers for all installed OSAXen
56
48
  rescue AEM::CommandError => e
57
49
  if e.number != -1708 # ignore 'event not handled' error
58
50
  raise
59
51
  end
60
52
  end
61
- @type_by_code, @type_by_name, @reference_by_code, @reference_by_name = @_terms
62
- extend(Appscript::AppDataAccessors)
63
53
  end
64
54
 
65
55
  end
66
56
 
57
+ @_standard_additions = nil
58
+
67
59
 
68
60
  ######################################################################
69
61
  # PUBLIC
@@ -74,16 +66,55 @@ module OSAX
74
66
  return OSAXNames
75
67
  end
76
68
 
77
- def OSAX.osax(name, app_name=nil)
78
- # convenience method; provides shortcut for creating a new ScriptingAddition instance;
79
- # a target application's name or full path may optionally be given as well
80
- addition = ScriptingAddition.new(name)
81
- if app_name
82
- addition = addition.by_name(app_name)
69
+ def OSAX.osax(name=nil, app_name=nil)
70
+ # Convenience method for creating a new ScriptingAddition instance.
71
+ # name : String | nil -- scripting addition's name; nil = 'StandardAdditions'
72
+ # app_name : String | nil -- target application's name/path, or nil for current application
73
+ # Result : ScriptingAddition
74
+ #
75
+ # If both arguments are nil, a ScriptingAddition object for StandardAdditions is created
76
+ # and returned. This object is cached for efficiency and returned in subsequent calls;
77
+ # thus clients can conveniently write (e.g):
78
+ #
79
+ # osax.some_command
80
+ # osax.another_command
81
+ #
82
+ # instead of:
83
+ #
84
+ # sa = osax
85
+ # sa.some_command
86
+ # sa.another_command
87
+ #
88
+ # without the additional overhead of creating a new ScriptingAddition object each time.
89
+ #
90
+ if name == nil and app_name == nil
91
+ if @_standard_additions == nil
92
+ @_standard_additions = ScriptingAddition.new('StandardAdditions')
93
+ end
94
+ addition = @_standard_additions
95
+ else
96
+ if name == nil
97
+ name = 'StandardAdditions'
98
+ end
99
+ addition = ScriptingAddition.new(name)
100
+ if app_name
101
+ addition = addition.by_name(app_name)
102
+ end
83
103
  end
84
104
  return addition
85
105
  end
86
106
 
107
+ # allow methods to be included via 'include OSAX'
108
+
109
+ def scripting_additions
110
+ return OSAX.scripting_additions
111
+ end
112
+
113
+ def osax(*args)
114
+ return OSAX.osax(*args)
115
+ end
116
+
117
+ #######
87
118
 
88
119
  class ScriptingAddition < Appscript::Reference
89
120
  # Represents a single scripting addition.
@@ -107,7 +138,7 @@ module OSAX
107
138
  @_terms = OSAXCache[osax_name][1] = \
108
139
  Terminology.tables_for_aetes(DefaultCodecs.unpack(desc))
109
140
  end
110
- osax_data = OSAXData.new(nil, nil, nil, @_terms)
141
+ osax_data = OSAXData.new(:current, nil, @_terms)
111
142
  end
112
143
  super(osax_data, AEM.app)
113
144
  end
@@ -140,33 +171,38 @@ module OSAX
140
171
  def by_name(name)
141
172
  # name : string -- name or full path to application
142
173
  return ScriptingAddition.new(@_osax_name,
143
- OSAXData.new(FindApp.by_name(name), nil, nil, @_terms))
174
+ OSAXData.new(:by_path, FindApp.by_name(name), @_terms))
144
175
  end
145
176
 
146
177
  def by_id(id)
147
178
  # id : string -- bundle id of application
148
179
  return ScriptingAddition.new(@_osax_name,
149
- OSAXData.new(FindApp.by_id(id), nil, nil, @_terms))
180
+ OSAXData.new(:by_path, FindApp.by_id(id), @_terms))
150
181
  end
151
182
 
152
183
  def by_creator(creator)
153
184
  # creator : string -- four-character creator code of application
154
185
  return ScriptingAddition.new(@_osax_name,
155
- OSAXData.new(FindApp.by_creator(creator), nil, nil, @_terms))
186
+ OSAXData.new(:by_path, FindApp.by_creator(creator), @_terms))
156
187
  end
157
188
 
158
189
  def by_pid(pid)
159
190
  # pid : integer -- Unix process id
160
- return ScriptingAddition.new(@_osax_name, OSAXData.new(nil, pid, nil, @_terms))
191
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_pid, pid, @_terms))
161
192
  end
162
193
 
163
194
  def by_url(url)
164
195
  # url : string -- eppc URL of application
165
- return ScriptingAddition.new(@_osax_name, OSAXData.new(nil, nil, url, @_terms))
196
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_url, url, @_terms))
197
+ end
198
+
199
+ def by_aem_app(aem_app)
200
+ # aem_app : AEM::Application -- an AEM::Application instance
201
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(:by_aem_app, aem_app, @_terms))
166
202
  end
167
203
 
168
204
  def current
169
- return ScriptingAddition.new(@_osax_name, OSAXData.new(nil, nil, nil, @_terms))
205
+ return ScriptingAddition.new(@_osax_name, OSAXData.new(:current, nil, @_terms))
170
206
  end
171
207
  end
172
208
 
data/src/rbae.c CHANGED
@@ -233,6 +233,7 @@ rbAE_AEDesc_isRecord(VALUE self)
233
233
  return AECheckIsRecord(&(AEDESC_OF(self))) ? Qtrue : Qfalse;
234
234
  }
235
235
 
236
+
236
237
  static VALUE
237
238
  rbAE_AEDesc_coerce(VALUE self, VALUE type)
238
239
  {
@@ -244,6 +245,7 @@ rbAE_AEDesc_coerce(VALUE self, VALUE type)
244
245
  return rbAE_wrapAEDesc(&desc);
245
246
  }
246
247
 
248
+
247
249
  static VALUE
248
250
  rbAE_AEDesc_length(VALUE self)
249
251
  {
@@ -256,6 +258,8 @@ rbAE_AEDesc_length(VALUE self)
256
258
  }
257
259
 
258
260
 
261
+ /*******/
262
+
259
263
  static VALUE
260
264
  rbAE_AEDesc_putItem(VALUE self, VALUE index, VALUE desc)
261
265
  {
@@ -295,8 +299,10 @@ rbAE_AEDesc_putAttr(VALUE self, VALUE key, VALUE desc)
295
299
  }
296
300
 
297
301
 
302
+ /*******/
303
+
298
304
  static VALUE
299
- rbAE_AEDesc_get(VALUE self, VALUE index, VALUE type)
305
+ rbAE_AEDesc_getItem(VALUE self, VALUE index, VALUE type)
300
306
  {
301
307
  OSErr err = noErr;
302
308
  AEKeyword key;
@@ -315,6 +321,38 @@ rbAE_AEDesc_get(VALUE self, VALUE index, VALUE type)
315
321
  }
316
322
 
317
323
 
324
+ static VALUE
325
+ rbAE_AEDesc_getParam(VALUE self, VALUE key, VALUE type)
326
+ {
327
+ OSErr err = noErr;
328
+ AEDesc desc;
329
+
330
+ err = AEGetParamDesc(&(AEDESC_OF(self)),
331
+ rbStringToDescType(key),
332
+ rbStringToDescType(type),
333
+ &desc);
334
+ if (err != noErr) rbAE_raiseMacOSError("Can't get parameter from AEDesc.", err);
335
+ return rbAE_wrapAEDesc(&desc);
336
+ }
337
+
338
+
339
+ static VALUE
340
+ rbAE_AEDesc_getAttr(VALUE self, VALUE key, VALUE type)
341
+ {
342
+ OSErr err = noErr;
343
+ AEDesc desc;
344
+
345
+ err = AEGetAttributeDesc(&(AEDESC_OF(self)),
346
+ rbStringToDescType(key),
347
+ rbStringToDescType(type),
348
+ &desc);
349
+ if (err != noErr) rbAE_raiseMacOSError("Can't get attribute from AEDesc.", err);
350
+ return rbAE_wrapAEDesc(&desc);
351
+ }
352
+
353
+
354
+ /*******/
355
+
318
356
  static VALUE
319
357
  rbAE_AEDesc_send(VALUE self, VALUE sendMode, VALUE timeout)
320
358
  {
@@ -706,7 +744,9 @@ Init_ae (void)
706
744
  rb_define_method(cAEDesc, "put_item", rbAE_AEDesc_putItem, 2);
707
745
  rb_define_method(cAEDesc, "put_param", rbAE_AEDesc_putParam, 2);
708
746
  rb_define_method(cAEDesc, "put_attr", rbAE_AEDesc_putAttr, 2);
709
- rb_define_method(cAEDesc, "get", rbAE_AEDesc_get, 2);
747
+ rb_define_method(cAEDesc, "get_item", rbAE_AEDesc_getItem, 2);
748
+ rb_define_method(cAEDesc, "get_param", rbAE_AEDesc_getParam, 2);
749
+ rb_define_method(cAEDesc, "get_attr", rbAE_AEDesc_getAttr, 2);
710
750
  rb_define_method(cAEDesc, "send", rbAE_AEDesc_send, 2);
711
751
 
712
752
  // AE::MacOSError
@@ -103,9 +103,9 @@ class TC_AEMReferences < Test::Unit::TestCase
103
103
  assert_not_equal(AEMReference::App.elements('ctxt').property('ctxt'), AEMReference::App.property('ctxt').property('ctxt'))
104
104
  assert_not_equal(AEMReference::App.elements('ctxt').property('ctxt'), 333)
105
105
  assert_not_equal(333, AEMReference::App.property('ctxt').property('ctxt'))
106
- # by-range and by-filter references do basic type checking to ensure a reference is given
107
- assert_raises(TypeError) { AEMReference::App.elements('docu').by_range(1, 2) }
108
- assert_raises(TypeError) { AEMReference::App.elements('docu').by_filter(1) }
106
+ # # by-range and by-filter references do basic type checking to ensure a reference is given
107
+ # assert_raises(TypeError) { AEMReference::App.elements('docu').by_range(1, 2) }
108
+ # assert_raises(TypeError) { AEMReference::App.elements('docu').by_filter(1) }
109
109
 
110
110
  end
111
111
  end
@@ -0,0 +1,135 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'test/unit'
4
+ require 'appscript'
5
+
6
+ class AS_SafeObject
7
+ def self.hide(name)
8
+ end
9
+ end
10
+
11
+ class TC_AppscriptNewApp < Test::Unit::TestCase
12
+
13
+ def test_by_name
14
+ [
15
+ '/Applications/TextEdit.app',
16
+ 'Finder.app',
17
+ 'System Events'
18
+ ].each do |name|
19
+ a = Appscript.app(name)
20
+ assert_not_nil(a)
21
+ assert_instance_of(Appscript::Application, a)
22
+ assert_instance_of(Appscript::Reference, a.name)
23
+ end
24
+ assert_equal('app("/Applications/TextEdit.app")', Appscript.app('TextEdit').to_s)
25
+ assert_equal('app("/Applications/TextEdit.app")', Appscript.app.by_name('TextEdit').to_s)
26
+
27
+ assert_raises(Appscript::ApplicationNotFoundError) { Appscript.app('/non-existent/app') }
28
+ assert_raises(Appscript::ApplicationNotFoundError) { Appscript.app('non-existent.app') }
29
+ end
30
+
31
+ def test_by_id
32
+ [
33
+ 'com.apple.textedit',
34
+ 'com.apple.finder',
35
+ ].each do |name|
36
+ a = Appscript.app.by_id(name)
37
+ assert_not_nil(a)
38
+ assert_instance_of(Appscript::Application, a)
39
+ assert_instance_of(Appscript::Reference, a.name)
40
+ end
41
+ assert_equal('app("/Applications/TextEdit.app")', Appscript.app.by_id('com.apple.textedit').to_s)
42
+
43
+ assert_raises(Appscript::ApplicationNotFoundError) { Appscript.app.by_id('non.existent.app') }
44
+ end
45
+
46
+ def test_by_creator
47
+ a = Appscript.app.by_creator('ttxt')
48
+ assert_instance_of(Appscript::Reference, a.name)
49
+ assert_equal('app("/Applications/TextEdit.app")', a.to_s)
50
+ assert_raises(Appscript::ApplicationNotFoundError) { Appscript.app.by_id('!@$o') }
51
+ end
52
+
53
+ def test_by_pid
54
+ pid = `top -l1 | grep Finder | awk '{ print $1 }'`.to_i
55
+ a = Appscript.app.by_pid(pid)
56
+ assert_instance_of(Appscript::Reference, a.name)
57
+ assert_equal("app.by_pid(#{pid})", a.to_s)
58
+ assert_equal('Finder', a.name.get)
59
+ end
60
+
61
+ def test_by_aem_app
62
+ a = Appscript.app.by_aem_app(AEM::Application.by_path('/Applications/TextEdit.app'))
63
+ assert_instance_of(Appscript::Reference, a.name)
64
+ assert_equal('app.by_aem_app(AEM::Application.by_path("/Applications/TextEdit.app"))', a.to_s)
65
+ end
66
+ end
67
+
68
+
69
+ class TC_AppscriptCommands < Test::Unit::TestCase
70
+
71
+ def setup
72
+ @te = Appscript.app('TextEdit')
73
+ @f = Appscript.app('Finder')
74
+ end
75
+
76
+ def test_commands_1
77
+ assert_equal('TextEdit', @te.name.get)
78
+ d = @te.make(:new=>:document, :with_properties=>{:text=>'test test_commands'})
79
+ assert_instance_of(Appscript::Reference, d)
80
+ d.text.end.make(:new=>:word, :with_data=>' test2')
81
+ assert_equal('test test_commands test2', d.text.get)
82
+ assert_instance_of(String,
83
+ d.text.get(:ignore=>[:diacriticals, :punctuation, :whitespace, :expansion], :timeout=>10))
84
+ assert_nil(d.get(:wait_reply=>false))
85
+
86
+ d.text.set("\302\251 M. Lef\303\250vre")
87
+ assert_equal("\302\251 M. Lef\303\250vre", d.text.get)
88
+
89
+ d.close(:saving=>:no)
90
+ end
91
+
92
+ def test_commands_2
93
+ d = @te.make(:new=>:document, :at=>@te.documents.end)
94
+
95
+ @te.set(d.text, :to=> 'test1')
96
+ assert_equal('test1', d.text.get)
97
+
98
+ @te.set(d.text, :to=> 'test2')
99
+ @te.make(:new=>:word, :at=>Appscript.app.documents[1].paragraphs.end, :with_data=>' test3')
100
+ assert_equal('test2 test3', d.text.get)
101
+
102
+ d.close(:saving=>:no)
103
+
104
+ assert_raises(Appscript::CommandError) { @te.documents[10000].get }
105
+
106
+ assert_instance_of(Fixnum, @te.documents.count)
107
+ assert_equal(@te.documents.count, @te.count(:each=>:document))
108
+ end
109
+
110
+ def test_commands_3
111
+ assert_equal('Finder', @f.name.get)
112
+ val = @f.home.folders['Desktop'].get(:result_type=>:alias)
113
+ assert_instance_of(MacTypes::Alias, val)
114
+ assert_equal(val, @f.desktop.get(:result_type=>:alias))
115
+ assert_instance_of(Array, @f.disks.get)
116
+
117
+ r = @f.home.get
118
+ f = r.get(:result_type=>:file_ref)
119
+ assert_equal(r, @f.items[f].get)
120
+
121
+ assert_equal(@f.home.items.get, @f.home.items.get)
122
+ assert_not_equal(@f.disks['non-existent'], @f.disks[1].get)
123
+ end
124
+
125
+ def test_command_error
126
+ begin
127
+ @f.items[10000].get
128
+ rescue Appscript::CommandError => e
129
+ assert_equal(-1728, e.to_i)
130
+ assert_equal("CommandError\n\t\tOSERROR: -1728\n\t\tMESSAGE: Can't get reference.\n\t\tCOMMAND: app(\"/System/Library/CoreServices/Finder.app\").items[10000].get()\n", e.to_s)
131
+ assert_instance_of(AEM::CommandError, e.real_error)
132
+ end
133
+ end
134
+ end
135
+
@@ -3,7 +3,6 @@
3
3
  require 'test/unit'
4
4
  require "appscript"
5
5
 
6
-
7
6
  class TC_AppscriptReferences < Test::Unit::TestCase
8
7
 
9
8
  def setup
@@ -13,6 +12,16 @@ class TC_AppscriptReferences < Test::Unit::TestCase
13
12
 
14
13
  def test_reference_forms
15
14
  [
15
+
16
+ [@te.documents[
17
+ Appscript.con.documents[3],
18
+ Appscript.con.documents['foo']],
19
+ @s+'.documents[' +
20
+ 'con.documents[3], ' +
21
+ 'con.documents["foo"]]', nil],
22
+
23
+
24
+
16
25
  [@te.text, @s+'.text', nil],
17
26
 
18
27
  [@te.documents, @s+'.documents', nil],
@@ -36,13 +45,6 @@ class TC_AppscriptReferences < Test::Unit::TestCase
36
45
 
37
46
  [Appscript.con.documents[3], 'con.documents[3]', nil],
38
47
 
39
- [@te.documents[
40
- Appscript.con.documents[3],
41
- Appscript.con.documents['foo']],
42
- @s+'.documents[' +
43
- 'con.documents[3], ' +
44
- 'con.documents["foo"]]', nil],
45
-
46
48
 
47
49
  [Appscript.its.name.eq('foo').and(Appscript.its.words.eq([])),
48
50
  'its.name.eq("foo").and(its.words.eq([]))', nil],
@@ -46,6 +46,9 @@ class TC_MacTypes < Test::Unit::TestCase
46
46
  `rm #{@path2}`
47
47
  assert_raises(MacTypes::FileNotFoundError) { f.to_s } # File not found.
48
48
  assert_raises(MacTypes::FileNotFoundError) { f.to_file_url } # File not found.
49
+
50
+ assert_equal(MacTypes::Alias.path("/Library/Scripts/"), MacTypes::Alias.path("/Library/Scripts/"))
51
+ assert_not_equal(MacTypes::Alias.path("/Library/Scripts/"), MacTypes::Alias.path("/Applications/"))
49
52
  end
50
53
 
51
54
 
@@ -62,6 +65,9 @@ class TC_MacTypes < Test::Unit::TestCase
62
65
 
63
66
  # check a not-found error is raised if getting Alias for a filesystem object that doesn't exist
64
67
  assert_raises(MacTypes::FileNotFoundError) { g.to_alias } # File "/non/existent path" not found.
65
-
68
+
69
+ assert_equal(MacTypes::FileURL.path("/Library/Scripts/"), MacTypes::FileURL.path("/Library/Scripts/"))
70
+ assert_not_equal(MacTypes::FileURL.path("/Library/Scripts/"), MacTypes::FileURL.path("/Applications/"))
71
+ assert_not_equal(MacTypes::FileURL.path("/Library/Scripts/"), MacTypes::Alias.path("/Library/Scripts/"))
66
72
  end
67
73
  end