rb-appscript 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/CHANGES +45 -0
  2. data/README +5 -1
  3. data/TODO +3 -23
  4. data/doc/aem-manual/04_references.html +6 -6
  5. data/doc/aem-manual/05_targettingapplications.html +3 -3
  6. data/doc/aem-manual/07_findapp.html +1 -1
  7. data/doc/appscript-manual/04_gettinghelp.html +81 -7
  8. data/doc/appscript-manual/05_keywordconversion.html +18 -18
  9. data/doc/appscript-manual/07_applicationobjects.html +4 -4
  10. data/doc/appscript-manual/08_realvsgenericreferences.html +2 -2
  11. data/doc/appscript-manual/09_referenceforms.html +12 -14
  12. data/doc/appscript-manual/11_applicationcommands.html +1 -1
  13. data/doc/appscript-manual/14_notes.html +12 -12
  14. data/doc/osax-manual/index.html +2 -0
  15. data/rb-appscript.gemspec +2 -2
  16. data/sample/AB_list_people_with_emails.rb +3 -0
  17. data/sample/Add_iCal_event.rb +3 -0
  18. data/sample/Create_daily_iCal_todos.rb +2 -0
  19. data/sample/Export_Address_Book_phone_numbers.rb +3 -0
  20. data/sample/Hello_world.rb +3 -0
  21. data/sample/List_iTunes_playlist_names.rb +3 -0
  22. data/sample/Make_Mail_message.rb +3 -0
  23. data/sample/Open_file_in_TextEdit.rb +3 -0
  24. data/sample/Organize_Mail_messages.rb +3 -0
  25. data/sample/Print_folder_tree.rb +3 -0
  26. data/sample/Select_all_HTML_files.rb +3 -0
  27. data/sample/Set_iChat_status.rb +3 -0
  28. data/sample/Simple_Finder_GUI_Scripting.rb +6 -3
  29. data/sample/Stagger_Finder_windows.rb +3 -0
  30. data/sample/TextEdit_demo.rb +3 -0
  31. data/sample/iTunes_top40_to_html.rb +3 -0
  32. data/src/SendThreadSafe.c +380 -0
  33. data/src/SendThreadSafe.h +139 -0
  34. data/src/lib/_aem/aemreference.rb +46 -18
  35. data/src/lib/_aem/codecs.rb +19 -3
  36. data/src/lib/_aem/mactypes.rb +2 -6
  37. data/src/lib/_aem/send.rb +1 -1
  38. data/src/lib/_aem/typewrappers.rb +2 -2
  39. data/src/lib/_appscript/referencerenderer.rb +10 -4
  40. data/src/lib/_appscript/reservedkeywords.rb +4 -4
  41. data/src/lib/_appscript/terminology.rb +43 -35
  42. data/src/lib/aem.rb +8 -4
  43. data/src/lib/appscript.rb +78 -15
  44. data/src/lib/kae.rb +4 -0
  45. data/src/lib/osax.rb +9 -6
  46. data/src/rbae.c +102 -23
  47. data/test/test_aemreference.rb +4 -4
  48. data/test/test_appscriptcommands.rb +2 -2
  49. data/test/test_appscriptreference.rb +4 -4
  50. data/test/test_codecs.rb +14 -1
  51. data/test/test_findapp.rb +1 -1
  52. data/test/test_mactypes.rb +1 -1
  53. data/test/test_osax.rb +1 -1
  54. data/test/testall.sh +1 -1
  55. metadata +4 -2
