rubyosa 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS CHANGED
@@ -1,5 +1,5 @@
1
- Maintainer:
2
- Laurent Sansonetti <lsansonett@apple.com>
1
+ Maintainer & Author:
2
+ Laurent Sansonetti <lsansonetti@apple.com>
3
3
 
4
4
  Contributors:
5
5
  Aaron Patterson <aaron.patterson@gmail.com>
data/README CHANGED
@@ -33,7 +33,10 @@ The rdoc-osa tool can be used to generate API reference documentation
33
33
  for the given application. See its --help flag for more information
34
34
  about how to use it.
35
35
 
36
- Feel free to send feedback to lsansonetti@apple.com.
36
+ Feel free to send feedback to rubyosa-discuss@rubyforge.org.
37
+
38
+ You can also file bugs, patches and feature requests to the tracker:
39
+ http://rubyforge.org/tracker/?group_id=1845.
37
40
 
38
41
  When reporting a bug, please set the AEDebugSends and AEDebugReceives
39
42
  environment variables to 1 and attach the logs.
@@ -0,0 +1,19 @@
1
+ # Ask BBEdit to run the uptime(1) command and get the result.
2
+
3
+ begin require 'rubygems'; rescue LoadError; end
4
+ require 'rbosa'
5
+
6
+ puts 'Asking for uptime...'
7
+
8
+ bbedit = OSA.app('BBEdit')
9
+
10
+ bbedit.make(OSA::BBEdit::TextDocument).text = <<EOS
11
+ #!/bin/sh
12
+ uptime
13
+ EOS
14
+
15
+ bbedit.run_unix_script
16
+
17
+ output_doc = bbedit.text_documents.find { |x| x.name == 'Unix Script Output' }
18
+
19
+ puts output_doc.text.get
@@ -1,3 +1,6 @@
1
+ # Lists the content of the Finder desktop.
2
+
3
+ begin require 'rubygems'; rescue LoadError; end
1
4
  require 'rbosa'
2
5
 
3
6
  ary = OSA.app('Finder').desktop.entire_contents.get
@@ -1,5 +1,6 @@
1
1
  # Opens given movies and in QuickTime and starts playing them indefinitely in fullscreen mode.
2
2
 
3
+ begin require 'rubygems'; rescue LoadError; end
3
4
  require 'rbosa'
4
5
 
5
6
  if ARGV.empty?
@@ -0,0 +1,19 @@
1
+ # Create new TextEdit documents with a 'Hello World' text.
2
+
3
+ begin require 'rubygems'; rescue LoadError; end
4
+ require 'rbosa'
5
+
6
+ textedit = OSA.app('TextEdit')
7
+
8
+ # Complex way.
9
+ textedit.make(OSA::TextEdit::Document, nil, nil, {:ctxt => 'Hello World #1'})
10
+
11
+ # Easier way.
12
+ textedit.make(OSA::TextEdit::Document).text = 'Hello World #2'
13
+
14
+ =begin
15
+ # Easiest way, not implemented for now.
16
+ document = OSA::TextEdit::Document.new
17
+ document.text = 'Hello World #3'
18
+ textedit << document
19
+ =end
@@ -0,0 +1,18 @@
1
+ # Periodically set your iChat image to one of the default images.
2
+
3
+ begin require 'rubygems'; rescue LoadError; end
4
+ require 'rbosa'
5
+
6
+ ichat = OSA.app('iChat')
7
+
8
+ old_image = ichat.image
9
+ trap('INT') { ichat.image = old_image; exit 0 }
10
+
11
+ paths = Dir.glob("/Library/User Pictures/**/*.tif")
12
+
13
+ while true do
14
+ paths.each do |path|
15
+ ichat.image = File.read(path)
16
+ sleep 2
17
+ end
18
+ end
@@ -1,11 +1,15 @@
1
1
  # Periodically set your iChat status to the output of uptime(1).
2
2
 
3
+ begin require 'rubygems'; rescue LoadError; end
3
4
  require 'rbosa'
4
5
 
5
6
  app = OSA.app('iChat')
6
7
  previous_status_message = app.status_message
7
8
  trap('INT') { app.status_message = previous_status_message; exit 0 }
