plist 3.5.0 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5dada9257510eabf5ab04f5bd4dd6098a33ce68142d3c63c6278c643630a7761
4
- data.tar.gz: f45363442af85f0515c7d30d25a3ca8625f366a91110334c4b2db7717ce9fa98
3
+ metadata.gz: 2a76784a6b7e953d1cd9b6deaf390eb7556fd0df83e8970db7ac4640c71493b4
4
+ data.tar.gz: 8324c2b332839aa772eb77a4a760b24e43e24c363c99d01c2b2b4b11e311938a
5
5
  SHA512:
6
- metadata.gz: cfe9ad984a22f43d24802fc159fca5e53d4ac88e45610d5accb0cafcfcecf99a643d290ed4073147e32f1f34b1c84b1ed8745b1d1c30edc8dc3c0fdc7d225efd
7
- data.tar.gz: 91744b0fd7d45ccc1adff2f5279c37a511725896479e4a20261605735067e518f5eb5354605567a796aba2a78724555389b09b162d7218c363759df85d1dcce3
6
+ metadata.gz: c33686b606d723821333314b4a18fe6ac39d65a3712aa1cd43896dbbd4bfb95130aa0a1f62b4a3b260c2a633d44681eec0766fef5651ae70580340a905e4f6e4
7
+ data.tar.gz: 5b63b55469235ec79f14af6f25c05cbc8e6088ce9532e9dfc5aa0887e533a7ee67424953d62e0b3dfe046c7745d9a2add1e0b8ddf07c34f46c5a551c92a9a158
@@ -13,6 +13,6 @@ require 'base64'
13
13
  require 'cgi'
14
14
  require 'stringio'
15
15
 
16
- require 'plist/generator'
17
- require 'plist/parser'
18
- require 'plist/version'
16
+ require_relative 'plist/generator'
17
+ require_relative 'plist/parser'
18
+ require_relative 'plist/version'
@@ -26,13 +26,11 @@ module Plist
26
26
 
27
27
  # Helper method for injecting into classes. Calls <tt>Plist::Emit.dump</tt> with +self+.
28
28
  def to_plist(envelope = true, options = {})
29
- options = { :indent => DEFAULT_INDENT }.merge(options)
30
- return Plist::Emit.dump(self, envelope, options)
29
+ Plist::Emit.dump(self, envelope, options)
31
30
  end
32
31
 
33
32
  # Helper method for injecting into classes. Calls <tt>Plist::Emit.save_plist</tt> with +self+.
34
33
  def save_plist(filename, options = {})
35
- options = { :indent => DEFAULT_INDENT }.merge(options)
36
34
  Plist::Emit.save_plist(self, filename, options)
37
35
  end
38
36
 
@@ -46,177 +44,129 @@ module Plist
46
44
  # 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.
47
45
  def self.dump(obj, envelope = true, options = {})
48
46
  options = { :indent => DEFAULT_INDENT }.merge(options)
49
- output = plist_node(obj, options)
50
47
 
48
+ output = PlistBuilder.new(options[:indent]).build(obj)
51
49
  output = wrap(output) if envelope
52
50
 
53
- return output
51
+ output
54
52
  end
55
53
 
56
54
  # Writes the serialized object's plist to the specified filename.
57
55
  def self.save_plist(obj, filename, options = {})
58
- options = { :indent => DEFAULT_INDENT }.merge(options)
59
56
  File.open(filename, 'wb') do |f|
60
57
  f.write(obj.to_plist(true, options))
61
58
  end
62
59
  end
63
60
 
64
61
  private
65
- def self.plist_node(element, options = {})
66
- options = { :indent => DEFAULT_INDENT }.merge(options)
67
- output = ''
68
-
69
- if element.respond_to? :to_plist_node
70
- output << element.to_plist_node
71
- else
72
- case element
73
- when Array
74
- if element.empty?
75
- output << "<array/>\n"
76
- else
77
- output << tag('array', '', options) {
78
- element.collect {|e| plist_node(e, options)}
79
- }
80
- end
81
- when Hash
82
- if element.empty?
83
- output << "<dict/>\n"
84
- else
85
- inner_tags = []
86
62
 
87
- element.keys.sort_by{|k| k.to_s }.each do |k|
88
- v = element[k]
89
- inner_tags << tag('key', CGI.escapeHTML(k.to_s), options)
90
- inner_tags << plist_node(v, options)
91
- end
63
+ class PlistBuilder
64
+ def initialize(indent_str)
65
+ @indent_str = indent_str.to_s
66
+ end
92
67
 