@@ -0,0 +1,139 @@
1
+ /*
2
+ File: AESendThreadSafe.h
3
+
4
+ Contains: Code to send Apple events in a thread-safe manner.
5
+
6
+ Written by: DTS
7
+
8
+ Copyright: Copyright (c) 2007 Apple Inc. All Rights Reserved.
9
+
10
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
11
+ ("Apple") in consideration of your agreement to the following
12
+ terms, and your use, installation, modification or
13
+ redistribution of this Apple software constitutes acceptance of
14
+ these terms. If you do not agree with these terms, please do
15
+ not use, install, modify or redistribute this Apple software.
16
+
17
+ In consideration of your agreement to abide by the following
18
+ terms, and subject to these terms, Apple grants you a personal,
19
+ non-exclusive license, under Apple's copyrights in this
20
+ original Apple software (the "Apple Software"), to use,
21
+ reproduce, modify and redistribute the Apple Software, with or
22
+ without modifications, in source and/or binary forms; provided
23
+ that if you redistribute the Apple Software in its entirety and
24
+ without modifications, you must retain this notice and the
25
+ following text and disclaimers in all such redistributions of
26
+ the Apple Software. Neither the name, trademarks, service marks
27
+ or logos of Apple Inc. may be used to endorse or promote
28
+ products derived from the Apple Software without specific prior
29
+ written permission from Apple. Except as expressly stated in
30
+ this notice, no other rights or licenses, express or implied,
31
+ are granted by Apple herein, including but not limited to any
32
+ patent rights that may be infringed by your derivative works or
33
+ by other works in which the Apple Software may be incorporated.
34
+
35
+ The Apple Software is provided by Apple on an "AS IS" basis.
36
+ APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
37
+ WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
38
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
39
+ THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
40
+ COMBINATION WITH YOUR PRODUCTS.
41
+
42
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
43
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
44
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
46
+ OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
47
+ OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
48
+ OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
49
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
50
+ SUCH DAMAGE.
51
+
52
+ Change History (most recent first):
53
+
54
+ $Log: AESendThreadSafe.h,v $
55
+ Revision 1.2 2007/02/12 11:58:43
56
+ Corrected grammo in comment.
57
+
58
+ Revision 1.1 2007/02/09 10:55:27
59
+ First checked in.
60
+
61
+
62
+ */
63
+
64
+ /*
65
+
66
+ 2007/06/24 -- Modified by HAS to make AESendMessageThreadSafeSynchronous API-compatible with AESendMessage.
67
+
68
+ */
69
+
70
+ #ifndef _SENDTHREADSAFE_H
71
+ #define _SENDTHREADSAFE_H
72
+
73
+ #include <ApplicationServices/ApplicationServices.h>
74
+
75
+ /////////////////////////////////////////////////////////////////
76
+
77
+ /*
78
+ Introduction
79
+ ------------
80
+ Since Mac OS X 10.2 it has been possible to synchronously send an Apple event
81
+ from a thread other than the main thread. The technique for doing this is
82
+ documented in Technote 2053 "Mac OS X 10.2".
83
+
84
+ <http://developer.apple.com/technotes/tn2002/tn2053.html>
85
+
86
+ Unfortunately, this technique isn't quite right. Specifically, due to a bug
87
+ in Apple Event Manager <rdar://problem/4976113>, it is not safe to dispose of
88
+ the Mach port (using mach_port_destroy, as documented in the technote, or, more
89
+ correctly, using mach_port_mod_refs) that you created to use as the reply port.
90
+ Doing this triggers a race condition that, very rarely, can cause the system
91
+ to destroy some other, completely unrelated, Mach port within your process.
92
+ This could cause all sorts of problems. One common symptom is that, after
93
+ accidentally destroying the Mach port associated with a thread, your program
94
+ dies with the following message:
95
+
96
+ /SourceCache/Libc/Libc-320.1.3/pthreads/pthread.c:897: failed assertion `ret == MACH_MSG_SUCCESS'
97
+
98
+ The best workaround to this problem is to not dispose of the Mach port that
99
+ you use as the Apple event reply port. If you have a limited number of
100
+ secondary threads from which you need to send Apple events, it's relatively
101
+ easy to allocate an Apple event reply port for each thread and then never
102
+ dispose it. However, if you have general case code, it might be tricky
103
+ to track down all of the threads that send Apple events and make sure they
104
+ have reply ports. This module was designed as a general case solution to
105
+ the problem.
106
+
107
+ The module exports a single function, AESendMessageThreadSafeSynchronous, which,
108
+ as its name suggests, sends an Apple event and waits for the reply (that is,
109
+ a synchronous IPC) and is safe to call from an arbitrary thread. It's basically
110
+ a wrapper around the system function AESendMessage, with added smarts to manage
111
+ a per-thread Apple event reply port.
112
+
113
+ When <rdar://problem/4976113> is fixed, this module should be unnecessary but
114
+ benign.
115
+
116
+ For information about how this works, see the comments in the implementation.
117
+ */
118
+
119
+ /////////////////////////////////////////////////////////////////
120
+
121
+ #ifdef __cplusplus
122
+ extern "C" {
123
+ #endif
124
+
125
+ OSStatus SendMessageThreadSafe(
126
+ AppleEvent * eventPtr,
127
+ AppleEvent * replyPtr,
128
+ AESendMode sendMode,
129
+ long timeOutInTicks
130
+ );
131
+ // A thread-safe replacement for AESend. This is very much like AESendMessage,
132
+ // except that it takes care of setting up the reply port when you use it
133
+ // from a thread other than the main thread.
134
+
135
+ #ifdef __cplusplus
136
+ }
137
+ #endif
138
+
139
+ #endif
@@ -1,4 +1,4 @@
1
- #!/usr/local/bin/ruby
1
+ #!/usr/local/bin/ruby -w
2
2
  # Copyright (C) 2006 HAS.
