Module: Plist4r::Backend::RubyCocoa

Defined in:
lib/plist4r/backend/ruby_cocoa.rb

Class Methods

Overview

This backend only works on MacOSX. It supports everything except Example.to_gnustep, and saving in the :gnustep file format. This is Because RubyCocoa uses the NSPropertyListSerialization class, which doesnt support writing to OpenStep Format.

Here we are calling the stock OSX Ruby in a seperate process. It isolates the runtime from any shared lib (.so) LoadErrors. And allows calling from other installed Ruby instances (eg REE), which dont usually have RubyCocoa enabled.

This Backend should work for any 10.5 (Leopard), 10.6 (Snow Leopard) Mac OSX distribution. It will do nothing on non-mac platforms (eg Linux etc).

Author:

Class Method Details

+ (Object) convert_19_to_hash(hash, key = nil)



263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 263

def convert_19_to_hash hash, key=nil
  h = Hash.new
  h.replace hash

  hash.each do |key,value|
    if value.class.ancestors.include? Hash
      unless value.class == Hash
        h[key] = convert_19_to_hash(value)
      end
    end
  end
  h
end

+ (Object) from_binary(plist)



356
357
358
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 356

def from_binary plist
  from_string plist
end

+ (Object) from_gnustep(plist)



360
361
362
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 360

def from_gnustep plist
  from_string plist
end

+ (Object) from_string(plist)



343
344
345
346
347
348
349
350
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 343

def from_string plist
  require 'tempfile'
  tf = Tempfile.new "from_string.plist."
  tf.write plist.from_string
  tf.close
  filename = tf.path
  return open_with_args plist, filename
end

+ (Object) from_xml(plist)



352
353
354
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 352

def from_xml plist
  from_string plist
end

+ (Object) open_with_args(plist, filename)



328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 328

def open_with_args plist, filename
  result = ruby_cocoa_exec "open(\"#{filename}\")"
  case result[1].exitstatus
  when 0
    hash = Plist4r::OrderedHash.new
    
    hash.replace Marshal.load(read_result_file)
    plist.import_hash hash
  else
    $stderr.puts result[3]
    raise "Error executing #{result[0]}. See stderr for more information"
  end
  return plist
end

+ (Object) read_result_file



259
260
261
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 259

def read_result_file
  File.read @result_file.path
end

+ (Array) ruby_cocoa_exec(stdin_str)

Write a temporary script to the filesystem, then execute it

Parameters:

  • (String) stdin_str

    The command to run

Returns:

  • (Array)

    cmd, status, stdout_result, stderr_result



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 216

def ruby_cocoa_exec stdin_str
  rubycocoa_framework = "/System/Library/Frameworks/RubyCocoa.framework"
  raise "RubyCocoa Framework not found. Searched in: #{rubycocoa_framework}" unless File.exists? rubycocoa_framework

  require 'tempfile'
  require 'plist4r/mixin/popen4'

  unless @rb_script && File.exists?(@rb_script.path)
    @rb_script = Tempfile.new "ruby_cocoa_wrapper.rb."
    @rb_script.puts ruby_cocoa_wrapper_rb
    @rb_script.close
    File.chmod 0755, @rb_script.path
  end

  cmd = @rb_script.path
  plist4r_root = File.expand_path File.join(File.dirname(__FILE__), "..", "..")
  @result_file = Tempfile.new("result_file.rb")
  @result_file.close

  pid, stdin, stdout, stderr = ::Plist4r::Popen4::popen4 [cmd, plist4r_root, @result_file.path]

    stdin.puts stdin_str
    stdin.close

    ignored, status = [nil,nil]

    begin
      Timeout::timeout(Plist4r::Config[:backend_timeout]) do
        ignored, status = Process::waitpid2 pid
      end
    rescue Timeout::Error => exc
      puts "#{exc.message}, killing pid #{pid}"
      Process::kill('TERM', pid)
      # Process::kill('HUP', pid)
      ignored, status = Process::waitpid2 pid
    end

    stdout_result = stdout.read.strip
    stderr_result = stderr.read.strip

  return [cmd, status, stdout_result, stderr_result]    
end

+ (Object) ruby_cocoa_wrapper_rb

A seperate ruby script, which runs in its own process.

See Also:



62
63
64
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 62

