plist 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,178 @@
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
+ # Note: Date and Data elements are not yet implemented. Plist::parse_xml
7
+ # should blow up if when it encounters such elements. If you encounter
8
+ # such an error, please send your plist file to patrick@hexane.org,
9
+ # so that I can implement the proper support.
10
+ #
11
+ # The main point of this api is one method: Plist::parse_xml( filename )
12
+ class Plist
13
+
14
+ def Plist::parse_xml( filename )
15
+ listener = Listener.new
16
+ #parser = REXML::Parsers::StreamParser.new(File.new(filename), listener)
17
+ parser = StreamParser.new(filename, listener)
18
+ parser.parse
19
+ listener.result
20
+ end
21
+
22
+ class Listener
23
+ attr_accessor :result, :open
24
+
25
+ def initialize
26
+ @result = nil
27
+ @open = Array.new
28
+ end
29
+
30
+ #include REXML::StreamListener
31
+
32
+ def tag_start(name, attributes)
33
+ @open.push PTag::mappings[name].new
34
+ end
35
+
36
+ def text( contents )
37
+ @open.last.text = contents if @open.last
38
+ end
39
+
40
+ def tag_end(name)
41
+ last = @open.pop
42
+ if @open.empty?
43
+ @result = last.to_ruby
44
+ else
45
+ @open.last.children.push last
46
+ end
47
+ end
48
+ end
49
+
50
+ class StreamParser
51
+ def initialize( filename, listener )
52
+ @filename = filename
53
+ @listener = listener
54
+ end
55
+
56
+ TEXT = /([^<]+)/
57
+ XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um
58
+ DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
59
+
60
+
61
+ def parse
62
+ plist_tags = PTag::mappings.keys.join('|')
63
+ start_tag = /<(#{plist_tags})([^>]*)>/i
64
+ end_tag = /<\/(#{plist_tags})[^>]*>/i
65
+
66
+ require 'strscan'
67
+ @scanner = StringScanner.new( File.open(@filename, "r") {|f| f.read} )
68
+ until @scanner.eos?
69
+ if @scanner.scan(XMLDECL_PATTERN)
70
+ elsif @scanner.scan(DOCTYPE_PATTERN)
71
+ elsif @scanner.scan(start_tag)
72
+ @listener.tag_start(@scanner[1], nil)
73
+ if (@scanner[2] =~ /\/$/)
74
+ @listener.tag_end(@scanner[1])
75
+ end
76
+ elsif @scanner.scan(TEXT)
77
+ @listener.text(@scanner[1])
78
+ elsif @scanner.scan(end_tag)
79
+ @listener.tag_end(@scanner[1])
80
+ else
81
+ raise "Unimplemented element"
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ class PTag
88
+ @@mappings = { }
89
+ def PTag::mappings
90
+ @@mappings
91
+ end
92
+
93
+ def PTag::inherited( sub_class )
94
+ key = sub_class.to_s.downcase
95
+ key.gsub!(/^plist::/, '' )
96
+ key.gsub!(/^p/, '') unless key == "plist"
97
+
98
+ @@mappings[key] = sub_class
99
+ end
100
+
101
+ attr_accessor :text, :children
102
+ def initialize
103
+ @children = Array.new
104
+ end
105
+
106
+ def to_ruby
107
+ raise "Unimplemented: " + self.class.to_s + "#to_ruby on #{self.inspect}"
108
+ end
109
+ end
110
+
111
+ class PList < PTag
112
+ def to_ruby
113
+ children.first.to_ruby
114
+ end
115
+ end
116
+
117
+ class PDict < PTag
118
+ def to_ruby
119
+ dict = Hash.new
120
+ key = nil
121
+
122
+ children.each do |c|
123
+ if key.nil?
124
+ key = c.to_ruby
125
+ else
126
+ dict[key] = c.to_ruby
127
+ key = nil
128
+ end
129
+ end
130
+
131
+ dict
132
+ end
133
+ end
134
+
135
+ class PKey < PTag
136
+ def to_ruby
137
+ text
138
+ end
139
+ end
140
+
141
+ class PString < PTag
142
+ def to_ruby
143
+ text
144
+ end
145
+ end
146
+
147
+ class PArray < PTag
148
+ def to_ruby
149
+ children.collect do |c|
150
+ c.to_ruby
151
+ end
152
+ end
153
+ end
154
+
155
+ class PInteger < PTag
156
+ def to_ruby
157
+ text.to_i
158
+ end
159
+ end
160
+
161
+ class PTrue < PTag
162
+ def to_ruby
163
+ true
164
+ end
165
+ end
166
+
167
+ class PFalse < PTag
168
+ def to_ruby
169
+ false
170
+ end
171
+ end
172
+
173
+ class PReal < PTag
174
+ def to_ruby
175
+ text.to_f
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,56 @@
1
+ require 'test/unit'
2
+ require 'pp'
3
+
4
+ require 'plist'
5
+
6
+ class TestPlist < Test::Unit::TestCase
7
+ def test_Plist_parse_xml
8
+ result = Plist::parse_xml("AlbumData.xml")
9
+
10
+ # dict
11
+ assert_kind_of( Hash, result )
12
+ assert_equal( ["List of Albums",
13
+ "Minor Version",
14
+ "Master Image List",
15
+ "Major Version",
16
+ "List of Keywords",
17
+ "Archive Path",
18
+ "List of Rolls",
19
+ "Application Version"],
20
+ result.keys )
21
+
22
+ # array
23
+ assert_kind_of( Array, result["List of Rolls"] )
24
+ assert_equal( [ {"PhotoCount"=>1,
25
+ "KeyList"=>["7"],
26
+ "Parent"=>999000,
27
+ "Album Type"=>"Regular",
28
+ "AlbumName"=>"Roll 1",
29
+ "AlbumId"=>6}],
30
+ result["List of Rolls"] )
31
+
32
+ # string
33
+ assert_kind_of( String, result["Application Version"] )
34
+ assert_equal( "5.0.4 (263)", result["Application Version"] )
35
+
36
+ # integer
37
+ assert_kind_of( Integer, result["Major Version"] )
38
+ assert_equal( 2, result["Major Version"] )
39
+
40
+ # true
41
+ assert_kind_of( TrueClass, result["List of Albums"][0]["Master"] )
42
+ assert( result["List of Albums"][0]["Master"] )
43
+
44
+ # false
45
+ assert_kind_of( FalseClass, result["List of Albums"][1]["SlideShowUseTitles"] )
46
+ assert( ! result["List of Albums"][1]["SlideShowUseTitles"] )
47
+
48
+ end
49
+
50
+ #def test_load_something_big
51
+ # plist = Plist::parse_xml( "/Users/patsplat/Pictures/iPhoto Library/AlbumData.xml" )
52
+ #end
53
+
54
+ end
55
+
56
+ __END__
metadata CHANGED
@@ -1,51 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.11
2
+ rubygems_version: 0.8.11.3
3
3
  specification_version: 1
4
4
  name: plist
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2006-07-31 00:00:00 -07:00
8
- summary: Serialize your data as a Property List (aka plist).
6
+ version: 1.0.0
7
+ date: 2006-01-08 00:00:00 -05:00
8
+ summary: plist parses Mac OS X plist files into ruby data types.
9
9
  require_paths:
10
- - lib
11
- email: ben@bleything.net
12
- homepage: http://projects.bleything.net/plist
13
- rubyforge_project: plist
14
- description: The Property List (plist) Generator allows you to serialize your data to Property Lists. This is especially useful when writing system-level code for Mac OS X, but has other applications as well. The basic Ruby datatypes (numbers, strings, symbols, dates/times, arrays, and hashes) can be natively converted to plist types, and other types are Marshal'ed into the plist <data> type.
10
+ - "."
11
+ email: plist@hexane.org
12
+ homepage: http://www.narf-lib.org
13
+ rubyforge_project: narf
14
+ description: ''
15
15
  autorequire: plist
16
16
  default_executable:
17
17
  bindir: bin
18
18
  has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
24
25
  version:
25
26
  platform: ruby
26
27
  signing_key:
27
28
  cert_chain:
28
29
  authors:
29
- - Ben Bleything
30
+ - Patrick May
30
31
  files:
31
- - Rakefile
32
- - README
33
- - MIT-LICENSE
34
- - test/test_basic_types.rb
35
- - test/test_collections.rb
36
- - lib/plist.rb
32
+ - plist.rb
33
+ - test_plist.rb
37
34
  test_files:
38
- - test/test_basic_types.rb
39
- - test/test_collections.rb
35
+ - test_plist.rb
40
36
  rdoc_options: []
41
-
42
37
  extra_rdoc_files: []
43
-
44
38
  executables: []
45
-
46
39
  extensions: []
47
-
48
40
  requirements: []
49
-
50
- dependencies: []
51
-
41
+ dependencies: []
@@ -1,20 +0,0 @@
1
- Copyright (c) 2006, Ben Bleything <ben@bleything.net>
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be included
12
- in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
15
- KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
16
- WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README DELETED
@@ -1,10 +0,0 @@
1
- = Ruby PropertyList (plist) Generator
2
-
3
- plist adds Array#to_plist and Hash#to_plist, as well as providing
4
- infrastructure so that you can add plist serialization to your own
5
- objects.
6
-
7
- == License and Copyright
8
-
9
- plist is released under the MIT License (see the MIT-LICENSE file) and
10
- is Copyright 2006, Ben Bleything.
data/Rakefile DELETED
@@ -1,129 +0,0 @@
1
- ##############################################################
2
- # Copyright 2006, Ben Bleything. #
3
- # <ben@bleything.net> #
4
- # #
5
- # Based heavily on Geoffrey Grosenbach's Rakefile for gruff. #
6
- # Includes whitespace-fixing code 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 = "bleything"
31
-
32
- TEST_FILES = Dir.glob('test/test_*.rb').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 and docs, remove .bak files"
44
- task :clean => [ :clobber_rdoc, :clobber_package ] do
45
- puts cmd = "find . -type f -name *.bak -delete"
46
- `#{cmd}`
47
- end
48
-
49
- desc "Strip trailing whitespace and fix newlines for all release files"
50
- task :fix_whitespace => [ :clean ] do
51
- RELEASE_FILES.each do |filename|
52
- File.open(filename) do |file|
53
- newfile = ''
54
- needs_love = false
55
-
56
- file.readlines.each_with_index do |line, lineno|
57
- if line =~ /[ \t]+$/
58
- needs_love = true
59
- puts "#{filename}: trailing whitespace on line #{lineno}"
60
- line.gsub!(/[ \t]*$/, '')
61
- end
62
-
63
- if line.chomp == line
64
- needs_love = true
65
- puts "#{filename}: no newline on line #{lineno}"
66
- line << "\n"
67
- end
68
-
69
- newfile << line
70
- end
71
-
72
- if needs_love
73
- tempname = "#{filename}.new"
74
-
75
- File.open(tempname, 'w').write(newfile)
76
- File.chmod(File.stat(filename).mode, tempname)
77
-
78
- FileUtils.ln filename, "#{filename}.bak"
79
- FileUtils.ln tempname, filename, :force => true
80
- File.unlink(tempname)
81
- end
82
- end
83
- end
84
- end
85
-
86
- desc "Copy documentation to rubyforge"
87
- task :update_rdoc => [ :rdoc ] do
88
- Rake::SshDirPublisher.new("#{RUBYFORGE_USER}@rubyforge.org", "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}", "docs").upload
89
- end
90
-
91
- # Genereate the RDoc documentation
92
- Rake::RDocTask.new { |rdoc|
93
- rdoc.rdoc_dir = 'docs'
94
- rdoc.title = "PropertyList Generator -- plist"
95
- # rdoc.options << '--line-numbers --inline-source --main README --accessor adv_attr_accessor=M'
96
- rdoc.template = "#{ENV['template']}.rb" if ENV['template']
97
- rdoc.rdoc_files.include('README')
98
- rdoc.rdoc_files.include('lib/plist.rb')
99
- }
100
-
101
- # Create compressed packages
102
- spec = Gem::Specification.new do |s|
103
- s.name = PKG_NAME
104
- s.version = PKG_VERSION
105
-
106
- s.summary = "Serialize your data as a Property List (aka plist)."
107
- s.description = <<-EOD
108
- The Property List (plist) Generator allows you to serialize your data to Property Lists. This is especially useful when writing system-level code for Mac OS X, but has other applications as well. The basic Ruby datatypes (numbers, strings, symbols, dates/times, arrays, and hashes) can be natively converted to plist types, and other types are Marshal'ed into the plist <data> type.
109
- EOD
110
-
111
- s.author = "Ben Bleything"
112
- s.email = "ben@bleything.net"
113
- s.homepage = "http://projects.bleything.net/plist"
114
-
115
- s.rubyforge_project = RUBYFORGE_PROJECT
116
-
117
- s.has_rdoc = true
118
-
119
- s.files = RELEASE_FILES
120
- s.test_files = TEST_FILES
121
-
122
- s.autorequire = 'plist'
123
- end
124
-
125
- Rake::GemPackageTask.new(spec) do |p|
126
- p.gem_spec = spec
127
- p.need_tar = true
128
- p.need_zip = true
129
- end
@@ -1,104 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- ######################################
4
- # Copyright 2006, Ben Bleything. #
5
- # <ben@bleything.net> #
6
- # #
7
- # Distributed under the MIT license. #
8
- ######################################
9
-
10
- #
11
- # plist data types:
12
- #
13
- # CFString -> String
14
- # CFNumber -> Float(?)
15
- # CFBoolean -> <true/> or <false/>
16
- # CFDate -> Date
17
- # CFData -> binary data
18
- #
19
- # CFArray -> Array
20
- # CFDictionary -> Hash (important: all keys must be CFString/String)
21
- #
22
-
23
- require 'date'
24
-
25
- module Plist
26
- VERSION = '0.0.1'
27
-
28
- def to_plist(header = true)
29
- output = []
30
-
31
- if header
32
- output << '<?xml version="1.0" encoding="UTF-8"?>'
33
- output << '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
34
- output << '<plist version="1.0">'
35
- end
36
-
37
- output << plist_node(self)
38
-
39
- output << '</plist>' if header
40
-
41
- return output.join
42
- end
43
-
44
- private
45
- def plist_node(element)
46
- output = ''
47
- case element
48
- when Array
49
- output << tag('array') {
50
- element.collect {|e| plist_node(e)}.join
51
- }
52
- when Hash
53
- inner_tags = []
54
-
55
- element.each do |k,v|
56
- inner_tags << tag('key', k.to_s)
57
- inner_tags << plist_node(v)
58
- end
59
-
60
- output << tag('dict') {
61
- inner_tags.join
62
- }
63
- when true, false
64
- output << "<#{element}/>"
65
- when Time
66
- output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'))
67
- when Date # also catches DateTime
68
- output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'))
69
- when String, Symbol, Fixnum, Bignum, Integer, Float
70
- output << tag(element_type(element), element.to_s)
71
- else
72
- output << tag('data', Marshal.dump(element))
73
- end
74
-
75
- return output
76
- end
77
-
78
- def tag(type, contents = '', &block)
79
- contents << block.call if block_given?
80
-
81
- return "<#{type}>#{contents.to_s}</#{type}>"
82
- end
83
-
84
- def element_type(item)
85
- return case item
86
- when Array: 'array'
87
- when String, Symbol: 'string'
88
- when Fixnum, Bignum, Integer: 'integer'
89
- when Float: 'real'
90
- when Array: 'array'
91
- when Hash: 'dict'
92
- else
93
- raise "Don't know about this data type... something must be wrong!"
94
- end
95
- end
96
- end
97
-
98
- class Array
99
- include Plist
100
- end
101
-
102
- class Hash
103
- include Plist
104
- end
@@ -1,54 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- ######################################
4
- # Copyright 2006, Ben Bleything. #
5
- # <ben@bleything.net> #
6
- # #
7
- # Distributed under the MIT license. #
8
- ######################################
9
-
10
- require 'test/unit'
11
- require 'plist'
12
-
13
- class TestBasicTypes < Test::Unit::TestCase
14
- def wrap(tag, content)
15
- return "<array><#{tag}>#{content}</#{tag}></array>"
16
- end
17
-
18
- def test_strings
19
- expected = wrap('string', 'testdata')
20
-
21
- assert_equal expected, ['testdata'].to_plist(false)
22
- assert_equal expected, [:testdata].to_plist(false)
23
- end
24
-
25
- def test_integers
26
- [42, 2376239847623987623, -8192].each do |i|
27
- assert_equal wrap('integer', i), [i].to_plist(false)
28
- end
29
- end
30
-
31
- def test_floats
32
- [3.14159, -38.3897, 2398476293847.9823749872349980].each do |i|
33
- assert_equal wrap('real', i), [i].to_plist(false)
34
- end
35
- end
36
-
37
- def test_booleans
38
- assert_equal '<array><true/></array>', [true].to_plist(false)
39
- assert_equal '<array><false/></array>', [false].to_plist(false)
40
- end
41
-
42
- def test_time
43
- test_time = Time.now
44
- assert_equal wrap('date', test_time.utc.strftime('%Y-%m-%dT%H:%M:%SZ')), [test_time].to_plist(false)
45
- end
46
-
47
- def test_dates
48
- test_date = Date.today
49
- test_datetime = DateTime.now
50
-
51
- assert_equal wrap('date', test_date.strftime('%Y-%m-%dT%H:%M:%SZ')), [test_date].to_plist(false)
52
- assert_equal wrap('date', test_datetime.strftime('%Y-%m-%dT%H:%M:%SZ')), [test_datetime].to_plist(false)
53
- end
54
- end
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- ######################################
4
- # Copyright 2006, Ben Bleything. #
5
- # <ben@bleything.net> #
6
- # #
7
- # Distributed under the MIT license. #
8
- ######################################
9
-
10
- require 'test/unit'
11
- require 'plist'
12
-
13
- class TestCollections < Test::Unit::TestCase
14
- def test_true
15
- assert true
16
- end
17
- end