plist 0.0.1 → 1.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.
@@ -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