rb-appscript 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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