plist 1.1.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/plist.rb +156 -12
  2. data/test_plist.rb +26 -0
  3. metadata +2 -2
data/plist.rb CHANGED
@@ -3,23 +3,74 @@
3
3
 
4
4
  # Plist parses Mac OS X xml property list files into ruby data structures.
5
5
  #
6
+ # === Load a plist file
7
+ # This is the main point of the library:
8
+ #
9
+ # r = Plist::parse_xml( filename_or_xml )
10
+ #
11
+ # === Save a plist
12
+ # You can turn the variables back into a plist string:
13
+ #
14
+ # r.to_plist
15
+ #
16
+ # There is a convenience method for saving a variable to a file:
17
+ #
18
+ # r.save_plist(filename)
19
+ #
20
+ # Only these ruby types can be converted into a plist:
21
+ #
22
+ # String
23
+ # Float
24
+ # DateTime
25
+ # Integer
26
+ # FalseClass
27
+ # TrueClass
28
+ # Array
29
+ # Hash
30
+ #
31
+ # Note that Array and Hash are recursive -- the elements of an Array and the values of a Hash
32
+ # must convert to a plist. Also note that the keys of the Hash must be strings.
33
+ #
34
+ # If you have suggestions for mapping other Ruby types to the plist types, send a note to:
35
+ #
36
+ # mailto:plist@hexane.org
37
+ #
38
+ # I'll take a look and probably add it, I'm just reticent to create too many
39
+ # "convenience" methods without at least agreeing with someone :-)
40
+ #
41
+ # === Credits
42
+ # plist.rb has been implemented by Patrick May. A few other folks have been helpful in developing plist.rb:
43
+ #
44
+ # + Martin Dittus, who pointed out that Time wasn't enough for plist Dates,
45
+ # especially those in "~/Library/Cookies/Cookies.plist"
46
+ #
47
+ # + Chuck Remes, who pushed me towards implementing #to_plist
48
+ class Plist
49
+
50
+ TEMPLATE = <<-XML
51
+ <?xml version="1.0" encoding="UTF-8"?>
52
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
53
+ <plist version="1.0">
54
+ %plist%
55
+ </plist>
56
+ XML
57
+ def Plist::_xml( xml )
58
+ TEMPLATE.sub( /%plist%/, xml )
59
+ end
60
+
6
61
  # Note that I don't use these two elements much:
7
62
  #
8
- # + Date elements are returned as Time objects.
63
+ # + Date elements are returned as DateTime objects.
9
64
  # + Data elements are not yet implemented.
10
65
  #
11
66
  # Plist::parse_xml will blow up if it encounters a data element.
12
67
  # If you encounter such an error, or if you have a Date element which
13
68
  # can't be parsed into a Time object, please send your plist file to
14
- # patrick@hexane.org so that I can implement the proper support.
15
- #
16
- # The main point of this api is one method: Plist::parse_xml( filename )
17
- class Plist
18
-
19
- def Plist::parse_xml( filename )
69
+ # plist@hexane.org so that I can implement the proper support.
70
+ def Plist::parse_xml( filename_or_xml )
20
71
  listener = Listener.new
21
72
  #parser = REXML::Parsers::StreamParser.new(File.new(filename), listener)
22
- parser = StreamParser.new(filename, listener)
73
+ parser = StreamParser.new(filename_or_xml, listener)
23
74
  parser.parse
24
75
  listener.result
25
76
  end
@@ -54,8 +105,8 @@ class Plist
54
105
  end
55
106
 
56
107
  class StreamParser
57
- def initialize( filename, listener )
58
- @filename = filename
108
+ def initialize( filename_or_xml, listener )
109
+ @filename_or_xml = filename_or_xml
59
110
  @listener = listener
60
111
  end
61
112
 
