plist 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2006, Ben Bleything <ben@bleything.net>
2
+ and Patrick May <patrick@hexane.org>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included
13
+ in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16
+ KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,36 @@
1
+ = All-purpose Property List manipulation library
2
+
3
+ Plist is a library to manipulate Property List files, also known as plists. It can parse plist files into native Ruby data structures as well as generating new plist files from your Ruby objects.
4
+
5
+ == Usage
6
+
7
+ See USAGE[link:files/docs/USAGE.html].
8
+
9
+ == Links
10
+
11
+ [<b>Project Page</b>] http://plist.rubyforge.org
12
+ [<b>Subversion repository</b>] svn://rubyforge.org//var/svn/plist
13
+ [<b>RDoc (on RubyForge)</b>] http://plist.rubyforge.org
14
+
15
+ == Credits
16
+
17
+ plist is maintained by Ben Bleything <mailto:ben@bleything.net> and Patrick May <mailto:patrick@hexane.org>. Patrick wrote most of the code; Ben is a recent addition to the project, having merged in his plist generation library.
18
+
19
+ Other folks who have helped along the way:
20
+
21
+ [<b>Martin Dittus</b>] who pointed out that +Time+ wasn't enough for plist <tt>Dates</tt>, especially those in <tt>~/Library/Cookies/Cookies.plist</tt>
22
+ [<b>Chuck Remes</b>] who pushed Patrick towards implementing <tt>#to_plist</tt>
23
+ [<b>Mat Schaffer</b>] who supplied code and test cases for <tt><data></tt> elements
24
+ [<b>Michael Granger</b>] for encouragement and help
25
+
26
+ == License and Copyright
27
+
28
+ plist is released under the MIT License.
29
+
30
+ Portions of the code (notably the Rakefile) contain code pulled and/or adapted from other projects. These files contain a comment at the top describing what was used.
31
+
32
+ === MIT License
33
+
34
+ :include: MIT-LICENSE
35
+
36
+
data/Rakefile ADDED
@@ -0,0 +1,142 @@
1
+ ##############################################################
2
+ # Copyright 2006, Ben Bleything <ben@bleything.net> and #
3
+ # Patrick May <patrick@hexane.org> #
4
+ # #
5
+ # Based heavily on Geoffrey Grosenbach's Rakefile for gruff. #
6
+ # Includes whitespace-fixing task based on code from Typo. #
7
+ # #
8
+ # Distributed under the MIT license. #
9
+ ##############################################################
10
+
11
+ require 'fileutils'
12
+ require 'rubygems'
13
+ require 'rake'
14
+ require 'rake/testtask'
15
+ require 'rake/rdoctask'
16
+ require 'rake/packagetask'
17
+ require 'rake/gempackagetask'
18
+ require 'rake/contrib/rubyforgepublisher'
19
+
20
+ $:.unshift(File.dirname(__FILE__) + "/lib")
21
+ require 'plist'
22
+
23
+ PKG_NAME = 'plist'
24
+ PKG_VERSION = Plist::VERSION
25
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
26
+
27
+ RELEASE_NAME = "REL #{PKG_VERSION}"
28
+
29
+ RUBYFORGE_PROJECT = "plist"
30
+ RUBYFORGE_USER = ENV['RUBYFORGE_USER']
31
+
32
+ TEST_FILES = Dir.glob('test/**/*').delete_if {|item| item.include?( "\.svn" ) }
33
+ RELEASE_FILES = [ "Rakefile", "README", "MIT-LICENSE" ] + TEST_FILES + Dir.glob( "lib/*" ).delete_if { |item| item.include?( "\.svn" ) }
34
+
35
+ task :default => [ :test ]
36
+ # Run the unit tests
37
+ Rake::TestTask.new { |t|
38
+ t.libs << "test"
39
+ t.pattern = 'test/test_*.rb'
40
+ t.verbose = true
41
+ }
42
+
43
+ desc "Clean pkg, coverage, and rdoc; remove .bak files"
44
+ task :clean => [ :clobber_rdoc, :clobber_package, :clobber_coverage ] do
45
+ puts cmd = "find . -type f -name *.bak -delete"
46
+ `#{cmd}`
47
+ end
48
+
49
+ task :clobber_coverage do
50
+ puts cmd = "rm -rf coverage"
51
+ `#{cmd}`
52
+ end
53
+
54
+ desc "Generate coverage analysis with rcov (requires rcov to be installed)"
55
+ task :rcov => [ :clobber_coverage ] do
56
+ puts cmd = "rcov -Ilib --xrefs -T test/*.rb"
57
+ puts `#{cmd}`
58
+ end
59
+
60
+ desc "Strip trailing whitespace and fix newlines for all release files"
61
+ task :fix_whitespace => [ :clean ] do
62
+ RELEASE_FILES.each do |filename|
63
+ next if File.directory? filename
64
+
65
+ File.open(filename) do |file|
66
+ newfile = ''
67
+ needs_love = false
68
+
69
+ file.readlines.each_with_index do |line, lineno|
70
+ if line =~ /[ \t]+$/
71
+ needs_love = true
72
+ puts "#{filename}: trailing whitespace on line #{lineno}"
73
+ line.gsub!(/[ \t]*$/, '')
74
+ end
75
+
76
+ if line.chomp == line
77
+ needs_love = true
78
+ puts "#{filename}: no newline on line #{lineno}"
79
+ line << "\n"
80
+ end
81
+
82
+ newfile << line
83
+ end
84
+
85
+ if needs_love
86
+ tempname = "#{filename}.new"
87
+
88
+ File.open(tempname, 'w').write(newfile)
89
+ File.chmod(File.stat(filename).mode, tempname)
90
+
91
+ FileUtils.ln filename, "#{filename}.bak"
92
+ FileUtils.ln tempname, filename, :force => true
93
+ File.unlink(tempname)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ desc "Copy documentation to rubyforge"
100
+ task :update_rdoc => [ :rdoc ] do
101
+ Rake::SshDirPublisher.new("#{RUBYFORGE_USER}@rubyforge.org", "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}/sekr1t", "rdoc").upload
102
+ end
103
+
104
+ # Genereate the RDoc documentation
105
+ Rake::RDocTask.new { |rdoc|
106
+ rdoc.rdoc_dir = 'rdoc'
107
+ rdoc.title = "All-purpose Property List manipulation library"
108
+ rdoc.options << '-SNmREADME'
109
+ rdoc.template = "docs/jamis-template.rb"
110
+ rdoc.rdoc_files.include('README', 'MIT-LICENSE', 'CHANGELOG')
111
+ rdoc.rdoc_files.include Dir.glob('docs/**').delete_if {|f| f.include? 'jamis' }
112
+ rdoc.rdoc_files.include('lib/**')
113
+ }
114
+
115
+ # Create compressed packages
116
+ spec = Gem::Specification.new do |s|
117
+ s.name = PKG_NAME
118
+ s.version = PKG_VERSION
119
+
120
+ s.summary = "All-purpose Property List manipulation library."
121
+ s.description = <<-EOD
122
+ Plist is a library to manipulate Property List files, also known as plists. It can parse plist files into native Ruby data structures as well as generating new plist files from your Ruby objects.
123
+ EOD
124
+
125
+ s.authors = "Ben Bleything and Patrick May"
126
+ s.homepage = "http://plist.rubyforge.org"
127
+
128
+ s.rubyforge_project = RUBYFORGE_PROJECT
129
+
130
+ s.has_rdoc = true
131
+
132
+ s.files = RELEASE_FILES
133
+ s.test_files = TEST_FILES
134
+
135
+ s.autorequire = 'plist'
136
+ end
137
+
138
+ Rake::GemPackageTask.new(spec) do |p|
139
+ p.gem_spec = spec
140
+ p.need_tar = true
141
+ p.need_zip = true
142
+ end
data/lib/plist.rb ADDED
@@ -0,0 +1,19 @@
1
+ #--
2
+ ##############################################################
3
+ # Copyright 2006, Ben Bleything <ben@bleything.net> and #
4
+ # Patrick May <patrick@hexane.org> #
5
+ # #
6
+ # Distributed under the MIT license. #
7
+ ##############################################################
8
+ #++
9
+ # = Plist
10
+ #
11
+ # This is the main file for plist. Everything interesting happens in Plist and Plist::Emit.
12
+
13
+ require 'cgi'
14
+ require 'plist/generator'
15
+ require 'plist/parser'
16
+
17
+ module Plist
18
+ VERSION = '2.1.1'
19
+ end
File without changes
File without changes
File without changes
@@ -0,0 +1,13 @@
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>key</key>
6
+ <dict>
7
+ <key></key>
8
+ <string>1</string>
9
+ <key>subkey</key>
10
+ <string>2</string>
11
+ </dict>
12
+ </dict>
13
+ </plist>
@@ -0,0 +1,86 @@
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 TestBasicTypes < Test::Unit::TestCase
12
+ def wrap(tag, content)
13
+ return "<array>\n\t<#{tag}>#{content}</#{tag}>\n</array>"
14
+ end
15
+
16
+ def test_strings
17
+ expected = wrap('string', 'testdata')
18
+
19
+ assert_equal expected, ['testdata'].to_plist(false)
20
+ assert_equal expected, [:testdata].to_plist(false)
21
+ end
22
+
23
+ def test_integers
24
+ [42, 2376239847623987623, -8192].each do |i|
25
+ assert_equal wrap('integer', i), [i].to_plist(false)
26
+ end
27
+ end
28
+
29
+ def test_floats
30
+ [3.14159, -38.3897, 2398476293847.9823749872349980].each do |i|
31
+ assert_equal wrap('real', i), [i].to_plist(false)
32
+ end
33
+ end
34
+
35
+ 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)
38
+ end
39
+
40
+ def test_time
41
+ test_time = Time.now
42
+ assert_equal wrap('date', test_time.utc.strftime('%Y-%m-%dT%H:%M:%SZ')), [test_time].to_plist(false)
43
+ end
44
+
45
+ def test_dates
46
+ test_date = Date.today
47
+ test_datetime = DateTime.now
48
+
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')
79
+ 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
+ end
@@ -1,3 +1,10 @@
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
+
1
8
  require 'test/unit'