def ruby_cocoa_wrapper_rb
  @ruby_cocoa_wrapper_rb ||= "#!/usr/bin/ruby\nraise \"No path given to plist4r\" unless ARGV[0] && File.exists?(\"\#{ARGV[0]}/plist4r.rb\")\n\ndir = ARGV[0]\n$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)\n\nrequire 'osx/cocoa'\nrequire 'time'\nrequire 'plist4r/mixin/ordered_hash'\nrequire 'plist4r/mixin/ruby_stdlib'\n\nmodule Plist4r\nclass StringIOData\nattr_accessor :string\ndef initialize string\n@string = string\nend\n\ndef to_s\n@string\nend\n\ndef _dump arg\n@string\nend\n\ndef self._load string\nOSX::NSData.dataWithRubyString(string)\nend\nend\nend\n\nclass String\ndef _dump arg\nself\nend\n\ndef self._load string\nString.new(string)\nend\nend\n\n# Property list API.\nmodule OSX\ndef object_to_plist(object, format=nil)\nformat ||= OSX::NSPropertyListXMLFormat_v1_0\ndata, error = OSX::NSPropertyListSerialization.objc_send \\\n:dataFromPropertyList, object,\n:format, format,\n:errorDescription\nraise error.to_s if data.nil?\ncase format\nwhen OSX::NSPropertyListXMLFormat_v1_0,\nOSX::NSPropertyListOpenStepFormat\nOSX::NSString.alloc.initWithData_encoding(data,\nOSX::NSUTF8StringEncoding).to_s\nelse\ndata.bytes.bytestr(data.length)\nend\nend\nmodule_function :object_to_plist\nend\n\nclass OSX::NSObject\ndef to_ruby\ncase self\nwhen OSX::NSDate\nself.to_time\nwhen OSX::NSCFBoolean\nself.boolValue\nwhen OSX::NSNumber\nself.integer? ? self.to_i : self.to_f\nwhen OSX::NSString\nself.to_s\nwhen OSX::NSData\nPlist4r::StringIOData.new self.rubyString\nwhen OSX::NSAttributedString\nself.string.to_s\nwhen OSX::NSArray\nself.to_a.map { |x| x.is_a?(OSX::NSObject) ? x.to_ruby : x }\nwhen OSX::NSDictionary\nh = Plist4r::ActiveSupport::OrderedHash.new\nself.each do |x, y|\nx = x.to_ruby if x.is_a?(OSX::NSObject)\ny = y.to_ruby if y.is_a?(OSX::NSObject)\nh[x] = y\nend\nh\nelse\nself\nend\nend\nend\n\nmodule Plist\ndef write_result_file obj\nFile.open @result_file, 'w' do |o|\no << obj\nend\nend\n\ndef to_xml input_file\n# to_plist defaults to NSPropertyListXMLFormat_v1_0\nhash = Marshal.load(File.read(input_file))\nx = hash.to_plist\n# x = hash.inspect\nwrite_result_file x\nend\n\ndef to_binary input_file\n# Here 200 == NSPropertyListBinaryFormat_v1_0\nhash = Marshal.load(File.read(input_file))\nx = hash.to_plist 200\n# x = hash.inspect\nwrite_result_file x\nend\n\ndef open filename\nplist_dict = ::OSX::NSDictionary.dictionaryWithContentsOfFile(filename)\nunless plist_dict\nplist_array = ::OSX::NSArray.arrayWithContentsOfFile(filename) unless plist_dict\nraise \"Couldnt parse file: \#{filename}\" unless plist_array\nplist_dict = Plist4r::ActiveSupport::OrderedHash.new\nplist_dict[\"Array\"] = plist_array.to_ruby\nend\n\nFile.open @result_file, 'w' do |o|\no.write Marshal.dump(plist_dict.to_ruby)\nend\nend\nend\n\nclass RubyCocoaWrapper\ninclude Plist\n\ndef exec stdin\n@result_file = ARGV[1]\ninstance_eval stdin\nexit 0\nend\nend\n\nstdin = $stdin.read()\nwrapper = RubyCocoaWrapper.new()\nwrapper.exec stdin\n"
end

+ (Object) to_binary(plist)



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 303

def to_binary plist
  require 'tempfile'
  input_file = Tempfile.new "input_file.rb."

  if RUBY_VERSION >= '1.9'
    old_style_hash = convert_19_to_hash(plist.to_hash)
    input_file.puts Marshal.dump(old_style_hash)
  else
    input_file.puts Marshal.dump(plist.to_hash)
  end

  input_file.close
  result = ruby_cocoa_exec "to_binary(\"#{input_file.path}\")"
  # print read_result_file

  case result[1].exitstatus
  when 0
    binary_string = read_result_file
    return binary_string
  else
    $stderr.puts result[3]
    raise "Error executing #{result[0]}. See stderr for more information"
  end
end

+ (Object) to_xml(plist)



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 277

def to_xml plist
  require 'tempfile'
  input_file = Tempfile.new "input_file.rb."

  oh = nil
  if RUBY_VERSION >= '1.9'
    old_style_hash = convert_19_to_hash(plist.to_hash)
    input_file.puts Marshal.dump(old_style_hash)
  else
    input_file.puts Marshal.dump(plist.to_hash)
  end

  input_file.close
  result = ruby_cocoa_exec "to_xml(\"#{input_file.path}\")"
  # print read_result_file

  case result[1].exitstatus
  when 0
    xml_string = read_result_file
    return xml_string
  else
    $stderr.puts result[3]
    raise "Error executing #{result[0]}. See stderr for more information"
  end
end