@@ -70,7 +121,11 @@ class Plist
70
121
  end_tag = /<\/(#{plist_tags})[^>]*>/i
71
122
 
72
123
  require 'strscan'
73
- @scanner = StringScanner.new( File.open(@filename, "r") {|f| f.read} )
124
+ @scanner = StringScanner.new( if (File.exists? @filename_or_xml)
125
+ File.open(@filename_or_xml, "r") {|f| f.read}
126
+ else
127
+ @filename_or_xml
128
+ end )
74
129
  until @scanner.eos?
75
130
  if @scanner.scan(XMLDECL_PATTERN)
76
131
  elsif @scanner.scan(DOCTYPE_PATTERN)
@@ -146,7 +201,7 @@ class Plist
146
201
 
147
202
  class PString < PTag
148
203
  def to_ruby
149
- text
204
+ text || ''
150
205
  end
151
206
  end
152
207
 
@@ -189,4 +244,93 @@ class Plist
189
244
  end
190
245
  end
191
246
 
247
+ module Emit
248
+ def save_plist(filename)
249
+ File.open(filename, 'wb') do |f|
250
+ f.write(self.to_plist)
251
+ end
252
+ end
253
+
254
+ # Only the expected classes can be emitted as a plist:
255
+ # String, Float, DateTime, Integer, TrueClass, FalseClass, Array, Hash
256
+ #
257
+ # Write me if you think another class can be coerced safely into one of the
258
+ # expected plist classes (plist@hexane.org)
259
+ def to_plist
260
+ Plist::_xml(self.to_plist_fragment)
261
+ end
262
+ end
263
+ end
264
+
265
+ class String
266
+ include Plist::Emit
267
+ def to_plist_fragment
268
+ "<string>#{self}</string>"
269
+ end
270
+ end
271
+
272
+ class Float
273
+ include Plist::Emit
274
+ def to_plist_fragment
275
+ "<real>#{self}</real>"
276
+ end
277
+ end
278
+
279
+ class DateTime
280
+ include Plist::Emit
281
+ def to_plist_fragment
282
+ "<date>#{self}</date>"
283
+ end
284
+ end
285
+
286
+ class Integer
287
+ include Plist::Emit
288
+ def to_plist_fragment
289
+ "<integer>#{self}</integer>"
290
+ end
291
+ end
292
+
293
+ class FalseClass
294
+ include Plist::Emit
295
+ def to_plist_fragment
296
+ "<false/>"
297
+ end
298
+ end
299
+
300
+ class TrueClass
301
+ include Plist::Emit
302
+ def to_plist_fragment
303
+ "<true/>"
304
+ end
305
+ end
306
+
307
+ class Array
308
+ include Plist::Emit
309
+ def to_plist_fragment
310
+ fragment = "<array>\n"
311
+ self.each do |e|
312
+ element_plist = e.to_plist_fragment
313
+ element_plist.each do |l|
314
+ fragment += " #{l}\n"
315
+ end
316
+ end
317
+ fragment += "</array>"
318
+ fragment
319
+ end
320
+ end
321
+
322
+ class Hash
323
+ include Plist::Emit
324
+ def to_plist_fragment
325
+ fragment = "<dict>\n"
326
+ self.keys.sort.each do |k|
327
+ fragment += " <key>#{k}</key>\n"
328
+ element_plist = self[k].to_plist_fragment
329
+ element_plist.each do |l|
330
+ fragment += " #{l}\n"
331
+ end
332
+ end
333
+ fragment += "</dict>"
334
+ fragment
335
+ end
192
336
  end
@@ -57,6 +57,32 @@ class TestPlist < Test::Unit::TestCase
57
57
  assert_equal( "2007-10-25T12:36:35Z", result.first['Expires'].to_s )
58
58
  end
59
59
 
60
+ def test_to_plist
61
+ assert_equal( Plist::_xml("<string>Hello, World</string>"), "Hello, World".to_plist )
62
+ assert_equal( Plist::_xml("<real>151936595.697543</real>"), 151936595.697543.to_plist )
63
+ assert_equal( Plist::_xml("<date>2006-04-21T16:47:58Z</date>"), DateTime.parse("2006-04-21T16:47:58Z").to_plist )
64
+ assert_equal( Plist::_xml("<integer>999000</integer>"), 999000.to_plist )
65
+ assert_equal( Plist::_xml("<false/>"), false.to_plist )
66
+ assert_equal( Plist::_xml("<true/>"), true.to_plist )
67
+
68
+ assert_equal( Plist::_xml("<array>\n <true/>\n <false/>\n</array>"),
69
+ [ true, false ].to_plist )
70
+
71
+ assert_equal( Plist::_xml("<dict>\n <key>False</key>\n <false/>\n <key>True</key>\n <true/>\n</dict>"),
72
+ { 'True' => true, 'False' => false }.to_plist )
73
+
74
+ source = File.open("AlbumData.xml") { |f| f.read }
75
+
76
+ result = Plist::parse_xml(source)
77
+
78
+ assert_equal( result, Plist::parse_xml(result.to_plist) )
79
+
80
+ File.delete('hello.plist') if File.exists?('hello.plist')
81
+ "Hello, World".save_plist('hello.plist')
82
+ assert_equal( Plist::_xml("<string>Hello, World</string>"),
83
+ File.open('hello.plist') {|f| f.read } )
84
+ end
85
+
60
86
  end
61
87
 
62
88
  __END__
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11.3
3
3
  specification_version: 1
4
4
  name: plist
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.1.1
7
- date: 2006-05-03 00:00:00 -04:00
6
+ version: 2.0.0
7
+ date: 2006-07-04 00:00:00 -04:00
8
8
  summary: plist parses Mac OS X plist files into ruby data types.
9
9
  require_paths:
10
10
  - "."