plist 1.1.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/plist.rb +156 -12
- data/test_plist.rb +26 -0
- 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
|
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
|
-
#
|
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(
|
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(
|
58
|
-
@
|
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.
|
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
|
data/test_plist.rb
CHANGED
@@ -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:
|
7
|
-
date: 2006-
|
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
|
- "."
|