plist4r 0.2.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/.gitignore +4 -0
  2. data/.yardopts +11 -0
  3. data/LICENSE +3 -1
  4. data/README.rdoc +25 -122
  5. data/Rakefile +14 -0
  6. data/VERSION +1 -1
  7. data/bin/plist4r +2 -0
  8. data/ext/osx_plist/Makefile +157 -0
  9. data/ext/osx_plist/extconf.rb +9 -0
  10. data/ext/osx_plist/plist.c +606 -0
  11. data/ext/osx_plist/plist.o +0 -0
  12. data/lib/plist4r.rb +6 -3
  13. data/lib/plist4r/application.rb +1 -2
  14. data/lib/plist4r/backend.rb +102 -34
  15. data/lib/plist4r/backend/c_f_property_list.rb +65 -0
  16. data/lib/plist4r/backend/c_f_property_list/LICENSE +19 -0
  17. data/lib/plist4r/backend/c_f_property_list/README +34 -0
  18. data/lib/plist4r/backend/c_f_property_list/cfpropertylist.rb +6 -0
  19. data/lib/plist4r/backend/c_f_property_list/rbBinaryCFPropertyList.rb +663 -0
  20. data/lib/plist4r/backend/c_f_property_list/rbCFPlistError.rb +26 -0
  21. data/lib/plist4r/backend/c_f_property_list/rbCFPropertyList.rb +348 -0
  22. data/lib/plist4r/backend/c_f_property_list/rbCFTypes.rb +241 -0
  23. data/lib/plist4r/backend/c_f_property_list/rbXMLCFPropertyList.rb +116 -0
  24. data/lib/plist4r/backend/example.rb +37 -52
  25. data/lib/plist4r/backend/haml.rb +47 -36
  26. data/lib/plist4r/backend/libxml4r.rb +24 -20
  27. data/lib/plist4r/backend/osx_plist.rb +82 -0
  28. data/lib/plist4r/backend/ruby_cocoa.rb +172 -54
  29. data/lib/plist4r/backend/test/data_types.rb +163 -0
  30. data/lib/plist4r/backend/test/harness.rb +255 -0
  31. data/lib/plist4r/backend/test/output.rb +47 -0
  32. data/lib/plist4r/backend_base.rb +4 -2
  33. data/lib/plist4r/{options.rb → cli.rb} +2 -1
  34. data/lib/plist4r/commands.rb +13 -8
  35. data/lib/plist4r/config.rb +36 -9
  36. data/lib/plist4r/docs/Backends.html +59 -0
  37. data/lib/plist4r/docs/DeveloperGuide.rdoc +53 -0
  38. data/lib/plist4r/docs/EditingPlistFiles.rdoc +88 -0
  39. data/lib/plist4r/docs/InfoPlistExample.rdoc +33 -0
  40. data/lib/plist4r/docs/LaunchdPlistExample.rdoc +33 -0
  41. data/lib/plist4r/docs/PlistKeyNames.rdoc +47 -0
  42. data/lib/plist4r/mixin/array_dict.rb +61 -0
  43. data/lib/plist4r/mixin/data_methods.rb +178 -54
  44. data/lib/plist4r/mixin/haml4r.rb +4 -0
  45. data/lib/plist4r/mixin/haml4r/css_attributes.rb +19 -0
  46. data/lib/plist4r/mixin/haml4r/examples.rb +261 -0
  47. data/lib/plist4r/mixin/haml4r/haml_table_example.rb +79 -0
  48. data/lib/plist4r/mixin/haml4r/table.rb +157 -0
  49. data/lib/plist4r/mixin/haml4r/table_cell.rb +160 -0
  50. data/lib/plist4r/mixin/haml4r/table_cells.rb +485 -0
  51. data/lib/plist4r/mixin/haml4r/table_section.rb +101 -0
  52. data/lib/plist4r/mixin/ordered_hash.rb +9 -1
  53. data/lib/plist4r/mixin/popen4.rb +1 -1
  54. data/lib/plist4r/mixin/ruby_stdlib.rb +154 -1
  55. data/lib/plist4r/mixin/script.rb +133 -0
  56. data/lib/plist4r/mixin/table.rb +435 -0
  57. data/lib/plist4r/plist.rb +272 -94
  58. data/lib/plist4r/plist_cache.rb +42 -43
  59. data/lib/plist4r/plist_type.rb +31 -74
  60. data/lib/plist4r/plist_type/info.rb +157 -3
  61. data/lib/plist4r/plist_type/launchd.rb +54 -48
  62. data/lib/plist4r/plist_type/plist.rb +1 -3
  63. data/plist4r.gemspec +74 -14
  64. data/spec/{examples.rb → launchd_examples.rb} +131 -139
  65. data/spec/plist4r/application_spec.rb +37 -0
  66. data/spec/plist4r/backend_spec.rb +256 -0
  67. data/spec/plist4r/cli_spec.rb +25 -0
  68. data/spec/plist4r/commands_spec.rb +20 -0
  69. data/spec/plist4r/config_spec.rb +38 -0
  70. data/spec/plist4r/mixin/array_dict_spec.rb +120 -0
  71. data/spec/plist4r/mixin/data_methods_spec.rb +96 -0
  72. data/spec/plist4r/mixin/haml4r/examples.rb +261 -0
  73. data/spec/plist4r/mixin/ruby_stdlib_spec.rb +228 -0
  74. data/spec/plist4r/plist_cache_spec.rb +261 -0
  75. data/spec/plist4r/plist_spec.rb +841 -23
  76. data/spec/plist4r/plist_type_spec.rb +126 -0
  77. data/spec/plist4r_spec.rb +53 -27
  78. data/spec/scratchpad.rb +226 -0
  79. data/spec/spec_helper.rb +5 -1
  80. metadata +109 -23
  81. data/lib/plist4r/backend/plutil.rb +0 -25
  82. data/lib/plist4r/mixin.rb +0 -7
  83. data/plists/array_mini.xml +0 -14
  84. data/plists/example_big_binary.plist +0 -0
  85. data/plists/example_medium_binary_launchd.plist +0 -0
  86. data/plists/example_medium_launchd.xml +0 -53
  87. data/plists/mini.xml +0 -12
  88. data/test.rb +0 -40