8
- while true
9
- app.status_message = `uptime`.strip
9
+ while true
10
+ u = `uptime`
11
+ hours = u.scan(/^\s*(\d+:\d+)\s/).to_s + ' hours'
12
+ days = u.scan(/\d+\sdays/).to_s
13
+ app.status_message = "OSX up #{days} #{hours}"
10
14
  sleep 5
11
15
  end
@@ -0,0 +1,14 @@
1
+ # Open the artwork of the current iTunes track in Preview.
2
+
3
+ begin require 'rubygems'; rescue LoadError; end
4
+ require 'rbosa'
5
+
6
+ artworks = OSA.app('iTunes').current_track.artworks
7
+ if artworks.size == 0
8
+ puts "No artwork for current track."
9
+ exit 1
10
+ end
11
+
12
+ fname = '/tmp/foo.' + artworks[0].format.downcase.strip
13
+ File.open(fname, 'w') { |io| io.write(artworks[0].data) }
14
+ system("open -a Preview #{fname}")
@@ -1,10 +1,12 @@
1
1
  # Simple iTunes controller.
2
2
 
3
+ begin require 'rubygems'; rescue LoadError; end
3
4
  require 'rbosa'
4
5
  require 'curses'
5
6
  include Curses
6
7
 
7
8
  app = OSA.app('iTunes')
9
+ OSA.utf8_strings = true
8
10
 
9
11
  if app.current_track.nil?
10
12
  # We don't support write access now, so...
@@ -1,5 +1,6 @@
1
1
  # Start playing, then fade the volume from 0 to the original setting.
2
2
 
3
+ begin require 'rubygems'; rescue LoadError; end
3
4
  require 'rbosa'
4
5
 
5
6
  app = OSA.app('iTunes')
@@ -1,8 +1,10 @@
1
1
  # Quick inspection of iTunes' sources, playlists and tracks.
2
2
 
3
+ begin require 'rubygems'; rescue LoadError; end
3
4
  require 'rbosa'
4
5
 
5
6
  app = OSA.app('iTunes')
7
+ OSA.utf8_strings = true
6
8
  app.sources.each do |source|
7
9
  puts source.name
8
10
  source.playlists.each do |playlist|
@@ -1,5 +1,6 @@
1
1
  # Print the given application's sdef(5).
2
2
 
3
+ begin require 'rubygems'; rescue LoadError; end
3
4
  require 'rbosa'
4
5
  require 'rexml/document'
5
6
 
@@ -24,11 +24,15 @@
24
24
  # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
25
  # POSSIBILITY OF SUCH DAMAGE.
26
26
 
27
+ $KCODE = 'u' # we will use UTF-8 strings
28
+
27
29
  require 'osa'
28
30
  require 'date'
31
+ require 'uri'
32
+ require 'iconv'
29
33
 
30
34
  # Try to load RubyGems first, libxml-ruby may have been installed by it.
31
- begin require 'rubygems' rescue LoadError end
35
+ begin require 'rubygems'; rescue LoadError; end
32
36
 
33
37
  # If libxml-ruby is not present, switch to REXML.
34
38
  USE_LIBXML = begin
@@ -69,7 +73,7 @@ end
69
73
 
70
74
  class String
71
75
  def to_4cc
72
- OSA.__four_char_code__(self)
76
+ OSA.__four_char_code__(Iconv.iconv('MACROMAN', 'UTF-8', self).to_s)
73
77
  end
74
78
  end
75
79
 
@@ -112,7 +116,8 @@ class OSA::Element
112
116
  end
113
117
 
114
118
  def self.from_rbobj(requested_type, value, enum_group_codes)
115
- self.__new__(*OSA.convert_to_osa(requested_type, value, enum_group_codes))
119
+ obj = OSA.convert_to_osa(requested_type, value, enum_group_codes)
120
+ obj.is_a?(OSA::Element) ? obj : self.__new__(*obj)
116
121
  end
117
122
  end
118
123
 
@@ -152,6 +157,10 @@ class OSA::ObjectSpecifierList
152
157
  end
153
158
  alias_method :size, :length
154
159
 
160
+ def empty?
161
+ length == 0
162
+ end
163
+
155
164
  def [](idx)
156
165
  idx += 1 # AE starts counting at 1.
157
166
  o = obj_spec_with_key(OSA::Element.__new__('long', [idx].pack('l')))
@@ -236,7 +245,7 @@ module OSA
236
245
  end