2
9
  require 'pp'
3
10
 
@@ -5,7 +12,7 @@ require 'plist'
5
12
 
6
13
  class TestPlist < Test::Unit::TestCase
7
14
  def test_Plist_parse_xml
8
- result = Plist::parse_xml("AlbumData.xml")
15
+ result = Plist::parse_xml("test/assets/AlbumData.xml")
9
16
 
10
17
  # dict
11
18
  assert_kind_of( Hash, result )
@@ -28,7 +35,7 @@ class TestPlist < Test::Unit::TestCase
28
35
  "AlbumName"=>"Roll 1",
29
36
  "AlbumId"=>6}],
30
37
  result["List of Rolls"] )
31
-
38
+
32
39
  # string
33
40
  assert_kind_of( String, result["Application Version"] )
34
41
  assert_equal( "5.0.4 (263)", result["Application Version"] )
@@ -47,59 +54,49 @@ class TestPlist < Test::Unit::TestCase
47
54
 
48
55
  end
49
56
 
57
+ # uncomment this test to work on speed optimization
50
58
  #def test_load_something_big
51
59
  # plist = Plist::parse_xml( "~/Pictures/iPhoto Library/AlbumData.xml" )
52
60
  #end
53
61
 
54
62
 
55
- # date fields are credited to
63
+ # date fields are credited to
56
64
  def test_date_fields