3
3
  # Released under MIT License.
4
4
 
@@ -83,6 +83,10 @@ module AEMReference
83
83
 
84
84
  class Base
85
85
 
86
+ def initialize
87
+ @_comparable = nil
88
+ end
89
+
86
90
  def AEM_comparable
87
91
  # called by Base#==; returns the data needed to compare two aem references
88
92
  if not @_comparable
@@ -122,6 +126,8 @@ module AEMReference
122
126
  protected :_key, :_container
123
127
 
124
128
  def initialize(container, key)
129
+ super()
130
+ @_desc = nil
125
131
  @_container = container
126
132
  @_key = key
127
133
  end
@@ -163,7 +169,7 @@ module AEMReference
163
169
  class InsertionSpecifier < Specifier
164
170
  # A reference to an element insertion point.
165
171
 
166
- # Syntax: all_elements_ref.start / all_elements_ref.end / element_ref.before / element_ref.after
172
+ # Syntax: all_elements_ref.beginning / all_elements_ref.end / element_ref.before / element_ref.after
167
173
 
168
174
  def initialize(container, key, keyname)
169
175
  super(container, key)
@@ -256,8 +262,8 @@ module AEMReference
256
262
  return LessOrEquals.new(self, val)
257
263
  end
258
264
 
259
- def starts_with(val)
260
- return StartsWith.new(self, val)
265
+ def begins_with(val)
266
+ return BeginsWith.new(self, val)
261
267
  end
262
268
 
263
269
  def ends_with(val)
@@ -296,8 +302,8 @@ module AEMReference
296
302
  # Thes can be called on any kind of element reference, and also on property references where the
297
303
  # property represents a one-to-one relationship, e.g. textedit.documents[1].text.end is valid
298
304
 
299
- def start
300
- return InsertionSpecifier.new(self, Beginning, :start)
305
+ def beginning
306
+ return InsertionSpecifier.new(self, Beginning, :beginning)
301
307
  end
302
308
 
303
309
  def end
@@ -591,18 +597,12 @@ module AEMReference
591
597
  # Syntax: elements_ref.by_filter(test)
592
598
 
593
599
  # The test argument is a Test object constructed from an its-based reference.