237
246
 
238
247
  def self.convert_to_ruby(osa_object)
239
- osa_type = osa_object.__type__.to_s
248
+ osa_type = osa_object.__type__
240
249
  osa_data = osa_object.__data__(osa_type) if osa_type and osa_type != 'null'
241
250
  if conversion = @conversions_to_ruby[osa_type]
242
251
  args = [osa_data, osa_type, osa_object]
@@ -245,12 +254,33 @@ module OSA
245
254
  end
246
255
 
247
256
  def self.convert_to_osa(requested_type, value, enum_group_codes=nil)
257
+ if requested_type.nil?
258
+ case value
259
+ when OSA::Element
260
+ return value
261
+ when String
262
+ requested_type = 'text'
263
+ when Array
264
+ requested_type = 'list'
265
+ when Hash
266
+ requested_type = 'record'
267
+ when Integer
268
+ requested_type = 'integer'
269
+ else
270
+ STDERR.puts "can't determine OSA type for #{value}" if $VERBOSE
271
+ ['null', nil]
272
+ end
273
+ end
274
+
248
275
  if conversion = @conversions_to_osa[requested_type]
249
276
  args = [value, requested_type]
250
277
  conversion.call(*args[0..(conversion.arity - 1)])
251
278
  elsif enum_group_codes and enum_group_codes.include?(requested_type)
252
279
  ['enum', value.code.to_4cc]
253
- else
280
+ elsif md = /^list_of_(.+)$/.match(requested_type)
281
+ ary = value.to_a.map { |x| convert_to_osa(md[1], x, enum_group_codes) }
282
+ ElementList.__new__(ary)
283
+ else
254
284
  STDERR.puts "unrecognized type #{requested_type}" if $VERBOSE
255
285
  ['null', nil]
256
286
  end
@@ -363,12 +393,18 @@ module OSA
363
393
  end
364
394
 
365
395
  # Creates properties.
366
- element.find('property').each do |pelement|
367
- name = pelement['name']
368
- code = pelement['code']
369
- type = pelement['type']
370
- access = pelement['access']
371
- description = pelement['description']
396
+ # Add basic properties that might be missing to the Item class (if any).
397
+ props = {}
398
+ element.find('property').each do |x|
399
+ props[x['name']] = [x['code'], x['type'], x['access'], x['description']]
400
+ end
401
+ if klass.name[-6..-1] == '::Item'
402
+ unless props.has_key?('id')
403
+ props['id'] = ['ID ', 'integer', 'r', 'the unique ID of the item']
404
+ end
405
+ end
406
+ props.each do |name, pary|
407
+ code, type, access, description = pary
372
408
  setter = (access == nil or access.include?('w'))
373
409
 
374
410
  if type == 'reference'
@@ -409,7 +445,7 @@ def #{method_name}
409
445
  end
410
446
  EOC
411
447
  end
412
-
448
+
413
449
  klass.class_eval(method_code)
414
450
  ptypedoc = if pklass.nil?
415
451
  if mod = enum_group_codes[type]
@@ -465,7 +501,7 @@ def #{method_name}
465
501
  unless OSA.lazy_events?
466
502
  @app.__send_event__('core', 'getd',
467
503
  [['----', Element.__new_object_specifier__(
468
- '#{eklass::CODE}', @app == self ? Element.__new__('null', nil) : self,
504
+ '#{eklass::CODE}'.to_4cc, @app == self ? Element.__new__('null', nil) : self,
469
505
  'indx', Element.__new__('abso', 'all '.to_4cc))]],
470
506
  true).to_rbobj
471
507
  else
@@ -569,6 +605,8 @@ EOC
569
605
  p_def << defi
570
606
  end
571
607
 
608
+ code = Iconv.iconv('MACROMAN', 'UTF-8', code).to_s
609
+
572
610
  method_code = <<EOC