93
- output << tag('dict', '', options) {
94
- inner_tags
95
- }
96
- end
97
- when true, false
98
- output << "<#{element}/>\n"
99
- when Time
100
- output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'), options)
101
- when Date # also catches DateTime
102
- output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'), options)
103
- when String, Symbol, Integer, Float
104
- output << tag(element_type(element), CGI.escapeHTML(element.to_s), options)
105
- when IO, StringIO
106
- element.rewind
107
- contents = element.read
108
- # note that apple plists are wrapped at a different length then
109
- # what ruby's base64 wraps by default.
110
- # I used #encode64 instead of #b64encode (which allows a length arg)
111
- # because b64encode is b0rked and ignores the length arg.
112
- data = "\n"
113
- Base64.encode64(contents).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" }
114
- output << tag('data', data, options)
68
+ def build(element, level=0)
69
+ if element.respond_to? :to_plist_node
70
+ element.to_plist_node
115
71
  else
116
- output << comment('The <data> element below contains a Ruby object which has been serialized with Marshal.dump.')
117
- data = "\n"
118
- Base64.encode64(Marshal.dump(element)).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" }
119
- output << tag('data', data, options)
72
+ case element
73
+ when Array
74
+ if element.empty?
75
+ tag('array', nil, level)
76
+ else
77
+ tag('array', nil, level) {
78
+ element.collect {|e| build(e, level + 1) }.join
79
+ }
80
+ end
81
+ when Hash
82
+ if element.empty?
83
+ tag('dict', nil, level)
84
+ else
85
+ tag('dict', '', level) do
86
+ element.sort_by{|k,v| k.to_s }.collect do |k,v|
87
+ tag('key', CGI.escapeHTML(k.to_s), level + 1) +
88
+ build(v, level + 1)
89
+ end.join
90
+ end
91
+ end
92
+ when true, false
93
+ tag(element, nil, level)
94
+ when Time
95
+ tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'), level)
96
+ when Date # also catches DateTime
97
+ tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'), level)
98
+ when String, Symbol, Integer, Float
99
+ tag(element_type(element), CGI.escapeHTML(element.to_s), level)
100
+ when IO, StringIO
101
+ data = element.tap(&:rewind).read
102
+ data_tag(data, level)
103
+ else
104
+ data = Marshal.dump(element)
105
+ comment_tag('The <data> element below contains a Ruby object which has been serialized with Marshal.dump.') +
106
+ data_tag(data, level)
107
+ end
120
108
  end
121
109
  end
122
110
 
123
- return output
124
- end
125
-
126
- def self.comment(content)
127
- return "<!-- #{content} -->\n"
128
- end
111
+ private
129
112
 
130
- def self.tag(type, contents = '', options = {}, &block)
131
- options = { :indent => DEFAULT_INDENT }.merge(options)
132
- out = nil
113
+ def tag(type, contents, level, &block)
114
+ if block_given?
115
+ indent("<#{type}>\n", level) +
116
+ block.call +
117
+ indent("</#{type}>\n", level)
118
+ elsif contents.to_s.empty?
119
+ indent("<#{type}/>\n", level)
120
+ else
121
+ indent("<#{type}>#{contents.to_s}</#{type}>\n", level)
122
+ end
123
+ end
133
124
 
134
- if block_given?
135
- out = IndentedString.new(options[:indent])
136
- out << "<#{type}>"
137
- out.raise_indent
125
+ def data_tag(data, level)
126
+ # note that apple plists are wrapped at a different length then
127
+ # what ruby's base64 wraps by default.
128
+ # I used #encode64 instead of #b64encode (which allows a length arg)
129
+ # because b64encode is b0rked and ignores the length arg.
130
+ tag('data', nil, level) do
131
+ Base64.encode64(data)
132
+ .gsub(/\s+/, '')
133
+ .scan(/.{1,68}/o)
134
+ .collect { |line| indent(line, level) }
135
+ .join("\n")
136
+ .concat("\n")
137
+ end
138
+ end
138
139
 
139
- out << block.call
140
+ def indent(str, level)
141
+ @indent_str.to_s * level + str
142
+ end
140
143
 
141
- out.lower_indent
142
- out << "</#{type}>"
143
- else
144
- out = "<#{type}>#{contents.to_s}</#{type}>\n"
144
+ def element_type(item)
145
+ case item
146
+ when String, Symbol
147
+ 'string'
148
+ when Integer
149
+ 'integer'
150
+ when Float
151
+ 'real'
152
+ else
153
+ raise "Don't know about this data type... something must be wrong!"
154
+ end
145
155
  end
146
156
 
147
- return out.to_s
157
+ def comment_tag(content)
158
+ return "<!-- #{content} -->\n"
159
+ end
148
160
  end
149
161
 
150
162
  def self.wrap(contents)
151
- output = ''
152
-
153
- output << '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
163
+ output = '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
154
164
  output << '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' + "\n"
155
165
  output << '<plist version="1.0">' + "\n"
156
-
157
166
  output << contents
158
-
159
167
  output << '</plist>' + "\n"
160
168
 
