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 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}/sekr1t", "rdoc").upload
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
- === Example Property List
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
- result = Plist::parse_xml("path/to/example.plist")
49
+ == Generation
39
50
 
40
- result.class
41
- => Hash
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
- "#{result["FirstName"]} #{result["LastName"]}"
44
- => "John Public"
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
- result["ZipPostal"]
47
- => "12345"
48
-
49
- === Generation
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
- coming.soon {|i| i.promise }
104
+ If for whatever reason you can't add this method, your object will be serialized with <tt>Marshal.dump</tt> instead.
@@ -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 = '2.1.2'
21
+ VERSION = '3.0.0'
19
22
  end
@@ -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
- # === Save a plist
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(self.to_plist)
54
+ f.write(obj.to_plist)
47
55
  end
48
56
  end
49
57
 
50
- # Only the expected classes can be emitted as a plist:
51
- # String, Float, DateTime, Integer, TrueClass, FalseClass, Array, Hash
52
- #
53
- # Write me if you think another class can be coerced safely into one of the
54
- # expected plist classes (plist@hexane.org)
55
- def to_plist( header = true )
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
- self.to_plist_node
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
- class String
66
- include Plist::Emit
67
- def to_plist_node
68
- "<string>#{CGI::escapeHTML(self)}</string>"
69
- end
70
- end
119
+ def self.comment(content)
120
+ return "<!-- #{content} -->\n"
121
+ end
71
122
 
72
- class Symbol
73
- include Plist::Emit
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
- class Float
80
- include Plist::Emit
81
- def to_plist_node
82
- "<real>#{self}</real>"
83
- end
84
- end
126
+ if block_given?
127
+ out = IndentedString.new
128
+ out << "<#{type}>"
129
+ out.raise_indent
85
130
 
86
- class Time
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
- class Date
94
- include Plist::Emit
95
- def to_plist_node
96
- "<date>#{self.strftime('%Y-%m-%dT%H:%M:%SZ')}</date>"
97
- end
98
- end
133
+ out.lower_indent
134
+ out << "</#{type}>"
135
+ else
136
+ out = "<#{type}>#{contents.to_s}</#{type}>\n"
137
+ end
99
138
 
100
- class Integer
101
- include Plist::Emit
102
- def to_plist_node
103
- "<integer>#{self}</integer>"
104
- end
105
- end
139
+ return out.to_s
140
+ end
106
141
 
107
- class FalseClass
108
- include Plist::Emit
109
- def to_plist_node
110
- "<false/>"
111
- end
112
- end
142
+ def self.wrap(contents)
143
+ output = ''
113
144
 
114
- class TrueClass
115
- include Plist::Emit
116
- def to_plist_node
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
- class Array
122
- include Plist::Emit
123
- def to_plist_node
124
- fragment = "<array>\n"
125
- self.each do |e|
126
- element_plist = e.to_plist_node
127
- element_plist.each do |l|
128
- fragment += "\t#{l.chomp}\n"
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
- fragment += "</array>"
132
- fragment
133
- end
134
- end
165
+ private
166
+ class IndentedString #:nodoc:
167
+ attr_accessor :indent_string
135
168
 
136
- class Hash
137
- include Plist::Emit
138
- def to_plist_node
139
- fragment = "<dict>\n"
140
- self.keys.sort.each do |k|
141
- fragment += "\t<key>#{CGI::escapeHTML(k)}</key>\n"
142
- element_plist = self[k].to_plist_node
143
- element_plist.each do |l|
144
- fragment += "\t#{l.chomp}\n"
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
- require 'stringio'
153
- [ IO, StringIO ].each do |io_class|
154
- io_class.module_eval do
155
- include Plist::Emit
156
- def to_plist_node
157
- self.rewind
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
- output = "<data>\n"
161
- Base64::encode64(data).gsub(/\s+/, '').scan(/.{1,68}/o) { output << $& << "\n" }
162
- output << "</data>"
218
+ class Array #:nodoc:
219
+ include Plist::Emit
220
+ end
163
221
 
164
- output
165
- end
166
- end
222
+ class Hash #:nodoc:
223
+ include Plist::Emit
167
224
  end
@@ -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
- @scanner = StringScanner.new( if (File.exists? @filename_or_xml)
90
- File.open(@filename_or_xml, "r") {|f| f.read}
91
- else
92
- @filename_or_xml
93
- end )
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(XMLDECL_PATTERN)
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
- # replacing Tempfile with StringIO
216
- # think it might be a bit nicer
217
- #require 'tempfile'
218
- #tf = Tempfile.new("plist.tmp")
219
- #tf.write Base64.decode64(text.gsub(/\s+/,''))
220
- #tf.close
221
- # is this a good idea?
222
- #tf.open
223
- #tf
224
- io = StringIO.new
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>
@@ -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>
@@ -10,4 +10,4 @@
10
10
  <string>2</string>
11
11
  </dict>
12
12
  </dict>
13
- </plist>
13
+ </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 TestBasicTypes < Test::Unit::TestCase
11
+ class TestGeneratorBasicTypes < Test::Unit::TestCase
12
12
  def wrap(tag, content)
13
- return "<array>\n\t<#{tag}>#{content}</#{tag}>\n</array>"
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, ['testdata'].to_plist(false)
20
- assert_equal expected, [:testdata].to_plist(false)
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', "&lt;Fish &amp; Chips&gt;")
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), [i].to_plist(false)
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), [i].to_plist(false)
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 "<array>\n\t<true/>\n</array>", [true].to_plist(false)
37
- assert_equal "<array>\n\t<false/>\n</array>", [false].to_plist(false)
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')), [test_time].to_plist(false)
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')), [test_date].to_plist(false)
50
- assert_equal wrap('date', test_datetime.strftime('%Y-%m-%dT%H:%M:%SZ')), [test_datetime].to_plist(false)
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>&lt;plist&gt;</string>"), "<plist>".to_plist )
83
- assert_equal( Plist::_xml("<string>Fish &amp; 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
@@ -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 TestPlist < Test::Unit::TestCase
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(Plist::_xml('<string>Fish &amp; Chips</string>'))
79
+ data = Plist::parse_xml('<string>Fish &amp; 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: 2.1.2
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