594
- # For convenience, an its-based reference can also be passed directly - this will
595
- # be expanded to a Boolean equality test, e.g. AEM.its.visible -> AEM.its.visible.eq(true)
596
600
 
597
601
  KeyForm = AEMReference.pack_enum(KAE::FormTest)
598
602
 
599
603
  def initialize(wantcode, container, key)
600
604
  if not key.is_a?(Test)
601
- if key.is_a?(Specifier) and key.AEM_root == Its
602
- key = key.eq(true)
603
- else
604
- raise TypeError, "Bad selector: not a test (its) based specifier: #{key.inspect}"
605
- end
605
+ raise TypeError, "Bad selector: not a test (its) based specifier: #{key.inspect}"
606
606
  end
607
607
  super(wantcode, container.AEM_true_self, key)
608
608
  end
@@ -730,6 +730,7 @@ module AEMReference
730
730
 
731
731
  class DeferredSpecifier < Base
732
732
  def initialize(desc, codecs)
733
+ @_ref = nil
733
734
  @_desc = desc
734
735
  @_codecs = codecs
735
736
  end
@@ -792,6 +793,7 @@ module AEMReference
792
793
  protected :_operand1, :_operand2
793
794
 
794
795
  def initialize(operand1, operand2)
796
+ super()
795
797
  @_operand1 = operand1
796
798
  @_operand2 = operand2
797
799
  end
@@ -851,8 +853,8 @@ module AEMReference
851
853
  Operator = AEMReference.pack_enum(KAE::KAELessThanEquals)
852
854
  end
853
855
 
854
- class StartsWith < ComparisonTest
855
- Name = :starts_with
856
+ class BeginsWith < ComparisonTest
857
+ Name = :begins_with
856
858
  Operator = AEMReference.pack_enum(KAE::KAEBeginsWith)
857
859
  end
858
860
 
@@ -886,6 +888,7 @@ module AEMReference
886
888
  protected :_operands
887
889
 
888
890
  def initialize(operands)
891
+ super()
889
892
  @_operands = operands
890
893
  end
891
894
 
@@ -942,6 +945,7 @@ module AEMReference
942
945
  class ReferenceRoot < PositionSpecifier
943
946
 
944
947
  def initialize
948
+ super(nil, nil, nil)
945
949
  end
946
950
 
947
951
  def to_s
@@ -968,7 +972,7 @@ module AEMReference
968
972
  class ApplicationRoot < ReferenceRoot
969
973
  # Reference base; represents an application's application object. Used to construct full references.
970
974
 
971
- # Syntax: app
975
+ # Syntax: AEM.app
972
976
 
973
977
  Name = :app
974
978
  Type = AE::AEDesc.new(KAE::TypeNull, '')
@@ -978,7 +982,7 @@ module AEMReference
978
982
  class CurrentContainer < ReferenceRoot
979
983
  # Reference base; represents elements' container object. Used to construct by-range references.
980
984
 
981
- # Syntax: con
985
+ # Syntax: AEM.con
982
986
 
983
987
  Name = :con
984
988
  Type = AE::AEDesc.new(KAE::TypeCurrentContainer, '')
@@ -988,13 +992,37 @@ module AEMReference
988
992
  class ObjectBeingExamined < ReferenceRoot
989
993
  # Reference base; represents an element to be tested. Used to construct by-filter references.
990
994
 
991
- # Syntax: its
995
+ # Syntax: AEM.its
992
996
 
993
997
  Name = :its
994
998
  Type = AE::AEDesc.new(KAE::TypeObjectBeingExamined, '')
995
999
  end
996
1000
 
997
1001
 
