nanaimo 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +1 -1
- data/lib/nanaimo.rb +0 -1
- data/lib/nanaimo/reader.rb +36 -10
- data/lib/nanaimo/version.rb +1 -1
- data/lib/nanaimo/writer.rb +35 -5
- data/lib/nanaimo/writer/pbxproj.rb +78 -0
- data/lib/nanaimo/writer/xml.rb +4 -0
- metadata +3 -3
- data/lib/nanaimo/xcode_project_writer.rb +0 -76
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14c576818f92d08020d63bfcc8d90840f37ec30d
|
4
|
+
data.tar.gz: bca04f1ce7e922722502091d131a6000285352cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e45beaaddb84a3ec4b076f95429fd2ae3040d48ea1b2d066e31990af2121cc0575faeddb1972d124c8a66bc08444547eb0f36dc87da53f32165d41fe34e60877
|
7
|
+
data.tar.gz: 1600ba9301952b7aac138af098c7caba4157609e8715e8189d167827682f6546f39e7d2ee105f87b0aaf7e4dd9c4f748d4e0abbb2a56e7f2f94d37c582298912
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Nanaimo Changelog
|
2
2
|
|
3
|
+
## 0.2.0 (2016-11-02)
|
4
|
+
|
5
|
+
##### Enhancements
|
6
|
+
|
7
|
+
* Add context to parse errors.
|
8
|
+
[Samuel Giddins](https://github.com/segiddins)
|
9
|
+
[#5](https://github.com/CocoaPods/Nanaimo/issues/5)
|
10
|
+
|
11
|
+
* Allow disabling 'strict' mode when writing plists, allowing unknown object
|
12
|
+
types to be serialized as their string representations.
|
13
|
+
[Samuel Giddins](https://github.com/segiddins)
|
14
|
+
|
15
|
+
##### Bug Fixes
|
16
|
+
|
17
|
+
* None.
|
18
|
+
|
19
|
+
|
3
20
|
## 0.1.4 (2016-11-01)
|
4
21
|
|
5
22
|
##### Enhancements
|
data/Gemfile.lock
CHANGED
data/lib/nanaimo.rb
CHANGED
data/lib/nanaimo/reader.rb
CHANGED
@@ -32,6 +32,32 @@ module Nanaimo
|
|
32
32
|
# @return [String] The contents of the plist.
|
33
33
|
#
|
34
34
|
attr_accessor :plist_string
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
"[!] #{super}#{context}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def context(n = 2)
|
41
|
+
line_number, column = location
|
42
|
+
line_number -= 1
|
43
|
+
lines = plist_string.split(NEWLINE)
|
44
|
+
|
45
|
+
s = line_number.succ.to_s.size
|
46
|
+
indent = "#{' ' * s}# "
|
47
|
+
indicator = "#{line_number.succ}> "
|
48
|
+
|
49
|
+
m = ::String.new("\n")
|
50
|
+
m << "#{indent}-------------------------------------------\n"
|
51
|
+
m << lines[[line_number - n, 0].max...line_number].map do |l|
|
52
|
+
"#{indent}#{l}\n"
|
53
|
+
end.join
|
54
|
+
m << "#{indicator}#{lines[line_number]}\n"
|
55
|
+
m << ' ' * (column + s + 2) << "^\n"
|
56
|
+
m << Array(lines[line_number.succ..[lines.count.pred, line_number + n].min]).map do |l|
|
57
|
+
l.strip.empty? ? '' : "#{indent}#{l}\n"
|
58
|
+
end.join
|
59
|
+
m << "#{indent}-------------------------------------------\n"
|
60
|
+
end
|
35
61
|
end
|
36
62
|
|
37
63
|
# @param plist_contents [String]
|
@@ -73,7 +99,7 @@ module Nanaimo
|
|
73
99
|
root_object = parse_object
|
74
100
|
|
75
101
|
eat_whitespace!
|
76
|
-
raise_parser_error ParseError,
|
102
|
+
raise_parser_error ParseError, 'Found additional characters after parsing the root plist object' unless @scanner.eos?
|
77
103
|
|
78
104
|
Nanaimo::Plist.new(root_object, plist_format)
|
79
105
|
end
|
@@ -93,7 +119,7 @@ module Nanaimo
|
|
93
119
|
def parse_object
|
94
120
|
_comment = skip_to_non_space_matching_annotations
|
95
121
|
start_pos = @scanner.pos
|
96
|
-
raise_parser_error ParseError, 'Unexpected
|
122
|
+
raise_parser_error ParseError, 'Unexpected end of string while parsing' if @scanner.eos?
|
97
123
|
if @scanner.skip(/\{/)
|
98
124
|
parse_dictionary
|
99
125
|
elsif @scanner.skip(/\(/)
|
@@ -112,15 +138,15 @@ module Nanaimo
|
|
112
138
|
|
113
139
|
def parse_string
|
114
140
|
eat_whitespace!
|
115
|
-
unless match = @scanner.scan(%r{[\w
|
116
|
-
raise_parser_error ParseError, "
|
141
|
+
unless match = @scanner.scan(%r{[\w/.$]+})
|
142
|
+
raise_parser_error ParseError, "Invalid character #{current_character.inspect} in unquoted string"
|
117
143
|
end
|
118
144
|
Nanaimo::String.new(match, nil)
|
119
145
|
end
|
120
146
|
|
121
147
|
def parse_quotedstring(quote)
|
122
148
|
unless string = @scanner.scan(/(?:([^#{quote}\\]|\\.)*)#{quote}/)
|
123
|
-
raise_parser_error ParseError, "
|
149
|
+
raise_parser_error ParseError, "Unterminated quoted string, expected #{quote} but never found it"
|
124
150
|
end
|
125
151
|
string = Unicode.unquotify_string(string.chomp!(quote))
|
126
152
|
Nanaimo::QuotedString.new(string, nil)
|
@@ -137,7 +163,7 @@ module Nanaimo
|
|
137
163
|
eat_whitespace!
|
138
164
|
break if @scanner.skip(/\)/)
|
139
165
|
unless @scanner.skip(/,/)
|
140
|
-
raise_parser_error ParseError, "Array
|
166
|
+
raise_parser_error ParseError, "Array missing ',' in between objects"
|
141
167
|
end
|
142
168
|
end
|
143
169
|
|
@@ -153,7 +179,7 @@ module Nanaimo
|
|
153
179
|
key = parse_object
|
154
180
|
eat_whitespace!
|
155
181
|
unless @scanner.skip(/=/)
|
156
|
-
raise_parser_error ParseError, "Dictionary missing value
|
182
|
+
raise_parser_error ParseError, "Dictionary missing value for key #{key.as_ruby.inspect}, expected '=' and found #{current_character.inspect}"
|
157
183
|
end
|
158
184
|
|
159
185
|
value = parse_object
|
@@ -162,7 +188,7 @@ module Nanaimo
|
|
162
188
|
eat_whitespace!
|
163
189
|
break if @scanner.skip(/}/)
|
164
190
|
unless @scanner.skip(/;/)
|
165
|
-
raise_parser_error ParseError, "Dictionary
|
191
|
+
raise_parser_error ParseError, "Dictionary missing ';' after key-value pair for #{key.as_ruby.inspect}, found #{current_character.inspect}"
|
166
192
|
end
|
167
193
|
end
|
168
194
|
|
@@ -189,7 +215,7 @@ module Nanaimo
|
|
189
215
|
|
190
216
|
def read_singleline_comment
|
191
217
|
unless comment = @scanner.scan_until(NEWLINE)
|
192
|
-
raise_parser_error ParseError,
|
218
|
+
raise_parser_error ParseError, 'Failed to terminate single line comment'
|
193
219
|
end
|
194
220
|
comment
|
195
221
|
end
|
@@ -208,7 +234,7 @@ module Nanaimo
|
|
208
234
|
|
209
235
|
def read_multiline_comment
|
210
236
|
unless annotation = @scanner.scan(%r{(?:.+?)(?=\*/)}m)
|
211
|
-
raise_parser_error ParseError,
|
237
|
+
raise_parser_error ParseError, 'Failed to terminate multiline comment'
|
212
238
|
end
|
213
239
|
@scanner.skip(%r{\*/})
|
214
240
|
|
data/lib/nanaimo/version.rb
CHANGED
data/lib/nanaimo/writer.rb
CHANGED
@@ -3,8 +3,24 @@ module Nanaimo
|
|
3
3
|
# string representation.
|
4
4
|
#
|
5
5
|
class Writer
|
6
|
+
autoload :PBXProjWriter, 'nanaimo/writer/pbxproj'
|
6
7
|
autoload :XMLWriter, 'nanaimo/writer/xml'
|
7
8
|
|
9
|
+
# Raised when attempting to write a plist containing an object of an
|
10
|
+
# unsupported type.
|
11
|
+
#
|
12
|
+
class UnsupportedPlistTypeError < Error
|
13
|
+
def initialize(plist_format, object)
|
14
|
+
@plist_format = plist_format
|
15
|
+
@object = object
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"Unable to write #{@object.inspect}. " \
|
20
|
+
"`#{@object.class}` is an invalid object type to serialize in a #{@plist_format} plist."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
8
24
|
# The magic comment that denotes a UTF8-encoded plist.
|
9
25
|
#
|
10
26
|
UTF8 = "// !$*UTF8*$!\n".freeze
|
@@ -14,12 +30,13 @@ module Nanaimo
|
|
14
30
|
# spaces and newlines to make output more legible
|
15
31
|
# @param output [#<<] The output stream to write the plist to
|
16
32
|
#
|
17
|
-
def initialize(plist, pretty
|
33
|
+
def initialize(plist, pretty: true, output: ::String.new, strict: true)
|
18
34
|
@plist = plist
|
19
35
|
@pretty = pretty
|
20
36
|
@output = output
|
21
37
|
@indent = 0
|
22
38
|
@newlines = true
|
39
|
+
@strict = strict
|
23
40
|
end
|
24
41
|
|
25
42
|
# Writes the plist to the given output.
|
@@ -30,11 +47,15 @@ module Nanaimo
|
|
30
47
|
write_newline
|
31
48
|
end
|
32
49
|
|
33
|
-
attr_reader :indent, :pretty, :output, :newlines
|
34
|
-
private :indent, :pretty, :output, :newlines
|
50
|
+
attr_reader :indent, :pretty, :output, :newlines, :strict
|
51
|
+
private :indent, :pretty, :output, :newlines, :strict
|
35
52
|
|
36
53
|
private
|
37
54
|
|
55
|
+
def plist_format
|
56
|
+
:ascii
|
57
|
+
end
|
58
|
+
|
38
59
|
def write_utf8
|
39
60
|
output << UTF8
|
40
61
|
end
|
@@ -53,19 +74,28 @@ module Nanaimo
|
|
53
74
|
write_array(object)
|
54
75
|
when Dictionary, ::Hash
|
55
76
|
write_dictionary(object)
|
56
|
-
when
|
77
|
+
when QUOTED_STRING_REGEXP, QuotedString, ''
|
57
78
|
write_quoted_string(object)
|
58
79
|
when String, ::String, Symbol
|
59
80
|
write_string(object)
|
60
81
|
when Data
|
61
82
|
write_data(object)
|
62
83
|
else
|
63
|
-
raise
|
84
|
+
raise UnsupportedPlistTypeError.new(plist_format, object) if strict
|
85
|
+
write_string_quoted_if_necessary(object)
|
64
86
|
end
|
65
87
|
write_annotation(object) if pretty
|
66
88
|
output
|
67
89
|
end
|
68
90
|
|
91
|
+
QUOTED_STRING_REGEXP = %r{[^\w\./]}
|
92
|
+
private_constant :QUOTED_STRING_REGEXP
|
93
|
+
|
94
|
+
def write_string_quoted_if_necessary(object)
|
95
|
+
string = object.to_s
|
96
|
+
string =~ QUOTED_STRING_REGEXP ? write_quoted_string(string) : write_string(string)
|
97
|
+
end
|
98
|
+
|
69
99
|
def write_string(object)
|
70
100
|
output << value_for(object).to_s
|
71
101
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Nanaimo
|
2
|
+
class Writer
|
3
|
+
# Transforms native ruby objects or Plist objects into their ASCII Plist
|
4
|
+
# string representation, formatted as Xcode writes Xcode projects.
|
5
|
+
#
|
6
|
+
class PBXProjWriter < Writer
|
7
|
+
ISA = String.new('isa', '')
|
8
|
+
private_constant :ISA
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@objects_section = false
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def write_dictionary(object)
|
18
|
+
n = newlines
|
19
|
+
@newlines = false if flat_dictionary?(object)
|
20
|
+
return super(sort_dictionary(object)) unless @objects_section
|
21
|
+
@objects_section = false
|
22
|
+
write_dictionary_start
|
23
|
+
value = value_for(object)
|
24
|
+
objects_by_isa = value.group_by { |_k, v| isa_for(v) }
|
25
|
+
objects_by_isa.each do |isa, kvs|
|
26
|
+
write_newline
|
27
|
+
output << "/* Begin #{isa} section */"
|
28
|
+
write_newline
|
29
|
+
sort_dictionary(kvs).each do |k, v|
|
30
|
+
write_dictionary_key_value_pair(k, v)
|
31
|
+
end
|
32
|
+
output << "/* End #{isa} section */"
|
33
|
+
write_newline
|
34
|
+
end
|
35
|
+
write_dictionary_end
|
36
|
+
ensure
|
37
|
+
@newlines = n
|
38
|
+
end
|
39
|
+
|
40
|
+
def write_dictionary_key_value_pair(k, v)
|
41
|
+
@objects_section = true if value_for(k) == 'objects'
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def sort_dictionary(dictionary)
|
46
|
+
hash = value_for(dictionary)
|
47
|
+
hash.to_a.sort do |(k1, v1), (k2, v2)|
|
48
|
+
v2_isa = isa_for(v2)
|
49
|
+
v1_isa = v2_isa && isa_for(v1)
|
50
|
+
comp = v1_isa <=> v2_isa
|
51
|
+
next comp if !comp.zero? && v1_isa
|
52
|
+
|
53
|
+
key1 = value_for(k1)
|
54
|
+
key2 = value_for(k2)
|
55
|
+
next -1 if key1 == 'isa'
|
56
|
+
next 1 if key2 == 'isa'
|
57
|
+
key1 <=> key2
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def isa_for(dictionary)
|
62
|
+
dictionary = value_for(dictionary)
|
63
|
+
return unless dictionary.is_a?(Hash)
|
64
|
+
isa = dictionary.values_at('isa', ISA).map(&method(:value_for)).compact.first
|
65
|
+
isa && value_for(isa)
|
66
|
+
end
|
67
|
+
|
68
|
+
def flat_dictionary?(dictionary)
|
69
|
+
case isa_for(dictionary)
|
70
|
+
when 'PBXBuildFile', 'PBXFileReference'
|
71
|
+
true
|
72
|
+
else
|
73
|
+
false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/nanaimo/writer/xml.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nanaimo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danielle Tomlinson
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-11-
|
12
|
+
date: 2016-11-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -84,8 +84,8 @@ files:
|
|
84
84
|
- lib/nanaimo/unicode/quote_maps.rb
|
85
85
|
- lib/nanaimo/version.rb
|
86
86
|
- lib/nanaimo/writer.rb
|
87
|
+
- lib/nanaimo/writer/pbxproj.rb
|
87
88
|
- lib/nanaimo/writer/xml.rb
|
88
|
-
- lib/nanaimo/xcode_project_writer.rb
|
89
89
|
- nanaimo.gemspec
|
90
90
|
homepage: https://github.com/CocoaPods/Nanaimo
|
91
91
|
licenses:
|
@@ -1,76 +0,0 @@
|
|
1
|
-
module Nanaimo
|
2
|
-
# Transforms native ruby objects or Plist objects into their ASCII Plist
|
3
|
-
# string representation, formatted as Xcode writes Xcode projects.
|
4
|
-
#
|
5
|
-
class XcodeProjectWriter < Writer
|
6
|
-
ISA = String.new('isa', '')
|
7
|
-
private_constant :ISA
|
8
|
-
|
9
|
-
def initialize(*)
|
10
|
-
super
|
11
|
-
@objects_section = false
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def write_dictionary(object)
|
17
|
-
n = newlines
|
18
|
-
@newlines = false if flat_dictionary?(object)
|
19
|
-
return super(sort_dictionary(object)) unless @objects_section
|
20
|
-
@objects_section = false
|
21
|
-
write_dictionary_start
|
22
|
-
value = value_for(object)
|
23
|
-
objects_by_isa = value.group_by { |_k, v| isa_for(v) }
|
24
|
-
objects_by_isa.each do |isa, kvs|
|
25
|
-
write_newline
|
26
|
-
output << "/* Begin #{isa} section */"
|
27
|
-
write_newline
|
28
|
-
sort_dictionary(kvs).each do |k, v|
|
29
|
-
write_dictionary_key_value_pair(k, v)
|
30
|
-
end
|
31
|
-
output << "/* End #{isa} section */"
|
32
|
-
write_newline
|
33
|
-
end
|
34
|
-
write_dictionary_end
|
35
|
-
ensure
|
36
|
-
@newlines = n
|
37
|
-
end
|
38
|
-
|
39
|
-
def write_dictionary_key_value_pair(k, v)
|
40
|
-
@objects_section = true if value_for(k) == 'objects'
|
41
|
-
super
|
42
|
-
end
|
43
|
-
|
44
|
-
def sort_dictionary(dictionary)
|
45
|
-
hash = value_for(dictionary)
|
46
|
-
hash.to_a.sort do |(k1, v1), (k2, v2)|
|
47
|
-
v2_isa = isa_for(v2)
|
48
|
-
v1_isa = v2_isa && isa_for(v1)
|
49
|
-
comp = v1_isa <=> v2_isa
|
50
|
-
next comp if !comp.zero? && v1_isa
|
51
|
-
|
52
|
-
key1 = value_for(k1)
|
53
|
-
key2 = value_for(k2)
|
54
|
-
next -1 if key1 == 'isa'
|
55
|
-
next 1 if key2 == 'isa'
|
56
|
-
key1 <=> key2
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def isa_for(dictionary)
|
61
|
-
dictionary = value_for(dictionary)
|
62
|
-
return unless dictionary.is_a?(Hash)
|
63
|
-
isa = dictionary.values_at('isa', ISA).map(&method(:value_for)).compact.first
|
64
|
-
isa && value_for(isa)
|
65
|
-
end
|
66
|
-
|
67
|
-
def flat_dictionary?(dictionary)
|
68
|
-
case isa_for(dictionary)
|
69
|
-
when 'PBXBuildFile', 'PBXFileReference'
|
70
|
-
true
|
71
|
-
else
|
72
|
-
false
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|