@@ -1,15 +1,60 @@
1
1
 
2
2
  require 'plist4r/backend_base'
3
+ require 'plist4r/mixin/ruby_stdlib'
3
4
 
4
- # This backend only works on MacOSX. It supports everything except {Backend::Example.to_next_step},
5
- # and saving in the :next_step file format. Here we are calling the stock OSX Ruby in a seperate process.
5
+ module Plist4r
6
+ class StringIOData
7
+ attr_accessor :string
8
+ def initialize string
9
+ @string = string
10
+ end
11
+
12
+ def to_s
13
+ @string
14
+ end
15
+
16
+ def _dump arg
17
+ @string
18
+ end
19
+
20
+ def self._load string
21
+ bstr = String.new(string)
22
+ bstr.blob = true
23
+ bstr
24
+ end
25
+ end
26
+ end
27
+
28
+ class String
29
+ def _dump arg
30
+ if self.blob?
31
+ string_io_data = Plist4r::StringIOData.new(self)
32
+ string_io_data._dump arg
33
+ else
34
+ self
35
+ end
36
+ end
37
+
38
+ def self._load string
39
+ String.new string
40
+ end
41
+ end
42
+
43
+ # This backend only works on MacOSX. It supports everything except {Backend::Example.to_gnustep},
44
+ # and saving in the :gnustep file format. This is Because RubyCocoa uses the
45
+ # NSPropertyListSerialization class, which doesnt support writing to OpenStep Format.
46
+ #
47
+ # Here we are calling the stock OSX Ruby in a seperate process.
6
48
  # It isolates the runtime from any shared lib (.so) LoadErrors. And allows calling from other installed
7
49
  # Ruby instances (eg REE), which dont usually have RubyCocoa enabled.
8
50
  #
9
51
  # This Backend should work for any 10.5 (Leopard), 10.6 (Snow Leopard) Mac OSX distribution.