57
- result = Plist::parse_xml("Cookies.plist")
65
+ result = Plist::parse_xml("test/assets/Cookies.plist")
58
66
  assert_kind_of( DateTime, result.first['Expires'] )
59
67
  assert_equal( "2007-10-25T12:36:35Z", result.first['Expires'].to_s )
60
68
  end
61
69
 
62
- def test_to_plist
63
- assert_equal( Plist::_xml("<string>Hello, World</string>"), "Hello, World".to_plist )
64
- assert_equal( Plist::_xml("<real>151936595.697543</real>"), 151936595.697543.to_plist )
65
- assert_equal( Plist::_xml("<date>2006-04-21T16:47:58Z</date>"), DateTime.parse("2006-04-21T16:47:58Z").to_plist )
66
- assert_equal( Plist::_xml("<integer>999000</integer>"), 999000.to_plist )
67
- assert_equal( Plist::_xml("<false/>"), false.to_plist )
68
- assert_equal( Plist::_xml("<true/>"), true.to_plist )
69
-
70
- assert_equal( Plist::_xml("<array>\n\t<true/>\n\t<false/>\n</array>"),
71
- [ true, false ].to_plist )
72
-
73
- assert_equal( Plist::_xml("<dict>\n\t<key>False</key>\n\t<false/>\n\t<key>True</key>\n\t<true/>\n</dict>"),
74
- { 'True' => true, 'False' => false }.to_plist )
75
-
76
- source = File.open("AlbumData.xml") { |f| f.read }
77
-
78
- result = Plist::parse_xml(source)
79
-
80
- assert_equal( result, Plist::parse_xml(result.to_plist) )
81
-
82
- File.delete('hello.plist') if File.exists?('hello.plist')
83
- "Hello, World".save_plist('hello.plist')
84
- assert_equal( Plist::_xml("<string>Hello, World</string>"),
85
- File.open('hello.plist') {|f| f.read } )
86
- end
87
-
88
70
  # this functionality is credited to Mat Schaffer,