1002
+ class CustomRoot < ReferenceRoot
1003
+ # Reference base; represents an arbitrary root object, e.g. an AEAddressDesc in a fully qualified reference
1004
+
1005
+ # Syntax: AEM.custom_root(value)
1006
+
1007
+ def initialize(value)
1008
+ @value = value
1009
+ super()
1010
+ end
1011
+
1012
+ def to_s
1013
+ return "AEM.custom_root(#{@value.inspect})"
1014
+ end
1015
+
1016
+ def _pack_self(codecs)
1017
+ return codecs.pack(@value)
1018
+ end
1019
+
1020
+ def AEM_resolve(obj)
1021
+ return obj.send(:custom_root, @value)
1022
+ end
1023
+ end
1024
+
1025
+
998
1026
  ###################################
999
1027
  # Reference root objects; used to construct new specifiers, e.g. AEM.app.property('pnam')
1000
1028
 
@@ -138,7 +138,7 @@ class Codecs
138
138
  # e.g. Adobe apps define additional unit types (ciceros, pixels, etc.)
139
139
  @unit_type_codecs.add_types(type_defs)
140
140
  end
141
-
141
+
142
142
  ######################################################################
143
143
  # Subclasses could override these to provide their own reference roots if needed
144
144
 
@@ -151,6 +151,7 @@ class Codecs
151
151
 
152
152
  SInt32Bounds = (-2**31)..(2**31-1)
153
153
  SInt64Bounds = (-2**63)..(2**63-1)
154
+ UInt64Bounds = (2**63)..(2**64-1)
154
155
 
155
156
  NullDesc = AE::AEDesc.new(KAE::TypeNull, '')
156
157
  TrueDesc = AE::AEDesc.new(KAE::TypeTrue, '')
@@ -218,6 +219,8 @@ class Codecs
218
219
  AE::AEDesc.new(KAE::TypeSInt32, [val].pack('l'))
219
220
  elsif SInt64Bounds === val
220
221
  AE::AEDesc.new(KAE::TypeSInt64, [val].pack('q'))
222
+ elsif UInt64Bounds === val
223
+ pack_uint64(val)
221
224
  else
222
225
  AE::AEDesc.new(KAE::TypeFloat, [val.to_f].pack('d'))
223
226
  end
@@ -235,6 +238,16 @@ class Codecs
235
238
 
236
239
  #######
237
240
 
241
+ def pack_uint64(val)
242
+ # On 10.5+, clients could override this method to do a non-lossy conversion,
243
+ # (assuming target app knows how to handle new UInt64 type):
244
+ #
245
+ # def pack_uint64(val)
246
+ # AE::AEDesc.new(KAE::TypeUInt64, [val.to_f].pack('Q'))
247
+ # end
248
+ AE::AEDesc.new(KAE::TypeFloat, [val.to_f].pack('d')) # pack as 64-bit float for compatibility (lossy conversion)
249
+ end
250
+
238
251
  def pack_array(val)
239
252
  lst = AE::AEDesc.new_list(false)
240
253
  val.each do |item|
@@ -352,6 +365,9 @@ class Codecs
352
365
  "#{vers}.#{subvers}.#{patch}"
353
366
 
354
367
  when KAE::TypeBoolean then desc.data != "\000"
368
+
369
+ when KAE::TypeUInt16 then desc.data.unpack('S')[0] # 10.5+
370
+ when KAE::TypeUInt64 then desc.data.unpack('Q')[0] # 10.5+
355
371
  else
356
372
  did_unpack, val = @unit_type_codecs.unpack(desc)
357
373
  if did_unpack
@@ -427,7 +443,7 @@ class Codecs
427
443
  InsertionLocEnums = {
428
444
  Codecs.four_char_code(KAE::KAEBefore) => 'before',
429
445
  Codecs.four_char_code(KAE::KAEAfter) => 'after',
430
- Codecs.four_char_code(KAE::KAEBeginning) => 'start',
446
+ Codecs.four_char_code(KAE::KAEBeginning) => 'beginning',
431
447
  Codecs.four_char_code(KAE::KAEEnd) => 'end',
432
448
  }
433
449
 
@@ -437,7 +453,7 @@ class Codecs
437
453
  Codecs.four_char_code(KAE::KAEEquals) => 'eq',
