Module: Plist4r::Backend::RubyCocoa

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

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 Summary collapse

Class Method Details

.convert_19_to_hash(hash, key = nil) ⇒ Object



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

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

.from_binary(plist) ⇒ Object



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

def from_binary plist
  from_string plist
end

.from_gnustep(plist) ⇒ Object



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

def from_gnustep plist
  from_string plist
end

.from_string(plist) ⇒ Object



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

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

.from_xml(plist) ⇒ Object



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

def from_xml plist
  from_string plist
end

.open_with_args(plist, filename) ⇒ Object



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

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

.read_result_fileObject



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

def read_result_file
  File.read @result_file.path
end

.ruby_cocoa_exec(stdin_str) ⇒ Array

Write a temporary script to the filesystem, then execute it



215
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
# File 'lib/plist4r/backend/ruby_cocoa.rb', line 215

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

.ruby_cocoa_wrapper_rbObject

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

See Also:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# 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\n  class 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\n  OSX::NSData.dataWithRubyString(string)\nend\n  end\nend\n\nclass String\n  def _dump arg\nself\n  end\n  \n  def self._load string\nString.new(string)\n  end\nend\n\n# Property list API.\nmodule OSX\n  def 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\n  when OSX::NSPropertyListXMLFormat_v1_0, \n       OSX::NSPropertyListOpenStepFormat\n    OSX::NSString.alloc.initWithData_encoding(data, \n      OSX::NSUTF8StringEncoding).to_s\n  else\n    data.bytes.bytestr(data.length)\nend\n  end\n  module_function :object_to_plist\nend\n\nclass OSX::NSObject\n  def to_ruby\ncase self\nwhen OSX::NSDate\n  self.to_time\nwhen OSX::NSCFBoolean\n  self.boolValue\nwhen OSX::NSNumber\n  self.integer? ? self.to_i : self.to_f\nwhen OSX::NSString\n  self.to_s\nwhen OSX::NSData\n  Plist4r::StringIOData.new self.rubyString\nwhen OSX::NSAttributedString\n  self.string.to_s\nwhen OSX::NSArray\n  self.to_a.map { |x| x.is_a?(OSX::NSObject) ? x.to_ruby : x }\nwhen OSX::NSDictionary\n  h = Plist4r::ActiveSupport::OrderedHash.new\n  self.each do |x, y| \n    x = x.to_ruby if x.is_a?(OSX::NSObject)\n    y = y.to_ruby if y.is_a?(OSX::NSObject)\n    h[x] = y\n  end\n  h\nelse\n  self\nend\n  end\nend\n\nmodule Plist  \n  def write_result_file obj\nFile.open @result_file, 'w' do |o|\n  o << obj\nend\n  end\n\n  def 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\n  end\n\n  def 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\n  end\n\n  def open filename\nplist_dict = ::OSX::NSDictionary.dictionaryWithContentsOfFile(filename)\nunless plist_dict\n  plist_array = ::OSX::NSArray.arrayWithContentsOfFile(filename) unless plist_dict\n  raise \"Couldnt parse file: \#{filename}\" unless plist_array\n  plist_dict = Plist4r::ActiveSupport::OrderedHash.new\n  plist_dict[\"Array\"] = plist_array.to_ruby\nend\n\nFile.open @result_file, 'w' do |o|\n  o.write Marshal.dump(plist_dict.to_ruby)\nend\n  end\nend\n\nclass RubyCocoaWrapper\n  include Plist\n\n  def exec stdin\n@result_file = ARGV[1]\ninstance_eval stdin\nexit 0\n  end\nend\n\nstdin = $stdin.read()\nwrapper = RubyCocoaWrapper.new()\nwrapper.exec stdin\n"
end

.to_binary(plist) ⇒ Object



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

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

.to_xml(plist) ⇒ Object



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

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