161
- return output
162
- end
163
-
164
- def self.element_type(item)
165
- case item
166
- when String, Symbol
167
- 'string'
168
-
169
- when Integer
170
- 'integer'
171
-
172
- when Float
173
- 'real'
174
-
175
- else
176
- raise "Don't know about this data type... something must be wrong!"
177
- end
178
- end
179
- private
180
- class IndentedString #:nodoc:
181
- attr_accessor :indent_string
182
-
183
- def initialize(str = "\t")
184
- @indent_string = str
185
- @contents = ''
186
- @indent_level = 0
187
- end
188
-
189
- def to_s
190
- return @contents
191
- end
192
-
193
- def raise_indent
194
- @indent_level += 1
195
- end
196
-
197
- def lower_indent
198
- @indent_level -= 1 if @indent_level > 0
199
- end
200
-
201
- def <<(val)
202
- if val.is_a? Array
203
- val.each do |f|
204
- self << f
205
- end
206
- else
207
- # if it's already indented, don't bother indenting further
208
- unless val =~ /\A#{@indent_string}/
209
- indent = @indent_string * @indent_level
210
-
211
- @contents << val.gsub(/^/, indent)
212
- else
213
- @contents << val
214
- end
215
-
216
- # it already has a newline, don't add another
217
- @contents << "\n" unless val =~ /\n$/
218
- end
219
- end
169
+ output
220
170
  end
221
171
  end
222
172
  end
@@ -13,6 +13,9 @@
13
13
  #
14
14
  # r = Plist.parse_xml(filename_or_xml)
15
15
  module Plist
16
+ # Raised when an element is not implemented
17
+ class UnimplementedElementError < RuntimeError; end
18
+
16
19
  # Note that I don't use these two elements much:
17
20
  #
18
21
  # + Date elements are returned as DateTime objects.
@@ -46,7 +49,10 @@ module Plist
46
49
  end
47
50
 
48
51
  def text(contents)
49
- @open.last.text = contents if @open.last
52
+ if @open.last
53
+ @open.last.text ||= ''
54
+ @open.last.text.concat(contents)
55
+ end
50
56
  end
51
57
 
52
58
  def tag_end(name)
@@ -72,11 +78,14 @@ module Plist
72
78
  @listener = listener
73
79
  end
74
80
 
75
- TEXT = /([^<]+)/
81
+ TEXT = /([^<]+)/
82
+ CDATA = /<!\[CDATA\[(.*?)\]\]>/
76
83
  XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/m
77
84
  DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/m
78
85
  COMMENT_START = /\A<!--/
79
86
  COMMENT_END = /.*?-->/m
87
+ UNIMPLEMENTED_ERROR = 'Unimplemented element. ' \
88
+ 'Consider reporting via https://github.com/patsplat/plist/issues'
80
89
 
81
90
  def parse
82
91
  plist_tags = PTag.mappings.keys.join('|')
@@ -105,10 +114,12 @@ module Plist
105
114
  end
106
115
  elsif @scanner.scan(TEXT)
107
116
  @listener.text(@scanner[1])
117
+ elsif @scanner.scan(CDATA)
118
+ @listener.text(@scanner[1])
108
119
  elsif @scanner.scan(end_tag)
109
120
  @listener.tag_end(@scanner[1])
110
121
  else
111
- raise "Unimplemented element"
122
+ raise UnimplementedElementError.new(UNIMPLEMENTED_ERROR)
112
123
  end
113
124
  end
114
125
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Plist
4
- VERSION = '3.5.0'.freeze
4
+ VERSION = '3.6.0'.freeze
5
5
  end
metadata CHANGED
@@ -1,28 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plist
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.0
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Bleything
8
8
  - Patrick May
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-12-21 00:00:00.000000000 Z
12
+ date: 2020-12-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
20
  version: '1.14'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - "~>"
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '1.14'
28
28
  - !ruby/object:Gem::Dependency
@@ -56,7 +56,7 @@ dependencies:
56
56
  description: Plist is a library to manipulate Property List files, also known as plists.
57
57
  It can parse plist files into native Ruby data structures as well as generating
58
58
  new plist files from your Ruby objects.
59
- email:
59
+ email:
60
60
  executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files: []
@@ -70,7 +70,7 @@ homepage: https://github.com/patsplat/plist
70
70
  licenses:
71
71
  - MIT
72
72
  metadata: {}
73
- post_install_message:
73
+ post_install_message:
74
74
  rdoc_options: []
75
75
  require_paths:
76
76
  - lib
@@ -78,16 +78,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '0'
81
+ version: 1.9.3
82
82
  required_rubygems_version: !ruby/object:Gem::Requirement
83
83
  requirements:
84
84
  - - ">="
85
85
  - !ruby/object:Gem::Version
86
86
  version: '0'
87
87
  requirements: []
88
- rubyforge_project:
89
- rubygems_version: 2.7.6
90
- signing_key:
88
+ rubygems_version: 3.2.3
89
+ signing_key:
91
90
  specification_version: 4
92
91
  summary: All-purpose Property List manipulation library
93
92
  test_files: []