plist 1.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
- "."
|