573
611
  def %METHOD_NAME%(#{p_dec.join(', ')})
574
612
  @app.__send_event__('#{code[0..3]}', '#{code[4..-1]}', [#{p_def.join(', ')}], #{result != nil})#{result != nil ? '.to_rbobj' : ''}
@@ -663,10 +701,7 @@ EOC
663
701
  end
664
702
 
665
703
  def self.new_element_code(type, varname, enum_group_codes)
666
- if md = /^list_of_(.+)$/.match(type)
667
- return "#{varname}.is_a?(OSA::Element) ? #{varname} : ElementList.__new__(#{varname}.to_a.map { |x| #{new_element_code(md[1], 'x', enum_group_codes)} })"
668
- end
669
- "#{varname}.is_a?(OSA::Element) ? #{varname} : Element.from_rbobj('#{type}', #{varname}, #{enum_group_codes.inspect})"
704
+ "#{varname}.is_a?(OSA::Element) ? #{varname} : Element.from_rbobj('#{type}', #{varname}, #{enum_group_codes.keys.inspect})"
670
705
  end
671
706
 
672
707
  def self.escape_string(string)
@@ -674,19 +709,20 @@ EOC
674
709
  end
675
710
 
676
711
  def self.rubyfy_constant_string(string, upcase=false)
677
- if /^\d/.match(string)
678
- string = 'C' << string
679
- else
680
- string = string.dup
681
- string[0] = string[0].chr.upcase
712
+ string = string.gsub(/[^\w\s]/, '')
713
+ first = string[0]
714
+ if (?a..?z).include?(first)
715
+ string[0] = first.chr.upcase
716
+ elsif !(?A..?Z).include?(first)
717
+ string.insert(0, 'C')
682
718
  end
683
719
  escape_string(upcase ? string.upcase : string.gsub(/\s(.)/) { |s| s[1].chr.upcase })
684
720
  end
685
721
 
686
- RUBY_RESERVED_KEYWORDS = ['for', 'in']
722
+ RUBY_RESERVED_KEYWORDS = ['for', 'in', 'class']
687
723
  def self.rubyfy_string(string, handle_ruby_reserved_keywords=false)
688
724
  # Prefix with '_' parameter names to avoid possible collisions with reserved Ruby keywords (for, etc...).
689
- if RUBY_RESERVED_KEYWORDS.include?(string)
725
+ if handle_ruby_reserved_keywords and RUBY_RESERVED_KEYWORDS.include?(string)
690
726
  '_' + string
691
727
  else
692
728
  escape_string(string).downcase
@@ -694,17 +730,20 @@ EOC
694
730
  end
695
731
 
696
732
  def self.rubyfy_method(string, klass, return_type=nil, setter=false)
697
- s = rubyfy_string(string)
698
- if setter
699
- # Suffix setters with '='.
700
- s << '='
701
- elsif return_type == 'boolean'
702
- # Suffix predicates with '?'.
703
- s << '?'
704
- end
705
- # Prefix with 'osa_' in case the class already has a method with such a name.
706
- if klass.method_defined?(s)
707
- s = 'osa_' + s
733
+ base = rubyfy_string(string)
734
+ s, i = base.dup, 1
735
+ loop do
736
+ if setter
737
+ # Suffix setters with '='.
738
+ s << '='
739
+ elsif return_type == 'boolean'
740
+ # Suffix predicates with '?'.
741
+ s << '?'
742
+ end
743
+ break unless klass.method_defined?(s)
744
+ # Suffix with an integer if the class already has a method with such a name.
745
+ i += 1
746
+ s = base + i.to_s
708
747
  end
709
748
  return s
710
749
  end
@@ -718,15 +757,21 @@ EOC
718
757
  end
719
758
  end
720
759
 
721
- # String, force TEXT type to not get Unicode.
722
- OSA.add_conversion_to_ruby('TEXT', 'utxt') { |value, type, object| object.__data__('TEXT') }
723
- OSA.add_conversion_to_osa('string', 'text', 'Unicode text') { |value| ['TEXT', value.to_s] }
760
+ # String, for unicode stuff force utf8 type if specified.
761
+ OSA.add_conversion_to_ruby('TEXT') { |value, type, object| object.__data__('TEXT') }
762
+ OSA.add_conversion_to_ruby('utxt', 'utf8') { |value, type, object| object.__data__(OSA.utf8_strings ? 'utf8' : 'TEXT') }
763
+ OSA.add_conversion_to_osa('string', 'text') { |value| ['TEXT', value.to_s] }
764
+ OSA.add_conversion_to_osa('Unicode text') { |value| [OSA.utf8_strings ? 'utf8' : 'TEXT', value.to_s] }
724
765
 
725
766
  # Signed/unsigned integer.
726
- OSA.add_conversion_to_ruby('shor', 'long', 'comp') { |value| value.unpack('l').first }
767
+ OSA.add_conversion_to_ruby('shor', 'long') { |value| value.unpack('l').first }
768
+ OSA.add_conversion_to_ruby('comp') { |value| value.unpack('q').first }
727
769
  OSA.add_conversion_to_ruby('magn') { |value| value.unpack('d').first }
728
770
  OSA.add_conversion_to_osa('integer', 'double integer') { |value| ['magn', [value].pack('l')] }
729
771
 
772
+ # Float
773
+ OSA.add_conversion_to_ruby('sing') { |value| value.unpack('f').first }
774
+
730
775
  # Boolean.
731
776
  OSA.add_conversion_to_ruby('bool') { |value| value.unpack('c').first != 0 }
732
777
  OSA.add_conversion_to_osa('boolean') { |value| [(value ? 'true'.to_4cc : 'fals'.to_4cc), nil] }
@@ -746,16 +791,41 @@ OSA.add_conversion_to_ruby('list') { |value, type, object|
746
791
  # File name.
747
792
  # Let's use the 'furl' type here instead of 'alis', as we don't have a way to produce an alias for a file that does not exist yet.
748
793
  OSA.add_conversion_to_osa('alias', 'file') { |value| ['furl', value.to_s] }
794
+ OSA.add_conversion_to_ruby('alis') { |value, type, object| URI.parse(object.__data__('furl')).path }
749
795
 
750
796
  # Hash.
751
- OSA.add_conversion_to_ruby('reco') { |value, type, object| object.is_a?(OSA::ElementRecord) ? object.to_hash : self }
797
+ OSA.add_conversion_to_ruby('reco') { |value, type, object| object.is_a?(OSA::ElementRecord) ? object.to_hash : value }
798
+ OSA.add_conversion_to_osa('record') do |value|
799
+ if value.is_a?(Hash)
800
+ value.each { |key, val| value[key] = OSA::Element.from_rbobj(nil, val, nil) }
801
+ OSA::ElementRecord.__new__(value)
802
+ else
803
+ value
804
+ end
805
+ end
752
806
 
753
807
  # Enumerator.
754
- OSA.add_conversion_to_ruby('enum') { |value, type, object| OSA::Enumerator.enum_for_code(object.__data__('TEXT')) or self }
808
+ OSA.add_conversion_to_ruby('enum') { |value, type, object| OSA::Enumerator.enum_for_code(object.__data__('TEXT')) or object }
755
809
 
756
810
  # Class.
757
- OSA.add_conversion_to_osa('type class') { |value| value.is_a?(Class) and value.ancestors.include?(OSA::Element) ? ['type', value::CODE.to_4cc] : self }
811
+ OSA.add_conversion_to_osa('type class') { |value| value.is_a?(Class) and value.ancestors.include?(OSA::Element) ? ['type', value::CODE.to_4cc] : value }
812
+ OSA.add_conversion_to_ruby('type') do |value, type, object|
813
+ if value == 'msng'
814
+ # Missing values.
815
+ nil
816
+ else
817
+ hash = object.instance_variable_get(:@app).instance_variable_get(:@classes)
818
+ hash[value] or value
819
+ end
820
+ end
758
821
 
759
822
  # QuickDraw Rectangle, aka "bounding rectangle".
760
823
  OSA.add_conversion_to_ruby('qdrt') { |value| value.unpack('S4') }
761
824
  OSA.add_conversion_to_osa('bounding rectangle') { |value| ['qdrt', value.pack('S4')] }
825
+
826
+ # Pictures (just return the raw data).
827
+ OSA.add_conversion_to_ruby('PICT') { |value, type, object| value[222..-1] } # Removing trailing garbage.
828
+ OSA.add_conversion_to_osa('picture') { |value| ['PICT', value.to_s] }
829
+ OSA.add_conversion_to_ruby('imaA') { |value, type, object| value }
830
+ OSA.add_conversion_to_osa('Image') { |value| ['imaA', value.to_s] }
831
+ OSA.add_conversion_to_osa('TIFF picture') { |value| ['TIFF', value.to_s] }
@@ -27,6 +27,7 @@
27
27
  */
28
28
 
29
29
  #include "rbosa.h"
30
+ #include <st.h>
30
31
 
31
32
  static VALUE mOSA;
32
33
  static VALUE cOSAElement;
@@ -194,6 +195,54 @@ rbosa_element_new_os (VALUE self, VALUE desired_class, VALUE container, VALUE ke
194
195
  return rbosa_element_make (self, &obj_specifier, Qnil);
195
196
  }
196
197
 
198
+ static void
199
+ __rbosa_raise_potential_app_error (AEDesc *reply)
200
+ {
201
+ OSErr error;
202
+ AEDesc errorNumDesc;
203
+ AEDesc errorStringDesc;
204
+ int errorNum;
205
+ char exception[128];
206
+
207
+ if (AEGetParamDesc (reply, keyErrorNumber, typeInteger, &errorNumDesc) != noErr)
208
+ return;
209
+
210
+ if (AEGetDescData (&errorNumDesc, &errorNum, sizeof errorNum) != noErr
211
+ || (errorNum = CFSwapInt32HostToBig (errorNum)) == 0) {
212
+
213
+ AEDisposeDesc (&errorNumDesc);
214
+ return;
215
+ }
216
+
217
+ /* The reply is an application error. */
218
+
219
+ exception[0] = '\0';
220
+ error = AEGetParamDesc (reply, keyErrorString, typeChar, &errorStringDesc);
221
+ if (error == noErr) {
222
+ Size size;
223
+
224
+ size = AEGetDescDataSize (&errorStringDesc);
225
+ if (size > 0) {
226
+ char *msg;
227
+
228
+ msg = (char *)malloc (size);
229
+ if (msg != NULL) {
230
+ if (AEGetDescData (&errorStringDesc, &msg, size) == noErr)
231
+ snprintf (exception, sizeof exception, "application returned error %d with message '%s'", errorNum, msg);
232
+ free (msg);
233
+ }
234
+ }
235
+ AEDisposeDesc (&errorStringDesc);
236
+ }
237
+
238
+ if (exception[0] == '\0')
239
+ snprintf (exception, sizeof exception, "application returned error %d", errorNum);
240
+
241
+ AEDisposeDesc (&errorNumDesc);
242
+
243
+ rb_raise (rb_eRuntimeError, exception);
244
+ }
245
+
197
246
  static VALUE
198
247
  rbosa_app_send_event (VALUE self, VALUE event_class, VALUE event_id, VALUE params, VALUE need_retval)
199
248
  {
@@ -249,6 +298,8 @@ rbosa_app_send_event (VALUE self, VALUE event_class, VALUE event_id, VALUE param
249
298
  rb_raise (rb_eRuntimeError, "Cannot send Apple Event '%s%s' : %s (%d)",
250
299
  RVAL2CSTR (event_class), RVAL2CSTR (event_id), GetMacOSStatusErrorString (error), error);
251
300
 
301
+ __rbosa_raise_potential_app_error (&reply);
302
+
252
303
  if (RTEST (need_retval)) {
253
304
  VALUE rb_reply;
254
305
  AEDesc replyObject;
@@ -284,18 +335,24 @@ rbosa_element_data (int argc, VALUE *argv, VALUE self)
284
335
  void * data;
285
336
  Size datasize;
286
337
  VALUE retval;
338
+ bool to_4cc;
287
339
 
288
340
  rb_scan_args (argc, argv, "01", &coerce_type);
341
+ to_4cc = false;
289
342
 
290
343
  desc = rbosa_element_aedesc (self);
291
344
 
292
345
  if (!NIL_P (coerce_type)) {
293
- error = AECoerceDesc (desc, RVAL2FOURCHAR (coerce_type), &coerced_desc);
346
+ FourCharCode code;
347
+
348
+ code = RVAL2FOURCHAR (coerce_type);
349
+ error = AECoerceDesc (desc, code, &coerced_desc);
294
350
  if (error != noErr)
295
351
  rb_raise (rb_eRuntimeError, "Cannot coerce desc to type %s : %s (%d)",
296
352
  RVAL2CSTR (coerce_type), GetMacOSStatusErrorString (error), error);
297
353
 
298
354
  desc = &coerced_desc;
355
+ to_4cc = code == 'type';
299
356
  }
300
357
 
301
358
  datasize = AEGetDescDataSize (desc);
@@ -304,7 +361,14 @@ rbosa_element_data (int argc, VALUE *argv, VALUE self)
304
361
  rb_fatal ("cannot allocate memory");
305
362
 
306
363
  error = AEGetDescData (desc, data, datasize);
307
- retval = error == noErr ? rb_str_new (data, datasize) : Qnil;
364
+ if (error == noErr) {
365
+ if (to_4cc)
366
+ *(DescType*)data = CFSwapInt32HostToBig (*(DescType*)data);
367
+ retval = rb_str_new (data, datasize);
368
+ }
369
+ else {
370
+ retval = Qnil;
371
+ }
308
372
 
309
373
  if (!NIL_P (coerce_type))
310
374
  AEDisposeDesc (&coerced_desc);
@@ -482,6 +546,42 @@ rbosa_elementlist_size (VALUE self)
482
546
  return INT2FIX (__rbosa_elementlist_count ((AEDescList *)rbosa_element_aedesc (self)));
483
547
  }
484
548
 
549
+ static int
550
+ __rbosa_elementrecord_set (VALUE key, VALUE value, AEDescList *list)
551
+ {
552
+ OSErr error;
553
+
554
+ error = AEPutKeyDesc (list, RVAL2FOURCHAR (key), rbosa_element_aedesc (value));
555
+ if (error != noErr)
556
+ rb_raise (rb_eRuntimeError, "Cannot set value %p for key %p of record %p: %s (%d)",
557
+ value, key, list, GetMacOSStatusErrorString (error), error);
558
+
559
+ return ST_CONTINUE;
560
+ }
561
+
562
+ static VALUE
563
+ rbosa_elementrecord_new (int argc, VALUE *argv, VALUE self)
564
+ {
565
+ OSErr error;
566
+ AEDescList list;
567
+ VALUE hash;
568
+
569
+ rb_scan_args (argc, argv, "01", &hash);
570
+
571
+ if (!NIL_P (hash))
572
+ Check_Type (hash, T_HASH);
573
+
574
+ error = AECreateList (NULL, 0, true, &list);
575
+ if (error != noErr)
576
+ rb_raise (rb_eRuntimeError, "Cannot create Apple Event descriptor list : %s (%d)",
577
+ GetMacOSStatusErrorString (error), error);
578
+
579
+ if (!NIL_P (hash))
580
+ rb_hash_foreach (hash, __rbosa_elementrecord_set, (VALUE)&list);
581
+
582
+ return rbosa_element_make (self, &list, Qnil);
583
+ }
584
+
485
585
  static VALUE
486
586
  rbosa_elementrecord_to_a (VALUE self)
487
587
  {
@@ -541,6 +641,7 @@ Init_osa (void)
541
641
  rb_define_method (cOSAElementList, "add", rbosa_elementlist_add, 1);
542
642
 
543
643
  cOSAElementRecord = rb_define_class_under (mOSA, "ElementRecord", cOSAElement);
644
+ rb_define_singleton_method (cOSAElementRecord, "__new__", rbosa_elementrecord_new, -1);
544
645
  rb_define_method (cOSAElementRecord, "to_a", rbosa_elementrecord_to_a, 0);
545
646
 
546
647
  mOSAEventDispatcher = rb_define_module_under (mOSA, "EventDispatcher");
@@ -548,4 +649,5 @@ Init_osa (void)
548
649
 
549
650
  rbosa_define_param ("timeout", INT2NUM (kAEDefaultTimeout));
550
651
  rbosa_define_param ("lazy_events", Qtrue);
652
+ rbosa_define_param ("utf8_strings", Qfalse);
551
653
  }
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: rubyosa
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2006-10-25 00:00:00 +02:00
6
+ version: 0.2.0
7
+ date: 2007-01-02 00:00:00 +01:00
8
8
  summary: A Ruby/AppleEvent bridge.
9
9
  require_paths:
10
10
  - lib
@@ -45,6 +45,10 @@ files:
45
45
  - sample/iTunes_inspect.rb
46
46
  - sample/QT_playall.rb
47
47
  - sample/sdef.rb
48
+ - sample/BBEdit_unix_script.rb
49
+ - sample/TextEdit_hello_world.rb
50
+ - sample/iChat_image.rb
51
+ - sample/iTunes_artwork.rb
48
52
  test_files: []
49
53
 
50
54
  rdoc_options: []