89
71
  # who discovered the plist with the data tag
90
72
  # supplied the test data, and provided the parsing code.
91
73
  def test_data
92
- data = Plist::parse_xml("example_data.plist");
93
- assert_equal( File.open("example_data.jpg"){|f| f.read }, data['image'].read )
94
- assert_equal( File.open("example_data.plist"){|f| f.read }, data.to_plist )
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 )
95
77
 
96
- data['image'] = StringIO.new( File.open("example_data.jpg"){ |f| f.read } )
78
+ data['image'] = StringIO.new( File.open("test/assets/example_data.jpg"){ |f| f.read } )
97
79
  File.open('temp.plist', 'w'){|f| f.write data.to_plist }
98
- assert_equal( File.open("example_data.plist"){|f| f.read }, data.to_plist )
80
+ assert_equal( File.open("test/assets/example_data.plist"){|f| f.read }, data.to_plist )
99
81
 
82
+ File.delete('temp.plist') if File.exists?('temp.plist')
100
83
 
101
84
  end
102
85
 
86
+ # bug fix for empty <key>
87
+ # reported by Matthias Peick <matthias@peick.de>
88
+ # reported and fixed by Frederik Seiffert <ego@frederikseiffert.de>
89
+ def test_empty_dict_key
90
+ data = Plist::parse_xml("test/assets/test_empty_key.plist");
91
+ assert_equal("2", data['key']['subkey'])
92
+ end
93
+
94
+ # bug fix for decoding entities
95
+ # reported by Matthias Peick <matthias@peick.de>
96
+ def test_decode_entities
97
+ data = Plist::parse_xml(Plist::_xml('<string>Fish &amp; Chips</string>'))
98
+ assert_equal('Fish & Chips', data)
99
+ end
103
100
  end
104
101
 
105
102
  __END__
metadata CHANGED
@@ -3,15 +3,17 @@ rubygems_version: 0.8.11.3
3
3
  specification_version: 1
4
4
  name: plist
5
5
  version: !ruby/object:Gem::Version
6
- version: 2.1.0
7
- date: 2006-07-29 00:00:00 -04:00
8
- summary: plist parses Mac OS X plist files into ruby data types.
6
+ version: 2.1.1
7
+ date: 2006-09-10 00:00:00 -04:00
8
+ summary: All-purpose Property List manipulation library.
9
9
  require_paths:
10
- - "."
11
- email: plist@hexane.org
12
- homepage: http://www.narf-lib.org
13
- rubyforge_project: narf
14
- description: ''
10
+ - lib
11
+ email:
12
+ homepage: http://plist.rubyforge.org
13
+ rubyforge_project: plist
14
+ description: "Plist is a library to manipulate Property List files, also known as plists. It
15
+ can parse plist files into native Ruby data structures as well as generating new
16
+ plist files from your Ruby objects."
15
17
  autorequire: plist
16
18
  default_executable:
17
19
  bindir: bin
@@ -27,16 +29,30 @@ platform: ruby
27
29
  signing_key:
28
30
  cert_chain:
29
31
  authors:
30
- - Patrick May
32
+ - Ben Bleything and Patrick May
31
33
  files:
32
- - plist.rb
33
- - test_plist.rb
34
- - AlbumData.xml
35
- - Cookies.plist
36
- - example_data.jpg
37
- - example_data.plist
34
+ - Rakefile
35
+ - README
36
+ - MIT-LICENSE
37
+ - test/assets
38
+ - test/test_generator_basic_types.rb
39
+ - test/test_parser.rb
40
+ - test/assets/AlbumData.xml
41
+ - test/assets/Cookies.plist
42
+ - test/assets/example_data.jpg
43
+ - test/assets/example_data.plist
44
+ - test/assets/test_empty_key.plist
45
+ - lib/plist
46
+ - lib/plist.rb
38
47
  test_files:
39
- - test_plist.rb
48
+ - test/assets
49
+ - test/test_generator_basic_types.rb
50
+ - test/test_parser.rb
51
+ - test/assets/AlbumData.xml
52
+ - test/assets/Cookies.plist
53
+ - test/assets/example_data.jpg
54
+ - test/assets/example_data.plist
55
+ - test/assets/test_empty_key.plist
40
56
  rdoc_options: []
41
57
  extra_rdoc_files: []
42
58
  executables: []