438
454
  Codecs.four_char_code(KAE::KAELessThan) => 'lt',
439
455
  Codecs.four_char_code(KAE::KAELessThanEquals) => 'le',
440
- Codecs.four_char_code(KAE::KAEBeginsWith) => 'starts_with',
456
+ Codecs.four_char_code(KAE::KAEBeginsWith) => 'begins_with',
441
457
  Codecs.four_char_code(KAE::KAEEndsWith) => 'ends_with',
442
458
  Codecs.four_char_code(KAE::KAEContains) => 'contains',
443
459
  }
@@ -1,4 +1,4 @@
1
- #!/usr/local/bin/ruby
1
+ #!/usr/local/bin/ruby -w
2
2
  # Copyright (C) 2006 HAS.
3
3
  # Released under MIT License.
4
4
 
@@ -61,7 +61,6 @@ module MacTypes
61
61
  #
62
62
  # Since Ruby doesn't already bridge the Mac OS's Alias Manager, simplest solution is to always store data internally as an AEDesc of typeAlias, and convert this to other forms on demand (e.g. when casting to string).
63
63
 
64
- attr_reader :desc
65
64
  private_class_method :new
66
65
 
67
66
  def initialize(desc)
@@ -89,10 +88,7 @@ module MacTypes
89
88
 
90
89
  # Methods
91
90
 
92
- def desc
93
- # Return AEDesc of typeAlias. If clients want a different type, they can subsequently call this AEDesc's coerce method.
94
- return @desc
95
- end
91
+ attr_reader :desc # Return AEDesc of typeAlias. If clients want a different type, they can subsequently call this AEDesc's coerce method.
96
92
 
97
93
  def url
98
94
  # Get as URL string.
data/src/lib/_aem/send.rb CHANGED
@@ -160,7 +160,7 @@ module Send
160
160
 
161
161
  def _send_apple_event(flags, timeout)
162
162
  # Hook method; may be overridden to customise how events are sent.
163
- return @AEM_event.send(flags, timeout)
163
+ return @AEM_event.send_thread_safe(flags, timeout)
164
164
  end
165
165
 
166
166
  def inspect
@@ -10,7 +10,7 @@ module TypeWrappers
10
10
 
11
11
  def initialize(code)
12
12
  if not (code.is_a?(String) and code.length == 4)
13
- raise ArgumentError, "Code must be a four-character string: #{code}"
13
+ raise ArgumentError, "Code must be a four-character string: #{code.inspect}"
14
14
  end
15
15
  @code = code
16
16
  end
@@ -26,7 +26,7 @@ module TypeWrappers
26
26
  alias_method :eql?, :==
27
27
 
28
28
  def to_s
29
- return "AEM::#{self.class::Name}.new(#{@code.dump})"
29
+ return "AEM::#{self.class::Name}.new(#{@code.inspect})"
30
30
  end
31
31
 
32
32
  def inspect
@@ -75,6 +75,12 @@ class ReferenceRenderer
75
75
 
76
76
  ##
77
77
 
78
+ def custom_root(value)
79
+ app
80
+ @result += ".AS_new_reference(#{value.inspect})"
81
+ return self
82
+ end
83
+
78
84
  def app
79
85
  case @_app_data.constructor
80
86
  when :current
@@ -99,8 +105,8 @@ class ReferenceRenderer
99
105
 
100
106
  ##
101
107
 
102
- def start
103
- @result += ".start"
108
+ def beginning
109
+ @result += ".beginning"
104
110
  return self
105
111
  end
106
112
 
@@ -173,8 +179,8 @@ class ReferenceRenderer
173
179
  return self
174
180
  end
175
181
 
176
- def starts_with(val)
177
- @result += ".starts_with(#{_format(val)})"
182
+ def begins_with(val)
183
+ @result += ".begins_with(#{_format(val)})"
178
184
  return self
179
185
  end
180
186