10
- # It will do nothing on non-mac platforms (eg Linux, etc).
52
+ # It will do nothing on non-mac platforms (eg Linux etc).
53
+ # @author Dreamcat4 (dreamcat4@gmail.com)
11
54
  module Plist4r::Backend::RubyCocoa
12
55
  class << self
56
+ # A seperate ruby script, which runs in its own process.
57
+ # @see ruby_cocoa_exec
13
58
  def ruby_cocoa_wrapper_rb
14
59
  @ruby_cocoa_wrapper_rb ||= <<-'EOC'
15
60
  #!/usr/bin/ruby
@@ -18,8 +63,62 @@ raise "No path given to plist4r" unless ARGV[0] && File.exists?("#{ARGV[0]}/plis
18
63
  dir = ARGV[0]
19
64
  $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
20
65
 
21
- require 'plist4r/mixin/ordered_hash'
22
66
  require 'osx/cocoa'
67
+ require 'date'
68
+ require 'plist4r/mixin/ordered_hash'
69
+ require 'plist4r/mixin/ruby_stdlib'
70
+
71
+ module Plist4r
72
+ class StringIOData
73
+ attr_accessor :string
74
+ def initialize string
75
+ @string = string
76
+ end
77
+
78
+ def to_s
79
+ @string
80
+ end
81
+
82
+ def _dump arg
83
+ @string
84
+ end
85
+
86
+ def self._load string
87
+ OSX::NSData.dataWithRubyString(string)
88
+ end
89
+ end
90
+ end
91
+
92
+ class String
93
+ def _dump arg
94
+ self
95
+ end
96
+
97
+ def self._load string
98
+ String.new(string)
99
+ end
100
+ end
101
+
102
+ # Property list API.
103
+ module OSX
104
+ def object_to_plist(object, format=nil)
105
+ format ||= OSX::NSPropertyListXMLFormat_v1_0
106
+ data, error = OSX::NSPropertyListSerialization.objc_send \
107
+ :dataFromPropertyList, object,
108
+ :format, format,
109
+ :errorDescription
110
+ raise error.to_s if data.nil?
111
+ case format
112
+ when OSX::NSPropertyListXMLFormat_v1_0,
113
+ OSX::NSPropertyListOpenStepFormat
114
+ OSX::NSString.alloc.initWithData_encoding(data,
115
+ OSX::NSUTF8StringEncoding).to_s
116
+ else
117
+ data.bytes.bytestr(data.length)
118
+ end
119
+ end
120
+ module_function :object_to_plist
121
+ end
23
122
 
24
123
  class OSX::NSObject
25
124
  def to_ruby
@@ -32,6 +131,8 @@ class OSX::NSObject
32
131
  self.integer? ? self.to_i : self.to_f
33
132
  when OSX::NSString
34
133
  self.to_s
134
+ when OSX::NSData
135
+ Plist4r::StringIOData.new self.rubyString
35
136
  when OSX::NSAttributedString
36
137
  self.string.to_s
37
138
  when OSX::NSArray
@@ -50,44 +151,39 @@ class OSX::NSObject
50
151
  end
51
152
  end
52
153
 
53
- module Plist
54
- def to_xml hash
154
+ module Plist
155
+ def write_result_file obj
156
+ File.open @result_file, 'w' do |o|
157
+ o << obj
158
+ end
159
+ end
160
+
161
+ def to_xml input_file
55
162
  # to_plist defaults to NSPropertyListXMLFormat_v1_0
163
+ hash = Marshal.load(File.read(input_file))
56
164
  x = hash.to_plist
57
- puts "#{x}"
165
+ write_result_file x
58
166
  end
59
167
 
60
- def to_binary hash
168
+ def to_binary input_file
61
169
  # Here 200 == NSPropertyListBinaryFormat_v1_0
170
+ hash = Marshal.load(File.read(input_file))
62
171
  x = hash.to_plist 200
63
- puts "#{x}"
172
+ write_result_file x
64
173
  end
65
174
 
66
175
  def open filename
67
176
  plist_dict = ::OSX::NSDictionary.dictionaryWithContentsOfFile(filename)
68
- if plist_dict
69
- puts "#{plist_dict.to_ruby.inspect}"
70
- else
177
+ unless plist_dict
71
178
  plist_array = ::OSX::NSArray.arrayWithContentsOfFile(filename) unless plist_dict
72
179
  raise "Couldnt parse file: #{filename}" unless plist_array
73
180
  plist_dict = ::Plist4r::OrderedHash.new
74
181
  plist_dict["Array"] = plist_array.to_ruby
75
- puts "#{plist_dict.inspect}"
76
182
  end
77
- end
78
183
 
79
- def save hash, filename, file_format
80
- case file_format.to_sym
81
- when :xml
82
- x = hash.to_plist # NSPropertyListXMLFormat_v1_0
83
- when :binary
84
- x = hash.to_plist 200 # NSPropertyListBinaryFormat_v1_0
85
- when :next_step
86
- raise "File format #{file_format.inspect} is not supported by RubyCocoa"
87
- else
88
- raise "File format #{file_format.inspect} not recognised"
184
+ File.open @result_file, 'w' do |o|
185
+ o.write Marshal.dump(plist_dict.to_ruby)
89
186
  end
90
- File.open(filename,'w'){ |o| o << x }
91
187
  end
92
188
  end
93
189
 
@@ -95,6 +191,7 @@ class RubyCocoaWrapper
95
191
  include Plist
96
192
 
97
193
  def exec stdin
194
+ @result_file = ARGV[1]
98
195
  instance_eval stdin
99
196
  exit 0
100
197
  end
@@ -106,6 +203,9 @@ wrapper.exec stdin
106
203
  EOC
107
204
  end
108
205
 
206
+ # Write a temporary script to the filesystem, then execute it
207
+ # @param [String] stdin_str The command to run
208
+ # @return [Array] cmd, status, stdout_result, stderr_result
109
209
  def ruby_cocoa_exec stdin_str
110
210
  rubycocoa_framework = "/System/Library/Frameworks/RubyCocoa.framework"
111
211
  raise "RubyCocoa Framework not found. Searched in: #{rubycocoa_framework}" unless File.exists? rubycocoa_framework
@@ -119,16 +219,29 @@ EOC
119
219
  @rb_script.close
120
220
  File.chmod 0755, @rb_script.path
121
221
  end
122
-
123
- plist4r_root = File.expand_path File.join(File.dirname(__FILE__), "..", "..")
222
+
124
223
  cmd = @rb_script.path
224
+ plist4r_root = File.expand_path File.join(File.dirname(__FILE__), "..", "..")
225
+ @result_file = Tempfile.new("result_file.rb")
226
+ @result_file.close
125
227
 
126
- pid, stdin, stdout, stderr = ::Plist4r::Popen4::popen4 [cmd, plist4r_root]
228
+ pid, stdin, stdout, stderr = ::Plist4r::Popen4::popen4 [cmd, plist4r_root, @result_file.path]
127
229
 
128
230
  stdin.puts stdin_str
129
-
130
231
  stdin.close
131
- ignored, status = Process::waitpid2 pid
232
+
233
+ ignored, status = [nil,nil]
234
+
235
+ begin
236
+ Timeout::timeout(Plist4r::Config[:backend_timeout]) do
237
+ ignored, status = Process::waitpid2 pid
238
+ end
239
+ rescue Timeout::Error => exc
240
+ puts "#{exc.message}, killing pid #{pid}"
241
+ Process::kill('TERM', pid)
242
+ # Process::kill('HUP', pid)
243
+ ignored, status = Process::waitpid2 pid
244
+ end
132
245
 
133
246
  stdout_result = stdout.read.strip
134
247
  stderr_result = stderr.read.strip
@@ -136,12 +249,20 @@ EOC
136
249
  return [cmd, status, stdout_result, stderr_result]
137
250
  end
138
251
 
252
+ def read_result_file
253
+ File.read @result_file.path
254
+ end
255
+
139
256
  def to_xml plist
140
- hash = plist.to_hash
141
- result = ruby_cocoa_exec "to_xml(#{hash.inspect})"
257
+ require 'tempfile'
258
+ input_file = Tempfile.new "input_file.rb."
259
+ input_file.puts Marshal.dump(plist.to_hash)
260
+ input_file.close
261
+ result = ruby_cocoa_exec "to_xml(\"#{input_file.path}\")"
262
+
142
263
  case result[1].exitstatus
143
264
  when 0
144
- xml_string = result[2]
265
+ xml_string = read_result_file
145
266
  return xml_string
146
267
  else
147
268
  $stderr.puts result[3]
@@ -150,31 +271,35 @@ EOC
150
271
  end
151
272
 
152
273
  def to_binary plist
153
- hash = plist.to_hash
154
- result = ruby_cocoa_exec "to_binary(#{hash.inspect})"
274
+ require 'tempfile'
275
+ input_file = Tempfile.new "input_file.rb."
276
+ input_file.puts Marshal.dump(plist.to_hash)
277
+ input_file.close
278
+ result = ruby_cocoa_exec "to_binary(\"#{input_file.path}\")"
279
+
155
280
  case result[1].exitstatus
156
281
  when 0
157
- binary_string = result[2]
282
+ binary_string = read_result_file
158
283
  return binary_string
159
284
  else
160
285
  $stderr.puts result[3]
161
286
  raise "Error executing #{result[0]}. See stderr for more information"
162
287
  end
163
288
  end
164
-
289
+
165
290
  def open_with_args plist, filename
291
+ require 'date'
292
+
166
293
  result = ruby_cocoa_exec "open(\"#{filename}\")"
167
294
  case result[1].exitstatus
168
295
  when 0
169
296
  hash = ::Plist4r::OrderedHash.new
170
- eval("hash.replace("+result[2]+")")
297
+ hash.replace Marshal.load(read_result_file)
171
298
  plist.import_hash hash
172
299
  else
173
300
  $stderr.puts result[3]
174
301
  raise "Error executing #{result[0]}. See stderr for more information"
175
302
  end
176
- file_format = Plist4r.file_detect_format filename
177
- plist.file_format file_format
178
303
  return plist
179
304
  end
180
305
 
@@ -187,23 +312,16 @@ EOC
187
312
  return open_with_args plist, filename
188
313
  end
189
314
 
190
- def open plist
191
- return open_with_args plist, plist.filename
315
+ def from_xml plist
316
+ from_string plist
192
317
  end
193
318
 
194
- def save hash, filename, file_format
195
- filename = plist.filename_path
196
- file_format = plist.file_format || Config[:default_format]
197
- raise "#{self} - cant save file of format #{file_format}" unless [:xml,:binary].include? file_format
319
+ def from_binary plist
320
+ from_string plist
321
+ end
198
322
 
199
- hash = plist.to_hash
200
- result = ruby_cocoa_exec "save(\"#{hash.inspect}\",#{filename},#{file_format})"
201
- case result[1].exitstatus
202
- when 0
203
- return true
204
- else
205
- raise "Error executing #{result[0]}. See stderr for more information"
206
- end
323
+ def from_gnustep plist
324
+ from_string plist
207
325
  end
208
326
  end
209
327
  end
@@ -0,0 +1,163 @@
1
+
2
+ module Plist4r
3
+ class Backend
4
+ module Test
5
+
6
+ class DataTypes
7
+ PlistDataTypes = [:bool, :integer, :float, :string, :time, :array, :hash, :data]
8
+
9
+ def initialize *args, &blk
10
+ @plists = Plist4r::OrderedHash.new
11
+ PlistDataTypes.each do |pdt|
12
+ @plist = Plist4r.new
13
+ self.send "gen_#{pdt}"
14
+ @plists[pdt] = @plist
15
+ end
16
+
17
+ @plist_1024 = gen_plist_1024
18
+ end
19
+
20
+ def method_missing meth, *args, &blk
21
+ @plists[meth] if PlistDataTypes.include?(meth)
22
+ end
23
+
24
+ def plists
25
+ @plists
26
+ end
27
+
28
+ def plist_1024
29
+ @plist_1024
30
+ end
31
+
32
+ def gen_bool
33
+ [true,false].each do |bool|
34
+ @plist.store "BoolKey#{bool.to_s.capitalize}", bool
35
+ end
36
+ end
37
+
38
+ def gen_integer
39
+ (0..9).each do |i|
40
+ @plist.store "IntegerKey#{(65+i).chr}", i
41
+ end
42
+ end
43
+
44
+ def gen_float
45
+ (0..100).each do |i|
46
+ @plist.store "RealKey#{i}", i.to_f / 100
47
+ end
48
+ end
49
+
50
+ def gen_string
51
+ (0..25).each do |i|
52
+ @plist.store "StringKey#{(65+i).chr}", "#{(97+i).chr}"*(i+1)
53
+ end
54
+ end
55
+
56
+ def gen_time
57
+ require 'date'
58
+ (0..25).each do |i|
59
+ @plist.store "DateKey#{(65+i).chr}", Time.parse("2010-04-#{sprintf("%.2i",i+1)}T19:50:01Z")
60
+ end
61
+ end
62
+
63
+ def gen_data
64
+ # a = []
65
+ (0..25).each do |i|
66
+ s = "1"*i
67
+ bstr = "DataBytes#{s}"
68
+ bstr.blob = true
69
+ @plist.store "DataKey#{(65+i).chr}", bstr
70
+ # a << bstr
71
+ end
72
+ # @plist.store "BinaryArray", a
73
+ end
74
+
75
+ def gen_array
76
+ (0..25).each do |i|
77
+ a = []
78
+ (0..i).each do |j|
79
+ a << "String#{(65+j).chr}"
80
+ end
81
+ @plist.store "ArrayKey#{(65+i).chr}", a
82
+ end
83
+ end
84
+
85
+ def gen_hash
86
+ (0..25).each do |i|
87
+ h = Plist4r::OrderedHash.new
88
+ (0..i).each do |j|
89
+ h["String#{(65+j).chr}"] = "#{(97+j).chr}"*(j+1)
90
+ end
91
+ @plist.store "HashKey#{(65+i).chr}", h
92
+ end
93
+ end
94
+
95
+ def gen_mixed
96
+ @plist.edit do
97
+ [true,false].each do |bool|
98
+ store "BoolKey#{bool.to_s.capitalize}", bool
99
+ end
100
+
101
+ (0..9).each do |i|
102
+ store "IntegerKey#{(65+i).chr}", i
103
+ end
104
+
105
+ (0..25).each do |i|
106
+ store "StringKey#{(65+i).chr}", "#{(97+i).chr}"*(i+1)
107
+ end
108
+
109
+ (0..25).each do |i|
110
+ a = []
111
+ (0..i).each do |j|
112
+ a << "String#{(65+j).chr}"
113
+ end
114
+ store "ArrayKey#{(65+i).chr}", a
115
+ end
116
+
117
+ (0..25).each do |i|
118
+ h = Plist4r::OrderedHash.new
119
+ (0..i).each do |j|
120
+ h["String#{(65+j).chr}"] = "#{(97+j).chr}"*(j+1)
121
+ end
122
+ store "HashKey#{(65+i).chr}", h
123
+ end
124
+ end
125
+ end
126
+
127
+ def gen_plist_1024
128
+ @plist_1024 ||= Plist4r.new do
129
+ coarse_multiplier = 18
130
+ (0..coarse_multiplier).each do |i|
131
+
132
+ a = []
133
+ (0..25).each do |j|
134
+ a << "String#{(65+j).chr}"
135
+ end
136
+ store "ArrayKey#{i}", a
137
+
138
+ h = Plist4r::OrderedHash.new
139
+ (0..25).each do |j|
140
+ h["String#{(65+j).chr}"] = "#{(97+j).chr}"*(j+1)
141
+ end
142
+ store "HashKey#{i}", h
143
+ end
144
+
145
+ a = []
146
+ (0..11).each do |j|
147
+ a << "String#{(65+j).chr}"
148
+ end
149
+ store "ArrayKeyPad1", a
150
+
151
+ a = []
152
+ (0..23).each do |j|
153
+ a << "String#{(65+j).chr}"
154
+ end
155
+ store "ArrayKeyPad2", a
156
+ end
157
+ @plist_1024
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+ end