data/plist.rb DELETED
@@ -1,375 +0,0 @@
1
- #require 'rexml/document'
2
- #require 'rexml/streamlistener'
3
-
4
- # Plist parses Mac OS X xml property list files into ruby data structures.
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
- # Notes:
32
- #
33
- # + Array and Hash are recursive -- the elements of an Array and the values of a Hash
34
- # must convert to a plist.
35
- # + The keys of the Hash must be strings.
36
- # + The contents of data elements are returned as a Tempfile.
37
- # + Data elements can be set with to an open IO or a StringIO
38
- #
39
- # If you have suggestions for mapping other Ruby types to the plist types, send a note to:
40
- #
41
- # mailto:plist@hexane.org
42
- #
43
- # I'll take a look and probably add it, I'm just reticent to create too many
44
- # "convenience" methods without at least agreeing with someone :-)
45
- #
46
- # === Credits
47
- # plist.rb has been implemented by Patrick May. A few other folks have been helpful in developing plist.rb:
48
- #
49
- # + Martin Dittus, who pointed out that Time wasn't enough for plist Dates,
50
- # especially those in "~/Library/Cookies/Cookies.plist"
51
- #
52
- # + Chuck Remes, who pushed me towards implementing #to_plist
53
- #
54
- # + Mat Schaffer, who supplied code and test cases for <data> elements
55
- #
56
- class Plist
57
-
58
- TEMPLATE = <<-XML
59
- <?xml version="1.0" encoding="UTF-8"?>
60
- <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
61
- <plist version="1.0">
62
- %plist%
63
- </plist>
64
- XML
65
- def Plist::_xml( xml )
66
- TEMPLATE.sub( /%plist%/, xml )
67
- end
68
-
69
- # Note that I don't use these two elements much:
70
- #
71
- # + Date elements are returned as DateTime objects.
72
- # + Data elements are implemented as a subcase of strings:
73
- # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/31933
74
- #
75
- # Plist::parse_xml will blow up if it encounters a data element.
76
- # If you encounter such an error, or if you have a Date element which
77
- # can't be parsed into a Time object, please send your plist file to
78
- # plist@hexane.org so that I can implement the proper support.
79
- def Plist::parse_xml( filename_or_xml )
80
- listener = Listener.new
81
- #parser = REXML::Parsers::StreamParser.new(File.new(filename), listener)
82
- parser = StreamParser.new(filename_or_xml, listener)
83
- parser.parse
84
- listener.result
85
- end
86
-
87
- class Listener
88
- #include REXML::StreamListener
89
-
90
- attr_accessor :result, :open
91
-
92
- def initialize
93
- @result = nil
94
- @open = Array.new
95
- end
96
-
97
-
98
- def tag_start(name, attributes)
99
- @open.push PTag::mappings[name].new
100
- end
101
-
102
- def text( contents )
103
- @open.last.text = contents if @open.last
104
- end
105
-
106
- def tag_end(name)
107
- last = @open.pop
108
- if @open.empty?
109
- @result = last.to_ruby
110
- else
111
- @open.last.children.push last
112
- end
113
- end
114
- end
115
-
116
- class StreamParser
117
- def initialize( filename_or_xml, listener )
118
- @filename_or_xml = filename_or_xml
119
- @listener = listener
120
- end
121
-
122
- TEXT = /([^<]+)/
123
- XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um
124
- DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
125
-
126
-
127
- def parse
128
- plist_tags = PTag::mappings.keys.join('|')
129
- start_tag = /<(#{plist_tags})([^>]*)>/i
130
- end_tag = /<\/(#{plist_tags})[^>]*>/i
131
-
132
- require 'strscan'
133
- @scanner = StringScanner.new( if (File.exists? @filename_or_xml)
134
- File.open(@filename_or_xml, "r") {|f| f.read}
135
- else
136
- @filename_or_xml
137
- end )
138
- until @scanner.eos?
139
- if @scanner.scan(XMLDECL_PATTERN)
140
- elsif @scanner.scan(DOCTYPE_PATTERN)
141
- elsif @scanner.scan(start_tag)
142
- @listener.tag_start(@scanner[1], nil)
143
- if (@scanner[2] =~ /\/$/)
144
- @listener.tag_end(@scanner[1])
145
- end
146
- elsif @scanner.scan(TEXT)
147
- @listener.text(@scanner[1])
148
- elsif @scanner.scan(end_tag)
149
- @listener.tag_end(@scanner[1])
150
- else
151
- raise "Unimplemented element"
152
- end
153
- end
154
- end
155
- end
156
-
157
- class PTag
158
- @@mappings = { }
159
- def PTag::mappings
160
- @@mappings
161
- end
162
-
163
- def PTag::inherited( sub_class )
164
- key = sub_class.to_s.downcase
165
- key.gsub!(/^plist::/, '' )
166
- key.gsub!(/^p/, '') unless key == "plist"
167
-
168
- @@mappings[key] = sub_class
169
- end
170
-
171
- attr_accessor :text, :children
172
- def initialize
173
- @children = Array.new
174
- end
175
-
176
- def to_ruby
177
- raise "Unimplemented: " + self.class.to_s + "#to_ruby on #{self.inspect}"
178
- end
179
- end
180
-
181
- class PList < PTag
182
- def to_ruby
183
- children.first.to_ruby
184
- end
185
- end
186
-
187
- class PDict < PTag
188
- def to_ruby
189
- dict = Hash.new
190
- key = nil
191
-
192
- children.each do |c|
193
- if key.nil?
194
- key = c.to_ruby
195
- else
196
- dict[key] = c.to_ruby
197
- key = nil
198
- end
199
- end
200
-
201
- dict
202
- end
203
- end
204
-
205
- class PKey < PTag
206
- def to_ruby
207
- text
208
- end
209
- end
210
-
211
- class PString < PTag
212
- def to_ruby
213
- text || ''
214
- end
215
- end
216
-
217
- class PArray < PTag
218
- def to_ruby
219
- children.collect do |c|
220
- c.to_ruby
221
- end
222
- end
223
- end
224
-
225
- class PInteger < PTag
226
- def to_ruby
227
- text.to_i
228
- end
229
- end
230
-
231
- class PTrue < PTag
232
- def to_ruby
233
- true
234
- end
235
- end
236
-
237
- class PFalse < PTag
238
- def to_ruby
239
- false
240
- end
241
- end
242
-
243
- class PReal < PTag
244
- def to_ruby
245
- text.to_f
246
- end
247
- end
248
-
249
- require 'date'
250
- class PDate < PTag
251
- def to_ruby
252
- DateTime.parse(text)
253
- end
254
- end
255
-
256
- require 'base64'
257
- require 'tempfile'
258
- class PData < PTag
259
- def to_ruby
260
- tf = Tempfile.new("plist.tmp")
261
- tf.write Base64.decode64(text.gsub(/\s+/,''))
262
- tf.close
263
- # is this a good idea?
264
- tf.open
265
- tf
266
- end
267
- end
268
-
269
- module Emit
270
- def save_plist(filename)
271
- File.open(filename, 'wb') do |f|
272
- f.write(self.to_plist)
273
- end
274
- end
275
-
276
- # Only the expected classes can be emitted as a plist:
277
- # String, Float, DateTime, Integer, TrueClass, FalseClass, Array, Hash
278
- #
279
- # Write me if you think another class can be coerced safely into one of the
280
- # expected plist classes (plist@hexane.org)
281
- def to_plist
282
- Plist::_xml(self.to_plist_fragment)
283
- end
284
- end
285
- end
286
-
287
- class String
288
- include Plist::Emit
289
- def to_plist_fragment
290
- "<string>#{self}</string>"
291
- end
292
- end
293
-
294
- class Float
295
- include Plist::Emit
296
- def to_plist_fragment
297
- "<real>#{self}</real>"
298
- end
299
- end
300
-
301
- class DateTime
302
- include Plist::Emit
303
- def to_plist_fragment
304
- "<date>#{self}</date>"
305
- end
306
- end
307
-
308
- class Integer
309
- include Plist::Emit
310
- def to_plist_fragment
311
- "<integer>#{self}</integer>"
312
- end
313
- end
314
-
315
- class FalseClass
316
- include Plist::Emit
317
- def to_plist_fragment
318
- "<false/>"
319
- end
320
- end
321
-
322
- class TrueClass
323
- include Plist::Emit
324
- def to_plist_fragment
325
- "<true/>"
326
- end
327
- end
328
-
329
- class Array
330
- include Plist::Emit
331
- def to_plist_fragment
332
- fragment = "<array>\n"
333
- self.each do |e|
334
- element_plist = e.to_plist_fragment
335
- element_plist.each do |l|
336
- fragment += "\t#{l.chomp}\n"
337
- end
338
- end
339
- fragment += "</array>"
340
- fragment
341
- end
342
- end
343
-
344
- class Hash
345
- include Plist::Emit
346
- def to_plist_fragment
347
- fragment = "<dict>\n"
348
- self.keys.sort.each do |k|
349
- fragment += "\t<key>#{k}</key>\n"
350
- element_plist = self[k].to_plist_fragment
351
- element_plist.each do |l|
352
- fragment += "\t#{l.chomp}\n"
353
- end
354
- end
355
- fragment += "</dict>"
356
- fragment
357
- end
358
- end
359
-
360
- require 'stringio'
361
- [ IO, StringIO ].each do |io_class|
362
- io_class.module_eval do
363
- include Plist::Emit
364
- def to_plist_fragment
365
- self.rewind
366
- data = self.read
367
-
368
- output = "<data>\n"
369
- Base64::encode64(data).gsub(/\s+/, '').scan(/.{1,68}/o) { output << $& << "\n" }
370
- output << "</data>"
371
-
372
- output
373
- end
374
- end
375
- end