plist 2.1.2 → 3.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/Rakefile +2 -2
- data/docs/USAGE +66 -13
- data/lib/plist.rb +4 -1
- data/lib/plist/generator.rb +189 -132
- data/lib/plist/parser.rb +28 -33
- data/test/assets/commented.plist +9 -0
- data/test/assets/example_data.bin +0 -0
- data/test/assets/test_data_elements.plist +24 -0
- data/test/assets/test_empty_key.plist +1 -1
- data/test/test_data_elements.rb +115 -0
- data/test/test_generator.rb +59 -0
- data/test/test_generator_basic_types.rb +17 -45
- data/test/test_generator_collections.rb +82 -0
- data/test/test_parser.rb +8 -20
- metadata +10 -1
data/Rakefile
CHANGED
@@ -61,7 +61,7 @@ end
|
|
61
61
|
|
62
62
|
desc "Strip trailing whitespace and fix newlines for all release files"
|
63
63
|
task :fix_whitespace => [ :clean ] do
|
64
|
-
RELEASE_FILES.each do |filename|
|
64
|
+
RELEASE_FILES.reject {|i| i =~ /assets/}.each do |filename|
|
65
65
|
next if File.directory? filename
|
66
66
|
|
67
67
|
File.open(filename) do |file|
|
@@ -100,7 +100,7 @@ end
|
|
100
100
|
|
101
101
|
desc "Copy documentation to rubyforge"
|
102
102
|
task :update_rdoc => [ :rdoc ] do
|
103
|
-
Rake::SshDirPublisher.new("#{RUBYFORGE_USER}@rubyforge.org", "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}
|
103
|
+
Rake::SshDirPublisher.new("#{RUBYFORGE_USER}@rubyforge.org", "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}", "rdoc").upload
|
104
104
|
end
|
105
105
|
|
106
106
|
# Genereate the RDoc documentation
|
data/docs/USAGE
CHANGED
@@ -1,4 +1,17 @@
|
|
1
|
-
|
1
|
+
== Parsing
|
2
|
+
|
3
|
+
result = Plist::parse_xml('path/to/example.plist')
|
4
|
+
|
5
|
+
result.class
|
6
|
+
=> Hash
|
7
|
+
|
8
|
+
"#{result['FirstName']} #{result['LastName']}"
|
9
|
+
=> "John Public"
|
10
|
+
|
11
|
+
result['ZipPostal']
|
12
|
+
=> "12345"
|
13
|
+
|
14
|
+
==== Example Property List
|
2
15
|
|
3
16
|
<?xml version="1.0" encoding="UTF-8"?>
|
4
17
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
@@ -32,20 +45,60 @@
|
|
32
45
|
<string>12345</string>
|
33
46
|
</dict>
|
34
47
|
</plist>
|
35
|
-
|
36
|
-
=== Parsing
|
37
48
|
|
38
|
-
|
49
|
+
== Generation
|
39
50
|
|
40
|
-
|
41
|
-
|
51
|
+
plist also provides the ability to generate plists from Ruby objects. The following Ruby classes are converted into native plist types:
|
52
|
+
Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time, true, false
|
42
53
|
|
43
|
-
|
44
|
-
|
54
|
+
* +Array+ and +Hash+ are both recursive; their elements will be converted into plist nodes inside the <array> and <dict> containers (respectively).
|
55
|
+
* +IO+ (and its descendants) and +StringIO+ objects are read from and their contents placed in a <data> element.
|
56
|
+
* User classes may implement +to_plist_node+ to dictate how they should be serialized; otherwise the object will be passed to <tt>Marshal.dump</tt> and the result placed in a <data> element. See below for more details.
|
45
57
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
58
|
+
==== Creating a plist
|
59
|
+
|
60
|
+
There are two ways to generate complete plists. Given an object:
|
61
|
+
|
62
|
+
obj = [1, :two, {'c' => 0xd}]
|
63
|
+
|
64
|
+
If you've mixed in <tt>Plist::Emit</tt> (which is already done for +Array+ and +Hash+), you can simply call +to_plist+:
|
65
|
+
|
66
|
+
obj.to_plist
|
67
|
+
|
68
|
+
This is equivalent to calling <tt>Plist::Emit.dump(obj)</tt>. Either one will yield:
|
69
|
+
|
70
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
71
|
+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
72
|
+
<plist version="1.0">
|
73
|
+
<array>
|
74
|
+
<integer>1</integer>
|
75
|
+
<string>two</string>
|
76
|
+
<dict>
|
77
|
+
<key>c</key>
|
78
|
+
<integer>13</integer>
|
79
|
+
</dict>
|
80
|
+
</array>
|
81
|
+
</plist>
|
82
|
+
|
83
|
+
You can also dump plist fragments by passing +false+ as the second parameter:
|
84
|
+
|
85
|
+
Plist::Emit.dump('holy cow!', false)
|
86
|
+
=> "<string>holy cow!</string>"
|
87
|
+
|
88
|
+
==== Custom serialization
|
89
|
+
|
90
|
+
If your class can be safely coerced into a native plist datatype, you can implement +to_plist_node+. Upon encountering an object of a class it doesn't recognize, the plist library will check to see if it responds to +to_plist_node+, and if so, insert the result of that call into the plist output.
|
91
|
+
|
92
|
+
An example:
|
93
|
+
|
94
|
+
class MyFancyString
|
95
|
+
...
|
96
|
+
|
97
|
+
def to_plist_node
|
98
|
+
return "<string>#{self.defancify}</string>"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
When you attempt to serialize a +MyFancyString+ object, the +to_plist_node+ method will be called and the object's contents will be defancified and placed in the plist.
|
50
103
|
|
51
|
-
|
104
|
+
If for whatever reason you can't add this method, your object will be serialized with <tt>Marshal.dump</tt> instead.
|
data/lib/plist.rb
CHANGED
@@ -10,10 +10,13 @@
|
|
10
10
|
#
|
11
11
|
# This is the main file for plist. Everything interesting happens in Plist and Plist::Emit.
|
12
12
|
|
13
|
+
require 'base64'
|
13
14
|
require 'cgi'
|
15
|
+
require 'stringio'
|
16
|
+
|
14
17
|
require 'plist/generator'
|
15
18
|
require 'plist/parser'
|
16
19
|
|
17
20
|
module Plist
|
18
|
-
VERSION = '
|
21
|
+
VERSION = '3.0.0'
|
19
22
|
end
|
data/lib/plist/generator.rb
CHANGED
@@ -1,167 +1,224 @@
|
|
1
|
-
|
1
|
+
#--###########################################################
|
2
2
|
# Copyright 2006, Ben Bleything <ben@bleything.net> and #
|
3
3
|
# Patrick May <patrick@hexane.org> #
|
4
4
|
# #
|
5
5
|
# Distributed under the MIT license. #
|
6
6
|
##############################################################
|
7
|
-
|
8
|
-
#
|
9
|
-
# You can turn the variables back into a plist string:
|
10
|
-
#
|
11
|
-
# r.to_plist
|
12
|
-
#
|
13
|
-
# There is a convenience method for saving a variable to a file:
|
14
|
-
#
|
15
|
-
# r.save_plist(filename)
|
16
|
-
#
|
17
|
-
# Only these ruby types can be converted into a plist:
|
18
|
-
#
|
19
|
-
# String
|
20
|
-
# Float
|
21
|
-
# DateTime
|
22
|
-
# Integer
|
23
|
-
# FalseClass
|
24
|
-
# TrueClass
|
25
|
-
# Array
|
26
|
-
# Hash
|
27
|
-
#
|
28
|
-
# Notes:
|
29
|
-
#
|
30
|
-
# + Array and Hash are recursive -- the elements of an Array and the values of a Hash
|
31
|
-
# must convert to a plist.
|
32
|
-
# + The keys of the Hash must be strings.
|
33
|
-
# + The contents of data elements are returned as a Tempfile.
|
34
|
-
# + Data elements can be set with to an open IO or a StringIO
|
35
|
-
#
|
36
|
-
# If you have suggestions for mapping other Ruby types to the plist types, send a note to:
|
37
|
-
#
|
38
|
-
# mailto:plist@hexane.org
|
39
|
-
#
|
40
|
-
# I'll take a look and probably add it, I'm just reticent to create too many
|
41
|
-
# "convenience" methods without at least agreeing with someone :-)
|
7
|
+
#++
|
8
|
+
# See Plist::Emit.
|
42
9
|
module Plist
|
10
|
+
# === Create a plist
|
11
|
+
# You can dump an object to a plist in one of two ways:
|
12
|
+
#
|
13
|
+
# * <tt>Plist::Emit.dump(obj)</tt>
|
14
|
+
# * <tt>obj.to_plist</tt>
|
15
|
+
# * This requires that you mixin the <tt>Plist::Emit</tt> module, which is already done for +Array+ and +Hash+.
|
16
|
+
#
|
17
|
+
# The following Ruby classes are converted into native plist types:
|
18
|
+
# Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time, true, false
|
19
|
+
# * +Array+ and +Hash+ are both recursive; their elements will be converted into plist nodes inside the <array> and <dict> containers (respectively).
|
20
|
+
# * +IO+ (and its descendants) and +StringIO+ objects are read from and their contents placed in a <data> element.
|
21
|
+
# * User classes may implement +to_plist_node+ to dictate how they should be serialized; otherwise the object will be passed to <tt>Marshal.dump</tt> and the result placed in a <data> element.
|
22
|
+
#
|
23
|
+
# For detailed usage instructions, refer to USAGE[link:files/docs/USAGE.html] and the methods documented below.
|
43
24
|
module Emit
|
25
|
+
# Helper method for injecting into classes. Calls <tt>Plist::Emit.dump</tt> with +self+.
|
26
|
+
def to_plist(envelope = true)
|
27
|
+
return Plist::Emit.dump(self, envelope)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Helper method for injecting into classes. Calls <tt>Plist::Emit.save_plist</tt> with +self+.
|
44
31
|
def save_plist(filename)
|
32
|
+
Plist::Emit.save_plist(self, filename)
|
33
|
+
end
|
34
|
+
|
35
|
+
# The following Ruby classes are converted into native plist types:
|
36
|
+
# Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time
|
37
|
+
#
|
38
|
+
# Write us (via RubyForge) if you think another class can be coerced safely into one of the expected plist classes.
|
39
|
+
#
|
40
|
+
# +IO+ and +StringIO+ objects are encoded and placed in <data> elements; other objects are <tt>Marshal.dump</tt>'ed unless they implement +to_plist_node+.
|
41
|
+
#
|
42
|
+
# The +envelope+ parameters dictates whether or not the resultant plist fragment is wrapped in the normal XML/plist header and footer. Set it to false if you only want the fragment.
|
43
|
+
def self.dump(obj, envelope = true)
|
44
|
+
output = plist_node(obj)
|
45
|
+
|
46
|
+
output = wrap(output) if envelope
|
47
|
+
|
48
|
+
return output
|
49
|
+
end
|
50
|
+
|
51
|
+
# Writes the serialized object's plist to the specified filename.
|
52
|
+
def self.save_plist(obj, filename)
|
45
53
|
File.open(filename, 'wb') do |f|
|
46
|
-
f.write(
|
54
|
+
f.write(obj.to_plist)
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
if (header)
|
57
|
-
Plist::_xml(self.to_plist_node)
|
58
|
+
private
|
59
|
+
def self.plist_node(element)
|
60
|
+
output = ''
|
61
|
+
|
62
|
+
if element.respond_to? :to_plist_node
|
63
|
+
output << element.to_plist_node
|
58
64
|
else
|
59
|
-
|
65
|
+
case element
|
66
|
+
when Array
|
67
|
+
if element.empty?
|
68
|
+
output << "<array/>\n"
|
69
|
+
else
|
70
|
+
output << tag('array') {
|
71
|
+
element.collect {|e| plist_node(e)}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
when Hash
|
75
|
+
if element.empty?
|
76
|
+
output << "<dict/>\n"
|
77
|
+
else
|
78
|
+
inner_tags = []
|
79
|
+
|
80
|
+
element.keys.sort.each do |k|
|
81
|
+
v = element[k]
|
82
|
+
inner_tags << tag('key', CGI::escapeHTML(k.to_s))
|
83
|
+
inner_tags << plist_node(v)
|
84
|
+
end
|
85
|
+
|
86
|
+
output << tag('dict') {
|
87
|
+
inner_tags
|
88
|
+
}
|
89
|
+
end
|
90
|
+
when true, false
|
91
|
+
output << "<#{element}/>\n"
|
92
|
+
when Time
|
93
|
+
output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'))
|
94
|
+
when Date # also catches DateTime
|
95
|
+
output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'))
|
96
|
+
when String, Symbol, Fixnum, Bignum, Integer, Float
|
97
|
+
output << tag(element_type(element), CGI::escapeHTML(element.to_s))
|
98
|
+
when IO, StringIO
|
99
|
+
element.rewind
|
100
|
+
contents = element.read
|
101
|
+
# note that apple plists are wrapped at a different length then
|
102
|
+
# what ruby's base64 wraps by default.
|
103
|
+
# I used #encode64 instead of #b64encode (which allows a length arg)
|
104
|
+
# because b64encode is b0rked and ignores the length arg.
|
105
|
+
data = "\n"
|
106
|
+
Base64::encode64(contents).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" }
|
107
|
+
output << tag('data', data)
|
108
|
+
else
|
109
|
+
output << comment( 'The <data> element below contains a Ruby object which has been serialized with Marshal.dump.' )
|
110
|
+
data = "\n"
|
111
|
+
Base64::encode64(Marshal.dump(element)).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" }
|
112
|
+
output << tag('data', data )
|
113
|
+
end
|
60
114
|
end
|
115
|
+
|
116
|
+
return output
|
61
117
|
end
|
62
|
-
end
|
63
|
-
end
|
64
118
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
"<string>#{CGI::escapeHTML(self)}</string>"
|
69
|
-
end
|
70
|
-
end
|
119
|
+
def self.comment(content)
|
120
|
+
return "<!-- #{content} -->\n"
|
121
|
+
end
|
71
122
|
|
72
|
-
|
73
|
-
|
74
|
-
def to_plist_node
|
75
|
-
"<string>#{CGI::escapeHTML(self.to_s)}</string>"
|
76
|
-
end
|
77
|
-
end
|
123
|
+
def self.tag(type, contents = '', &block)
|
124
|
+
out = nil
|
78
125
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
84
|
-
end
|
126
|
+
if block_given?
|
127
|
+
out = IndentedString.new
|
128
|
+
out << "<#{type}>"
|
129
|
+
out.raise_indent
|
85
130
|
|
86
|
-
|
87
|
-
include Plist::Emit
|
88
|
-
def to_plist_node
|
89
|
-
"<date>#{self.utc.strftime('%Y-%m-%dT%H:%M:%SZ')}</date>"
|
90
|
-
end
|
91
|
-
end
|
131
|
+
out << block.call
|
92
132
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
133
|
+
out.lower_indent
|
134
|
+
out << "</#{type}>"
|
135
|
+
else
|
136
|
+
out = "<#{type}>#{contents.to_s}</#{type}>\n"
|
137
|
+
end
|
99
138
|
|
100
|
-
|
101
|
-
|
102
|
-
def to_plist_node
|
103
|
-
"<integer>#{self}</integer>"
|
104
|
-
end
|
105
|
-
end
|
139
|
+
return out.to_s
|
140
|
+
end
|
106
141
|
|
107
|
-
|
108
|
-
|
109
|
-
def to_plist_node
|
110
|
-
"<false/>"
|
111
|
-
end
|
112
|
-
end
|
142
|
+
def self.wrap(contents)
|
143
|
+
output = ''
|
113
144
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
"<true/>"
|
118
|
-
end
|
119
|
-
end
|
145
|
+
output << '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
|
146
|
+
output << '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' + "\n"
|
147
|
+
output << '<plist version="1.0">' + "\n"
|
120
148
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
149
|
+
output << contents
|
150
|
+
|
151
|
+
output << '</plist>' + "\n"
|
152
|
+
|
153
|
+
return output
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.element_type(item)
|
157
|
+
return case item
|
158
|
+
when String, Symbol: 'string'
|
159
|
+
when Fixnum, Bignum, Integer: 'integer'
|
160
|
+
when Float: 'real'
|
161
|
+
else
|
162
|
+
raise "Don't know about this data type... something must be wrong!"
|
129
163
|
end
|
130
164
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
end
|
165
|
+
private
|
166
|
+
class IndentedString #:nodoc:
|
167
|
+
attr_accessor :indent_string
|
135
168
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
169
|
+
@@indent_level = 0
|
170
|
+
|
171
|
+
def initialize(str = "\t")
|
172
|
+
@indent_string = str
|
173
|
+
@contents = ''
|
174
|
+
end
|
175
|
+
|
176
|
+
def to_s
|
177
|
+
return @contents
|
178
|
+
end
|
179
|
+
|
180
|
+
def raise_indent
|
181
|
+
@@indent_level += 1
|
182
|
+
end
|
183
|
+
|
184
|
+
def lower_indent
|
185
|
+
@@indent_level -= 1 if @@indent_level > 0
|
186
|
+
end
|
187
|
+
|
188
|
+
def <<(val)
|
189
|
+
if val.is_a? Array
|
190
|
+
val.each do |f|
|
191
|
+
self << f
|
192
|
+
end
|
193
|
+
else
|
194
|
+
# if it's already indented, don't bother indenting further
|
195
|
+
unless val =~ /\A#{@indent_string}/
|
196
|
+
indent = @indent_string * @@indent_level
|
197
|
+
|
198
|
+
@contents << val.gsub(/^/, indent)
|
199
|
+
else
|
200
|
+
@contents << val
|
201
|
+
end
|
202
|
+
|
203
|
+
# it already has a newline, don't add another
|
204
|
+
@contents << "\n" unless val =~ /\n$/
|
205
|
+
end
|
145
206
|
end
|
146
207
|
end
|
147
|
-
fragment += "</dict>"
|
148
|
-
fragment
|
149
208
|
end
|
150
209
|
end
|
151
210
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
data = self.read
|
211
|
+
# we need to add this so sorting hash keys works properly
|
212
|
+
class Symbol #:nodoc:
|
213
|
+
def <=> (other)
|
214
|
+
self.to_s <=> other.to_s
|
215
|
+
end
|
216
|
+
end
|
159
217
|
|
160
|
-
|
161
|
-
|
162
|
-
|
218
|
+
class Array #:nodoc:
|
219
|
+
include Plist::Emit
|
220
|
+
end
|
163
221
|
|
164
|
-
|
165
|
-
|
166
|
-
end
|
222
|
+
class Hash #:nodoc:
|
223
|
+
include Plist::Emit
|
167
224
|
end
|
data/lib/plist/parser.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
|
1
|
+
#--###########################################################
|
2
2
|
# Copyright 2006, Ben Bleything <ben@bleything.net> and #
|
3
3
|
# Patrick May <patrick@hexane.org> #
|
4
4
|
# #
|
5
5
|
# Distributed under the MIT license. #
|
6
6
|
##############################################################
|
7
|
-
|
7
|
+
#++
|
8
8
|
# Plist parses Mac OS X xml property list files into ruby data structures.
|
9
9
|
#
|
10
10
|
# === Load a plist file
|
@@ -12,17 +12,6 @@
|
|
12
12
|
#
|
13
13
|
# r = Plist::parse_xml( filename_or_xml )
|
14
14
|
module Plist
|
15
|
-
TEMPLATE = <<-XML
|
16
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
17
|
-
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
18
|
-
<plist version="1.0">
|
19
|
-
%plist%
|
20
|
-
</plist>
|
21
|
-
XML
|
22
|
-
def Plist::_xml( xml )
|
23
|
-
TEMPLATE.sub( /%plist%/, xml )
|
24
|
-
end
|
25
|
-
|
26
15
|
# Note that I don't use these two elements much:
|
27
16
|
#
|
28
17
|
# + Date elements are returned as DateTime objects.
|
@@ -78,6 +67,8 @@ XML
|
|
78
67
|
TEXT = /([^<]+)/
|
79
68
|
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um
|
80
69
|
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
|
70
|
+
COMMENT_START = /\A<!--/u
|
71
|
+
COMMENT_END = /.*?-->/um
|
81
72
|
|
82
73
|
|
83
74
|
def parse
|
@@ -86,13 +77,20 @@ XML
|
|
86
77
|
end_tag = /<\/(#{plist_tags})[^>]*>/i
|
87
78
|
|
88
79
|
require 'strscan'
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
80
|
+
|
81
|
+
contents = (
|
82
|
+
if (File.exists? @filename_or_xml)
|
83
|
+
File.open(@filename_or_xml) {|f| f.read}
|
84
|
+
else
|
85
|
+
@filename_or_xml
|
86
|
+
end
|
87
|
+
)
|
88
|
+
|
89
|
+
@scanner = StringScanner.new( contents )
|
94
90
|
until @scanner.eos?
|
95
|
-
if @scanner.scan(
|
91
|
+
if @scanner.scan(COMMENT_START)
|
92
|
+
@scanner.scan(COMMENT_END)
|
93
|
+
elsif @scanner.scan(XMLDECL_PATTERN)
|
96
94
|
elsif @scanner.scan(DOCTYPE_PATTERN)
|
97
95
|
elsif @scanner.scan(start_tag)
|
98
96
|
@listener.tag_start(@scanner[1], nil)
|
@@ -136,7 +134,7 @@ XML
|
|
136
134
|
|
137
135
|
class PList < PTag
|
138
136
|
def to_ruby
|
139
|
-
children.first.to_ruby
|
137
|
+
children.first.to_ruby if children.first
|
140
138
|
end
|
141
139
|
end
|
142
140
|
|
@@ -212,19 +210,16 @@ XML
|
|
212
210
|
require 'base64'
|
213
211
|
class PData < PTag
|
214
212
|
def to_ruby
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
io.write Base64.decode64(text.gsub(/\s+/,''))
|
226
|
-
io.rewind
|
227
|
-
io
|
213
|
+
data = Base64.decode64(text.gsub(/\s+/, ''))
|
214
|
+
|
215
|
+
begin
|
216
|
+
return Marshal.load(data)
|
217
|
+
rescue Exception => e
|
218
|
+
io = StringIO.new
|
219
|
+
io.write data
|
220
|
+
io.rewind
|
221
|
+
return io
|
222
|
+
end
|
228
223
|
end
|
229
224
|
end
|
230
225
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<!-- I am a comment! -->
|
5
|
+
<!--
|
6
|
+
I am a multi-line comment!
|
7
|
+
hooray!
|
8
|
+
-->
|
9
|
+
</plist>
|
Binary file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>stringio</key>
|
6
|
+
<data>dGhpcyBpcyBhIHN0cmluZ2lvIG9iamVjdA==
|
7
|
+
</data>
|
8
|
+
<key>file</key>
|
9
|
+
<data>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
10
|
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
11
|
+
AAAAAAAAAAAAAA==
|
12
|
+
</data>
|
13
|
+
<key>io</key>
|
14
|
+
<data>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
15
|
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
16
|
+
AAAAAAAAAAAAAA==
|
17
|
+
</data>
|
18
|
+
<key>marshal</key>
|
19
|
+
<!-- The <data> element below contains a Ruby object which has been serialized with Marshal.dump. -->
|
20
|
+
<data>BAhvOhZNYXJzaGFsYWJsZU9iamVjdAY6CUBmb28iHnRoaXMgb2JqZWN0IHdh
|
21
|
+
cyBtYXJzaGFsZWQ=
|
22
|
+
</data>
|
23
|
+
</dict>
|
24
|
+
</plist>
|
@@ -0,0 +1,115 @@
|
|
1
|
+
##############################################################
|
2
|
+
# Copyright 2006, Ben Bleything <ben@bleything.net> and #
|
3
|
+
# Patrick May <patrick@hexane.org> #
|
4
|
+
# #
|
5
|
+
# Distributed under the MIT license. #
|
6
|
+
##############################################################
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'plist'
|
10
|
+
require 'stringio'
|
11
|
+
|
12
|
+
class MarshalableObject
|
13
|
+
attr_accessor :foo
|
14
|
+
|
15
|
+
def initialize(str)
|
16
|
+
@foo = str
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestDataElements < Test::Unit::TestCase
|
21
|
+
@@result = Plist::parse_xml('test/assets/test_data_elements.plist')
|
22
|
+
|
23
|
+
def test_marshal
|
24
|
+
expected = <<END
|
25
|
+
<!-- The <data> element below contains a Ruby object which has been serialized with Marshal.dump. -->
|
26
|
+
<data>
|
27
|
+
BAhvOhZNYXJzaGFsYWJsZU9iamVjdAY6CUBmb28iHnRoaXMgb2JqZWN0IHdhcyBtYXJz
|
28
|
+
aGFsZWQ=
|
29
|
+
</data>
|
30
|
+
END
|
31
|
+
|
32
|
+
mo = MarshalableObject.new('this object was marshaled')
|
33
|
+
|
34
|
+
assert_equal expected.chomp, Plist::Emit.dump(mo, false).chomp
|
35
|
+
|
36
|
+
assert_instance_of MarshalableObject, @@result['marshal']
|
37
|
+
|
38
|
+
assert_equal mo.foo, @@result['marshal'].foo
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_generator_io_and_file
|
42
|
+
expected = <<END
|
43
|
+
<data>
|
44
|
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
45
|
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
46
|
+
</data>
|
47
|
+
END
|
48
|
+
|
49
|
+
expected.chomp!
|
50
|
+
|
51
|
+
fd = IO.sysopen('test/assets/example_data.bin')
|
52
|
+
io = IO.open(fd, 'r')
|
53
|
+
|
54
|
+
# File is a subclass of IO, so catching IO in the dispatcher should work for File as well...
|
55
|
+
f = File.open('test/assets/example_data.bin')
|
56
|
+
|
57
|
+
assert_equal expected, Plist::Emit.dump(io, false).chomp
|
58
|
+
assert_equal expected, Plist::Emit.dump(f, false).chomp
|
59
|
+
|
60
|
+
assert_instance_of StringIO, @@result['io']
|
61
|
+
assert_instance_of StringIO, @@result['file']
|
62
|
+
|
63
|
+
io.rewind
|
64
|
+
f.rewind
|
65
|
+
|
66
|
+
assert_equal io.read, @@result['io'].read
|
67
|
+
assert_equal f.read, @@result['file'].read
|
68
|
+
|
69
|
+
io.close
|
70
|
+
f.close
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_generator_string_io
|
74
|
+
expected = <<END
|
75
|
+
<data>
|
76
|
+
dGhpcyBpcyBhIHN0cmluZ2lvIG9iamVjdA==
|
77
|
+
</data>
|
78
|
+
END
|
79
|
+
|
80
|
+
sio = StringIO.new('this is a stringio object')
|
81
|
+
|
82
|
+
assert_equal expected.chomp, Plist::Emit.dump(sio, false).chomp
|
83
|
+
|
84
|
+
assert_instance_of StringIO, @@result['stringio']
|
85
|
+
|
86
|
+
sio.rewind
|
87
|
+
assert_equal sio.read, @@result['stringio'].read
|
88
|
+
end
|
89
|
+
|
90
|
+
# this functionality is credited to Mat Schaffer,
|
91
|
+
# who discovered the plist with the data tag
|
92
|
+
# supplied the test data, and provided the parsing code.
|
93
|
+
def test_data
|
94
|
+
# test reading plist <data> elements
|
95
|
+
data = Plist::parse_xml("test/assets/example_data.plist");
|
96
|
+
assert_equal( File.open("test/assets/example_data.jpg"){|f| f.read }, data['image'].read )
|
97
|
+
|
98
|
+
# test writing data elements
|
99
|
+
expected = File.read("test/assets/example_data.plist")
|
100
|
+
result = data.to_plist
|
101
|
+
#File.open('result.plist', 'w') {|f|f.write(result)} # debug
|
102
|
+
assert_equal( expected, result )
|
103
|
+
|
104
|
+
# Test changing the <data> object in the plist to a StringIO and writing.
|
105
|
+
# This appears extraneous given that plist currently returns a StringIO,
|
106
|
+
# so the above writing test also flexes StringIO#to_plist_node.
|
107
|
+
# However, the interface promise is to return an IO, not a particular class.
|
108
|
+
# plist used to return Tempfiles, which was changed solely for performance reasons.
|
109
|
+
data['image'] = StringIO.new( File.read("test/assets/example_data.jpg"))
|
110
|
+
|
111
|
+
assert_equal(expected, data.to_plist )
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
##############################################################
|
2
|
+
# Copyright 2006, Ben Bleything <ben@bleything.net> and #
|
3
|
+
# Patrick May <patrick@hexane.org> #
|
4
|
+
# #
|
5
|
+
# Distributed under the MIT license. #
|
6
|
+
##############################################################
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'plist'
|
10
|
+
|
11
|
+
class SerializableObject
|
12
|
+
attr_accessor :foo
|
13
|
+
|
14
|
+
def initialize(str)
|
15
|
+
@foo = str
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_plist_node
|
19
|
+
return "<string>#{CGI::escapeHTML @foo}</string>"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class TestGenerator < Test::Unit::TestCase
|
24
|
+
def test_to_plist_vs_plist_emit_dump_no_envelope
|
25
|
+
source = [1, :b, true]
|
26
|
+
|
27
|
+
to_plist = source.to_plist(false)
|
28
|
+
plist_emit_dump = Plist::Emit.dump(source, false)
|
29
|
+
|
30
|
+
assert_equal to_plist, plist_emit_dump
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_to_plist_vs_plist_emit_dump_with_envelope
|
34
|
+
source = [1, :b, true]
|
35
|
+
|
36
|
+
to_plist = source.to_plist
|
37
|
+
plist_emit_dump = Plist::Emit.dump(source)
|
38
|
+
|
39
|
+
assert_equal to_plist, plist_emit_dump
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_dumping_serializable_object
|
43
|
+
str = 'this object implements #to_plist_node'
|
44
|
+
so = SerializableObject.new(str)
|
45
|
+
|
46
|
+
assert_equal "<string>#{str}</string>", Plist::Emit.dump(so, false)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_write_plist
|
50
|
+
data = [1, :two, {:c => 'dee'}]
|
51
|
+
|
52
|
+
data.save_plist('test.plist')
|
53
|
+
file = File.open('test.plist') {|f| f.read}
|
54
|
+
|
55
|
+
assert_equal file, data.to_plist
|
56
|
+
|
57
|
+
File.unlink('test.plist')
|
58
|
+
end
|
59
|
+
end
|
@@ -8,79 +8,51 @@
|
|
8
8
|
require 'test/unit'
|
9
9
|
require 'plist'
|
10
10
|
|
11
|
-
class
|
11
|
+
class TestGeneratorBasicTypes < Test::Unit::TestCase
|
12
12
|
def wrap(tag, content)
|
13
|
-
return "
|
13
|
+
return "<#{tag}>#{content}</#{tag}>"
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_strings
|
17
17
|
expected = wrap('string', 'testdata')
|
18
18
|
|
19
|
-
assert_equal expected,
|
20
|
-
assert_equal expected,
|
19
|
+
assert_equal expected, Plist::Emit.dump('testdata', false).chomp
|
20
|
+
assert_equal expected, Plist::Emit.dump(:testdata, false).chomp
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_strings_with_escaping
|
24
|
+
expected = wrap('string', "<Fish & Chips>")
|
25
|
+
|
26
|
+
assert_equal expected, Plist::Emit.dump('<Fish & Chips>', false).chomp
|
21
27
|
end
|
22
28
|
|
23
29
|
def test_integers
|
24
30
|
[42, 2376239847623987623, -8192].each do |i|
|
25
|
-
assert_equal wrap('integer', i),
|
31
|
+
assert_equal wrap('integer', i), Plist::Emit.dump(i, false).chomp
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
29
35
|
def test_floats
|
30
36
|
[3.14159, -38.3897, 2398476293847.9823749872349980].each do |i|
|
31
|
-
assert_equal wrap('real', i),
|
37
|
+
assert_equal wrap('real', i), Plist::Emit.dump(i, false).chomp
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
35
41
|
def test_booleans
|
36
|
-
assert_equal "<
|
37
|
-
assert_equal "<
|
42
|
+
assert_equal "<true/>", Plist::Emit.dump(true, false).chomp
|
43
|
+
assert_equal "<false/>", Plist::Emit.dump(false, false).chomp
|
38
44
|
end
|
39
45
|
|
40
46
|
def test_time
|
41
47
|
test_time = Time.now
|
42
|
-
assert_equal wrap('date', test_time.utc.strftime('%Y-%m-%dT%H:%M:%SZ')),
|
48
|
+
assert_equal wrap('date', test_time.utc.strftime('%Y-%m-%dT%H:%M:%SZ')), Plist::Emit.dump(test_time, false).chomp
|
43
49
|
end
|
44
50
|
|
45
51
|
def test_dates
|
46
52
|
test_date = Date.today
|
47
53
|
test_datetime = DateTime.now
|
48
54
|
|
49
|
-
assert_equal wrap('date', test_date.strftime('%Y-%m-%dT%H:%M:%SZ')),
|
50
|
-
assert_equal wrap('date', test_datetime.strftime('%Y-%m-%dT%H:%M:%SZ')),
|
51
|
-
end
|
52
|
-
|
53
|
-
# generater tests from patrick's plist.rb code
|
54
|
-
def test_to_plist
|
55
|
-
assert_equal( Plist::_xml("<string>Hello, World</string>"), "Hello, World".to_plist )
|
56
|
-
assert_equal( Plist::_xml("<real>151936595.697543</real>"), 151936595.697543.to_plist )
|
57
|
-
assert_equal( Plist::_xml("<date>2006-04-21T16:47:58Z</date>"), DateTime.parse("2006-04-21T16:47:58Z").to_plist )
|
58
|
-
assert_equal( Plist::_xml("<integer>999000</integer>"), 999000.to_plist )
|
59
|
-
assert_equal( Plist::_xml("<false/>"), false.to_plist )
|
60
|
-
assert_equal( Plist::_xml("<true/>"), true.to_plist )
|
61
|
-
|
62
|
-
assert_equal( Plist::_xml("<array>\n\t<true/>\n\t<false/>\n</array>"),
|
63
|
-
[ true, false ].to_plist )
|
64
|
-
|
65
|
-
assert_equal( Plist::_xml("<dict>\n\t<key>False</key>\n\t<false/>\n\t<key>True</key>\n\t<true/>\n</dict>"),
|
66
|
-
{ 'True' => true, 'False' => false }.to_plist )
|
67
|
-
|
68
|
-
source = File.open("test/assets/AlbumData.xml") { |f| f.read }
|
69
|
-
|
70
|
-
result = Plist::parse_xml(source)
|
71
|
-
|
72
|
-
assert_equal( result, Plist::parse_xml(result.to_plist) )
|
73
|
-
|
74
|
-
File.delete('hello.plist') if File.exists?('hello.plist')
|
75
|
-
"Hello, World".save_plist('hello.plist')
|
76
|
-
assert_equal( Plist::_xml("<string>Hello, World</string>"),
|
77
|
-
File.open('hello.plist') {|f| f.read } )
|
78
|
-
File.delete('hello.plist') if File.exists?('hello.plist')
|
55
|
+
assert_equal wrap('date', test_date.strftime('%Y-%m-%dT%H:%M:%SZ')), Plist::Emit.dump(test_date, false).chomp
|
56
|
+
assert_equal wrap('date', test_datetime.strftime('%Y-%m-%dT%H:%M:%SZ')), Plist::Emit.dump(test_datetime, false).chomp
|
79
57
|
end
|
80
|
-
|
81
|
-
def test_escape_string_values
|
82
|
-
assert_equal( Plist::_xml("<string><plist></string>"), "<plist>".to_plist )
|
83
|
-
assert_equal( Plist::_xml("<string>Fish & Chips</string>"), "Fish & Chips".to_plist )
|
84
|
-
end
|
85
|
-
|
86
58
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
##############################################################
|
2
|
+
# Copyright 2006, Ben Bleything <ben@bleything.net> and #
|
3
|
+
# Patrick May <patrick@hexane.org> #
|
4
|
+
# #
|
5
|
+
# Distributed under the MIT license. #
|
6
|
+
##############################################################
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
require 'plist'
|
10
|
+
|
11
|
+
class TestGeneratorCollections < Test::Unit::TestCase
|
12
|
+
def test_array
|
13
|
+
expected = <<END
|
14
|
+
<array>
|
15
|
+
<integer>1</integer>
|
16
|
+
<integer>2</integer>
|
17
|
+
<integer>3</integer>
|
18
|
+
</array>
|
19
|
+
END
|
20
|
+
|
21
|
+
assert_equal expected, [1,2,3].to_plist(false)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_empty_array
|
25
|
+
expected = <<END
|
26
|
+
<array/>
|
27
|
+
END
|
28
|
+
|
29
|
+
assert_equal expected, [].to_plist(false)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_hash
|
33
|
+
expected = <<END
|
34
|
+
<dict>
|
35
|
+
<key>abc</key>
|
36
|
+
<integer>123</integer>
|
37
|
+
<key>foo</key>
|
38
|
+
<string>bar</string>
|
39
|
+
</dict>
|
40
|
+
END
|
41
|
+
# thanks to recent changes in the generator code, hash keys are sorted before emission,
|
42
|
+
# so multi-element hash tests should be reliable. We're testing that here too.
|
43
|
+
assert_equal expected, {:foo => :bar, :abc => 123}.to_plist(false)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_empty_hash
|
47
|
+
expected = <<END
|
48
|
+
<dict/>
|
49
|
+
END
|
50
|
+
|
51
|
+
assert_equal expected, {}.to_plist(false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_hash_with_array_element
|
55
|
+
expected = <<END
|
56
|
+
<dict>
|
57
|
+
<key>ary</key>
|
58
|
+
<array>
|
59
|
+
<integer>1</integer>
|
60
|
+
<string>b</string>
|
61
|
+
<string>3</string>
|
62
|
+
</array>
|
63
|
+
</dict>
|
64
|
+
END
|
65
|
+
assert_equal expected, {:ary => [1,:b,'3']}.to_plist(false)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_array_with_hash_element
|
69
|
+
expected = <<END
|
70
|
+
<array>
|
71
|
+
<dict>
|
72
|
+
<key>foo</key>
|
73
|
+
<string>bar</string>
|
74
|
+
</dict>
|
75
|
+
<string>b</string>
|
76
|
+
<integer>3</integer>
|
77
|
+
</array>
|
78
|
+
END
|
79
|
+
|
80
|
+
assert_equal expected, [{:foo => 'bar'}, :b, 3].to_plist(false)
|
81
|
+
end
|
82
|
+
end
|
data/test/test_parser.rb
CHANGED
@@ -6,11 +6,10 @@
|
|
6
6
|
##############################################################
|
7
7
|
|
8
8
|
require 'test/unit'
|
9
|
-
require 'pp'
|
10
9
|
|
11
10
|
require 'plist'
|
12
11
|
|
13
|
-
class
|
12
|
+
class TestParser < Test::Unit::TestCase
|
14
13
|
def test_Plist_parse_xml
|
15
14
|
result = Plist::parse_xml("test/assets/AlbumData.xml")
|
16
15
|
|
@@ -59,7 +58,6 @@ class TestPlist < Test::Unit::TestCase
|
|
59
58
|
# plist = Plist::parse_xml( "~/Pictures/iPhoto Library/AlbumData.xml" )
|
60
59
|
#end
|
61
60
|
|
62
|
-
|
63
61
|
# date fields are credited to
|
64
62
|
def test_date_fields
|
65
63
|
result = Plist::parse_xml("test/assets/Cookies.plist")
|
@@ -67,22 +65,6 @@ class TestPlist < Test::Unit::TestCase
|
|
67
65
|
assert_equal( "2007-10-25T12:36:35Z", result.first['Expires'].to_s )
|
68
66
|
end
|
69
67
|
|
70
|
-
# this functionality is credited to Mat Schaffer,
|
71
|
-
# who discovered the plist with the data tag
|
72
|
-
# supplied the test data, and provided the parsing code.
|
73
|
-
def test_data
|
74
|
-
data = Plist::parse_xml("test/assets/example_data.plist");
|
75
|
-
assert_equal( File.open("test/assets/example_data.jpg"){|f| f.read }, data['image'].read )
|
76
|
-
assert_equal( File.open("test/assets/example_data.plist"){|f| f.read }, data.to_plist )
|
77
|
-
|
78
|
-
data['image'] = StringIO.new( File.open("test/assets/example_data.jpg"){ |f| f.read } )
|
79
|
-
File.open('temp.plist', 'w'){|f| f.write data.to_plist }
|
80
|
-
assert_equal( File.open("test/assets/example_data.plist"){|f| f.read }, data.to_plist )
|
81
|
-
|
82
|
-
File.delete('temp.plist') if File.exists?('temp.plist')
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
68
|
# bug fix for empty <key>
|
87
69
|
# reported by Matthias Peick <matthias@peick.de>
|
88
70
|
# reported and fixed by Frederik Seiffert <ego@frederikseiffert.de>
|
@@ -94,9 +76,15 @@ class TestPlist < Test::Unit::TestCase
|
|
94
76
|
# bug fix for decoding entities
|
95
77
|
# reported by Matthias Peick <matthias@peick.de>
|
96
78
|
def test_decode_entities
|
97
|
-
data = Plist::parse_xml(
|
79
|
+
data = Plist::parse_xml('<string>Fish & Chips</string>')
|
98
80
|
assert_equal('Fish & Chips', data)
|
99
81
|
end
|
82
|
+
|
83
|
+
def test_comment_handling_and_empty_plist
|
84
|
+
assert_nothing_raised do
|
85
|
+
assert_nil( Plist::parse_xml( File.read('test/assets/commented.plist') ) )
|
86
|
+
end
|
87
|
+
end
|
100
88
|
end
|
101
89
|
|
102
90
|
__END__
|
metadata
CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: plist
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
6
|
+
version: 3.0.0
|
7
7
|
date: 2006-09-20 00:00:00 -07:00
|
8
8
|
summary: All-purpose Property List manipulation library.
|
9
9
|
require_paths:
|
@@ -37,15 +37,24 @@ files:
|
|
37
37
|
- lib/plist.rb
|
38
38
|
- lib/plist/generator.rb
|
39
39
|
- lib/plist/parser.rb
|
40
|
+
- test/test_data_elements.rb
|
41
|
+
- test/test_generator.rb
|
40
42
|
- test/test_generator_basic_types.rb
|
43
|
+
- test/test_generator_collections.rb
|
41
44
|
- test/test_parser.rb
|
42
45
|
- test/assets/AlbumData.xml
|
46
|
+
- test/assets/commented.plist
|
43
47
|
- test/assets/Cookies.plist
|
48
|
+
- test/assets/example_data.bin
|
44
49
|
- test/assets/example_data.jpg
|
45
50
|
- test/assets/example_data.plist
|
51
|
+
- test/assets/test_data_elements.plist
|
46
52
|
- test/assets/test_empty_key.plist
|
47
53
|
test_files:
|
54
|
+
- test/test_data_elements.rb
|
55
|
+
- test/test_generator.rb
|
48
56
|
- test/test_generator_basic_types.rb
|
57
|
+
- test/test_generator_collections.rb
|
49
58
|
- test/test_parser.rb
|
50
59
|
rdoc_options: []
|
51
60
|
|