metaheader 1.3beta3 → 2.0.1
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.
- checksums.yaml +5 -5
- data/Gemfile +0 -2
- data/README.md +9 -12
- data/lib/metaheader.rb +80 -83
- data/lib/metaheader/version.rb +1 -1
- data/metaheader.gemspec +5 -6
- data/test/helper.rb +0 -4
- data/test/test_multiline.rb +193 -0
- data/test/test_parser.rb +74 -233
- data/test/test_validator.rb +38 -42
- metadata +20 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 551a95591a97a5578a68b27fd13ec3f7e20a23925884b222bdf1d276fdb58910
|
4
|
+
data.tar.gz: f1d46558535100ff763f79f8d1a67fbb8965fd0a305f0cc249815fbe0a3fab78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 134d937cd06dfa13fd548cee84fa69b7e2368a7e603228cfcc56e54a96b2e321173a026d3df1abfcda35e223aa464f426d00fe2b4cbdd55c6478d5073e0f8f9c
|
7
|
+
data.tar.gz: c28c1672dc9feb00d073f199ff53373852915cd75b78603437a67c2c10bbdca290638d5621aca0dbdcaf0d0a281ac97980381bb472249d8afb31b447f1da4e18
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/metaheader)
|
4
4
|
[](https://travis-ci.org/cfillion/metaheader)
|
5
|
-
[](https://coveralls.io/github/cfillion/metaheader?branch=master)
|
6
5
|
|
7
6
|
## Syntax
|
8
7
|
|
@@ -20,7 +19,8 @@
|
|
20
19
|
@key false
|
21
20
|
```
|
22
21
|
|
23
|
-
Any kind of comment syntax or prefix can be used
|
22
|
+
Any kind of comment syntax or prefix can be used (every line of a multiline
|
23
|
+
value must begin with the same decoration characters):
|
24
24
|
|
25
25
|
```cpp
|
26
26
|
/*
|
@@ -28,13 +28,14 @@ Any kind of comment syntax or prefix can be used:
|
|
28
28
|
*/
|
29
29
|
```
|
30
30
|
|
31
|
-
An alternative syntax is also supported:
|
31
|
+
An alternative syntax that allows spaces in the key name is also supported:
|
32
32
|
|
33
33
|
```
|
34
|
-
Key
|
34
|
+
Key: Value
|
35
35
|
```
|
36
36
|
|
37
|
-
Parsing stops at the first empty line
|
37
|
+
Parsing stops at the first empty line outside of a multiline value
|
38
|
+
(ignoring white space).
|
38
39
|
|
39
40
|
## Usage
|
40
41
|
|
@@ -42,14 +43,11 @@ Parsing stops at the first empty line (ignoring white space).
|
|
42
43
|
require 'metaheader'
|
43
44
|
|
44
45
|
input = '@key value'
|
45
|
-
mh = MetaHeader.
|
46
|
+
mh = MetaHeader.parse input
|
46
47
|
|
47
48
|
# alternatively:
|
48
49
|
# mh = MetaHeader.from_file path
|
49
50
|
|
50
|
-
# mark unknown keys as invalid
|
51
|
-
# mh.strict = true
|
52
|
-
|
53
51
|
# set @key as mandatory
|
54
52
|
errors = mh.validate key: MetaHeader::REQUIRED
|
55
53
|
|
@@ -66,13 +64,12 @@ value = mh[:key]
|
|
66
64
|
|
67
65
|
## Documentation
|
68
66
|
|
69
|
-
MetaHeader's documentation is hosted at
|
70
|
-
[http://rubydoc.info/gems/metaheader/MetaHeader](http://rubydoc.info/gems/metaheader/MetaHeader).
|
67
|
+
MetaHeader's documentation is hosted at <https://rubydoc.info/gems/metaheader/MetaHeader>.
|
71
68
|
|
72
69
|
## Contributing
|
73
70
|
|
74
71
|
1. [Fork this repository](https://github.com/cfillion/metaheader/fork)
|
75
|
-
2. Create your feature branch (`git
|
72
|
+
2. Create your feature branch (`git switch -c my-new-feature`)
|
76
73
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
77
74
|
4. Push to the branch (`git push -u origin my-new-feature`)
|
78
75
|
5. Create a new Pull Request
|
data/lib/metaheader.rb
CHANGED
@@ -1,84 +1,69 @@
|
|
1
1
|
require 'metaheader/version'
|
2
2
|
|
3
|
-
|
4
|
-
# @abstract Subclass and override {#parse} to implement a custom parser.
|
5
|
-
class Parser
|
6
|
-
class << self
|
7
|
-
# @api private
|
8
|
-
def inherited(k)
|
9
|
-
@parsers ||= []
|
10
|
-
@parsers << k
|
11
|
-
end
|
12
|
-
|
13
|
-
# @api private
|
14
|
-
def each(&b)
|
15
|
-
@parsers&.each(&b)
|
16
|
-
end
|
17
|
-
end
|
3
|
+
require 'stringio'
|
18
4
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
5
|
+
# Parser for metadata header in plain-text files
|
6
|
+
# @example
|
7
|
+
# mh = MetaHeader.parse '@hello world'
|
8
|
+
# puts mh[:hello]
|
9
|
+
class MetaHeader
|
10
|
+
# Allow a tag to be omitted when validating in strict mode.
|
11
|
+
OPTIONAL = Object.new.freeze
|
23
12
|
|
24
|
-
|
25
|
-
|
26
|
-
def parse(raw_input)
|
27
|
-
raise NotImplementedError
|
28
|
-
end
|
29
|
-
end
|
13
|
+
# Ensure a tag exists during validation.
|
14
|
+
REQUIRED = Object.new.freeze
|
30
15
|
|
16
|
+
# The tag cannot hold a value beside true or false during validation.
|
31
17
|
BOOLEAN = Object.new.freeze
|
32
|
-
|
33
|
-
|
34
|
-
SINGLELINE = Object.new.freeze
|
18
|
+
|
19
|
+
# The tag must have a string value (non-boolean tag) during validation.
|
35
20
|
VALUE = Object.new.freeze
|
36
21
|
|
37
|
-
#
|
38
|
-
|
39
|
-
# @return [Boolean]
|
40
|
-
attr_accessor :strict
|
22
|
+
# Don't allow multiline values during validation.
|
23
|
+
SINGLELINE = Object.new.freeze
|
41
24
|
|
42
25
|
# Create a new instance from the contents of a file.
|
43
26
|
# @param path [String] path to the file to be read
|
44
27
|
# @return [MetaHeader]
|
45
28
|
def self.from_file(path)
|
46
|
-
File.open(path) {|file| self.
|
29
|
+
File.open(path) {|file| self.parse file }
|
47
30
|
end
|
48
31
|
|
49
|
-
# Construct a
|
50
|
-
#
|
32
|
+
# Construct a MetaHeader object and parse every tags found in the input up to
|
33
|
+
# the first newline.
|
34
|
+
# @param input [String, IO, StringIO]
|
51
35
|
# @return [MetaHeader]
|
52
36
|
def self.parse(input)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
self.new input
|
57
|
-
end
|
37
|
+
mh = MetaHeader.new
|
38
|
+
mh.parse input
|
39
|
+
mh
|
58
40
|
end
|
59
41
|
|
60
|
-
#
|
61
|
-
|
62
|
-
def initialize(input)
|
63
|
-
@strict = false
|
42
|
+
# Construct a blank MetaHeader object
|
43
|
+
def initialize
|
64
44
|
@data = {}
|
45
|
+
end
|
65
46
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
47
|
+
# Parse every tags found in the input up to the first newline.
|
48
|
+
# @param input [String, IO, StringIO]
|
49
|
+
# @return [Integer] Character position of the first content line in the input
|
50
|
+
# data following the header.
|
51
|
+
def parse(input)
|
52
|
+
if input.is_a? String
|
70
53
|
input = StringIO.new input.encode universal_newline: true
|
71
54
|
end
|
72
55
|
|
73
|
-
|
74
|
-
|
75
|
-
Parser.each {|klass|
|
76
|
-
input.rewind
|
56
|
+
@last_tag = nil
|
57
|
+
@empty_lines = 0
|
77
58
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
59
|
+
content_offset = 0
|
60
|
+
input.each_line do |line|
|
61
|
+
full_line_size = line.size # parse_line can trim the line
|
62
|
+
continue = parse_line line
|
63
|
+
content_offset += full_line_size if continue || line.empty?
|
64
|
+
break unless continue
|
65
|
+
end
|
66
|
+
content_offset
|
82
67
|
end
|
83
68
|
|
84
69
|
# Returns the value of a tag by its name, or nil if not found.
|
@@ -95,7 +80,7 @@ class MetaHeader
|
|
95
80
|
|
96
81
|
# Replaces the value of a tag.
|
97
82
|
# @param value the new value
|
98
|
-
# @return value
|
83
|
+
# @return [Object] value
|
99
84
|
def []=(key, value)
|
100
85
|
raise ArgumentError, 'value cannot be nil' if value.nil?
|
101
86
|
|
@@ -104,7 +89,7 @@ class MetaHeader
|
|
104
89
|
end
|
105
90
|
|
106
91
|
# Returns how many tags were found in the input.
|
107
|
-
# @return [
|
92
|
+
# @return [Integer]
|
108
93
|
def size
|
109
94
|
@data.size
|
110
95
|
end
|
@@ -129,7 +114,7 @@ class MetaHeader
|
|
129
114
|
end
|
130
115
|
|
131
116
|
# Make a hash from the parsed data
|
132
|
-
# @return [Hash]
|
117
|
+
# @return [Hash<Symbol, Object>]
|
133
118
|
def to_h
|
134
119
|
Hash[@data.map {|name, tag| [name, tag.value] }]
|
135
120
|
end
|
@@ -141,22 +126,25 @@ class MetaHeader
|
|
141
126
|
end
|
142
127
|
|
143
128
|
# Validates parsed data according to a custom set of rules.
|
129
|
+
# A rule can be one of the predefined constants, a regex, a proc or a
|
130
|
+
# method (returing nil if the tag is valid or an error string otherwise).
|
144
131
|
# @example
|
145
132
|
# mh = MetaHeader.new "@hello world\n@chunky bacon"
|
146
133
|
# mh.validate \
|
147
134
|
# hello: [MetaHeader::REQUIRED, MetaHeader::SINGLELINE, /\d/],
|
148
135
|
# chunky: proc {|value| 'not bacon' unless value == 'bacon' }
|
149
|
-
# @param rules [Hash] tag_name => rule or
|
150
|
-
# @
|
151
|
-
# @
|
136
|
+
# @param rules [Hash] :tag_name => rule or [rule1, rule2, ...]
|
137
|
+
# @param strict [Boolean] Whether to report unknown tags as errors
|
138
|
+
# @return [Array<String>] List of error messasges
|
152
139
|
# @see OPTIONAL
|
153
140
|
# @see REQUIRED
|
154
|
-
# @see
|
141
|
+
# @see BOOLEAN
|
155
142
|
# @see VALUE
|
156
|
-
|
157
|
-
|
143
|
+
# @see SINGLELINE
|
144
|
+
def validate(rules, strict = false)
|
145
|
+
errors = []
|
158
146
|
|
159
|
-
if
|
147
|
+
if strict
|
160
148
|
@data.each {|key, tag|
|
161
149
|
errors << "unknown tag '%s'" % tag.name unless rules.has_key? key
|
162
150
|
}
|
@@ -168,27 +156,22 @@ class MetaHeader
|
|
168
156
|
end
|
169
157
|
}
|
170
158
|
|
171
|
-
errors
|
159
|
+
errors
|
172
160
|
end
|
173
161
|
|
174
162
|
# Rename one or more tags.
|
175
|
-
# @param old [Symbol, Hash]
|
176
|
-
# @param new [Symbol]
|
163
|
+
# @param old [Symbol, Array<Symbol>, Hash<Symbol, Symbol>]
|
164
|
+
# @param new [Symbol] Ignored if old is a Hash
|
177
165
|
# @example
|
178
166
|
# mh.alias :old, :new
|
179
|
-
# mh.alias :old1, :old2, :new
|
180
167
|
# mh.alias [:old1, :old2], :new
|
181
168
|
# mh.alias old1: :new1, old2: :new2
|
182
|
-
def alias(
|
183
|
-
|
184
|
-
|
185
|
-
tags, new = args
|
186
|
-
|
187
|
-
if args.size == 1
|
188
|
-
tags.each {|k, v| self.alias k, v }
|
169
|
+
def alias(old, new = nil)
|
170
|
+
if old.is_a? Hash
|
171
|
+
old.each {|k, v| self.alias k, v }
|
189
172
|
else
|
190
|
-
Array(
|
191
|
-
@data[new] = delete
|
173
|
+
Array(old).each {|tag|
|
174
|
+
@data[new] = delete tag if has? tag
|
192
175
|
}
|
193
176
|
end
|
194
177
|
end
|
@@ -202,8 +185,9 @@ private
|
|
202
185
|
(?:\s*(?<value>[^\n]+))?
|
203
186
|
\Z/x.freeze
|
204
187
|
|
205
|
-
def
|
188
|
+
def parse_line(line)
|
206
189
|
line.chomp!
|
190
|
+
line.encode! Encoding::UTF_8, invalid: :replace
|
207
191
|
|
208
192
|
# multiline value must have the same line prefix as the key
|
209
193
|
if @last_tag && line.start_with?(@last_prefix.rstrip)
|
@@ -227,6 +211,7 @@ private
|
|
227
211
|
|
228
212
|
@last_tag = Tag.new match[:key].freeze, value
|
229
213
|
@line_breaks = 0
|
214
|
+
@block_indent = nil
|
230
215
|
|
231
216
|
# disable implicit values with the alternate syntax
|
232
217
|
register @last_tag unless match[:alt] && match[:value].nil?
|
@@ -236,10 +221,12 @@ private
|
|
236
221
|
end
|
237
222
|
|
238
223
|
def register(tag)
|
224
|
+
return if has? tag
|
239
225
|
key = tag.name.downcase.gsub(/[^\w]/, '_').to_sym
|
240
226
|
@data[key] = tag
|
241
227
|
end
|
242
228
|
|
229
|
+
# handle multiline tags
|
243
230
|
def append(line)
|
244
231
|
prefix = line.rstrip
|
245
232
|
if prefix == @last_prefix.rstrip
|
@@ -248,9 +235,19 @@ private
|
|
248
235
|
return true # add the line break later
|
249
236
|
elsif line.start_with? @last_prefix
|
250
237
|
mline = line[@last_prefix.size..-1]
|
251
|
-
|
252
|
-
|
253
|
-
|
238
|
+
|
239
|
+
if @block_indent
|
240
|
+
if mline.start_with? @block_indent
|
241
|
+
stripped = mline[@block_indent.size..-1]
|
242
|
+
else
|
243
|
+
return
|
244
|
+
end
|
245
|
+
else
|
246
|
+
stripped = mline.lstrip
|
247
|
+
indent_level = mline.index stripped
|
248
|
+
return if indent_level < 1
|
249
|
+
@block_indent = mline[0, indent_level]
|
250
|
+
end
|
254
251
|
else
|
255
252
|
return
|
256
253
|
end
|
data/lib/metaheader/version.rb
CHANGED
data/metaheader.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.name = "metaheader"
|
9
9
|
spec.version = MetaHeader::VERSION
|
10
10
|
spec.authors = ["cfillion"]
|
11
|
-
spec.email = ["metaheader@cfillion.
|
11
|
+
spec.email = ["metaheader@cfillion.ca"]
|
12
12
|
spec.summary = %q{Parser for metadata headers in plain-text files}
|
13
13
|
spec.homepage = "https://github.com/cfillion/metaheader"
|
14
14
|
spec.license = "LGPL-3.0+"
|
@@ -20,9 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.required_ruby_version = '>= 2.3'
|
22
22
|
|
23
|
-
spec.add_development_dependency 'bundler', '~>
|
24
|
-
spec.add_development_dependency '
|
25
|
-
spec.add_development_dependency '
|
26
|
-
spec.add_development_dependency '
|
27
|
-
spec.add_development_dependency 'simplecov', '~> 0.11'
|
23
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
24
|
+
spec.add_development_dependency 'minitest', '~> 5.10'
|
25
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
26
|
+
spec.add_development_dependency 'simplecov', '~> 0.13'
|
28
27
|
end
|
data/test/helper.rb
CHANGED
@@ -0,0 +1,193 @@
|
|
1
|
+
require File.expand_path '../helper', __FILE__
|
2
|
+
|
3
|
+
class TestMultiline < MiniTest::Test
|
4
|
+
def test_multiline
|
5
|
+
mh = MetaHeader.parse <<-IN
|
6
|
+
@test Lorem
|
7
|
+
Ipsum
|
8
|
+
IN
|
9
|
+
|
10
|
+
assert_equal "Lorem\nIpsum", mh[:test]
|
11
|
+
assert_equal 1, mh.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_variant
|
15
|
+
mh = MetaHeader.parse <<-IN
|
16
|
+
@test
|
17
|
+
Lorem
|
18
|
+
Ipsum
|
19
|
+
IN
|
20
|
+
|
21
|
+
assert_equal "Lorem\nIpsum", mh[:test]
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_trailing_space
|
25
|
+
mh = MetaHeader.parse <<-IN
|
26
|
+
@hello\x20
|
27
|
+
test\x20
|
28
|
+
@world\x20\x20
|
29
|
+
test\x20
|
30
|
+
IN
|
31
|
+
|
32
|
+
assert_equal 'test ', mh[:hello]
|
33
|
+
assert_equal 'test ', mh[:world]
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_prefix
|
37
|
+
mh = MetaHeader.parse <<-IN
|
38
|
+
-- @test Lorem
|
39
|
+
-- Ipsum
|
40
|
+
IN
|
41
|
+
|
42
|
+
assert_equal "Lorem\nIpsum", mh[:test]
|
43
|
+
assert_equal 1, mh.size
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_no_indent
|
47
|
+
mh = MetaHeader.parse <<-IN
|
48
|
+
@test Lorem
|
49
|
+
Ipsum
|
50
|
+
Test
|
51
|
+
IN
|
52
|
+
|
53
|
+
assert_equal 1, mh.size
|
54
|
+
assert_equal "Lorem", mh[:test]
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_lose_indent
|
58
|
+
mh = MetaHeader.parse <<-IN
|
59
|
+
@test
|
60
|
+
Hello
|
61
|
+
|
62
|
+
World
|
63
|
+
@chunky bacon
|
64
|
+
|
65
|
+
@foo
|
66
|
+
IN
|
67
|
+
|
68
|
+
assert_equal "Hello\n\nWorld", mh[:test]
|
69
|
+
assert_equal 'bacon', mh[:chunky]
|
70
|
+
assert_nil mh[:foo]
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_fewer_indent
|
74
|
+
mh = MetaHeader.parse <<-IN
|
75
|
+
@test Lorem
|
76
|
+
Ipsum
|
77
|
+
Hello
|
78
|
+
World
|
79
|
+
IN
|
80
|
+
|
81
|
+
assert_equal 1, mh.size
|
82
|
+
assert_equal "Lorem\nIpsum", mh[:test]
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_extra_indent
|
86
|
+
mh = MetaHeader.parse <<-IN
|
87
|
+
@test Lorem
|
88
|
+
Ipsum
|
89
|
+
Hello
|
90
|
+
World
|
91
|
+
IN
|
92
|
+
|
93
|
+
assert_equal 1, mh.size
|
94
|
+
assert_equal "Lorem\nIpsum\n Hello\nWorld", mh[:test]
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_do_not_reuse_indent
|
98
|
+
mh = MetaHeader.parse <<-IN
|
99
|
+
@a
|
100
|
+
Lorem
|
101
|
+
Ipsum
|
102
|
+
@b
|
103
|
+
\tHello
|
104
|
+
\tWorld
|
105
|
+
IN
|
106
|
+
|
107
|
+
assert_equal 2, mh.size
|
108
|
+
assert_equal "Lorem\nIpsum", mh[:a]
|
109
|
+
assert_equal "Hello\nWorld", mh[:b]
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_sub_alternate_syntax
|
113
|
+
mh = MetaHeader.parse <<-IN
|
114
|
+
@test Lorem
|
115
|
+
Ipsum:
|
116
|
+
Dolor: sit amet
|
117
|
+
IN
|
118
|
+
|
119
|
+
assert_equal "Lorem\nIpsum:\nDolor: sit amet", mh[:test]
|
120
|
+
assert_equal 1, mh.size
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_explicit_boolean
|
124
|
+
mh = MetaHeader.parse <<-IN
|
125
|
+
@test true
|
126
|
+
test
|
127
|
+
IN
|
128
|
+
|
129
|
+
assert_equal "true\ntest", mh[:test]
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_empty_line_prefix
|
133
|
+
mh = MetaHeader.parse <<-IN
|
134
|
+
--@test
|
135
|
+
-- Hello
|
136
|
+
--
|
137
|
+
-- World
|
138
|
+
--
|
139
|
+
--@chunky
|
140
|
+
-- bacon
|
141
|
+
IN
|
142
|
+
|
143
|
+
assert_equal "Hello\n\nWorld", mh[:test]
|
144
|
+
assert_equal 'bacon', mh[:chunky]
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_empty_line_prefix_with_space
|
148
|
+
mh = MetaHeader.parse <<-IN
|
149
|
+
-- @test
|
150
|
+
-- Hello
|
151
|
+
--
|
152
|
+
-- World
|
153
|
+
IN
|
154
|
+
|
155
|
+
assert_equal "Hello\n\nWorld", mh[:test]
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_empty_line
|
159
|
+
mh = MetaHeader.parse <<-IN
|
160
|
+
@test
|
161
|
+
Hello
|
162
|
+
|
163
|
+
World
|
164
|
+
@chunky bacon
|
165
|
+
|
166
|
+
@foo
|
167
|
+
IN
|
168
|
+
|
169
|
+
assert_equal "Hello\n\nWorld", mh[:test]
|
170
|
+
assert_equal 'bacon', mh[:chunky]
|
171
|
+
assert_nil mh[:foo]
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_break_at_empty_line
|
175
|
+
mh = MetaHeader.parse <<-IN
|
176
|
+
-- @hello world
|
177
|
+
|
178
|
+
@chunky bacon
|
179
|
+
IN
|
180
|
+
|
181
|
+
assert_equal 'world', mh[:hello]
|
182
|
+
assert_nil mh[:chunky]
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_alternate_syntax
|
186
|
+
mh = MetaHeader.parse <<-IN
|
187
|
+
-- Hello:
|
188
|
+
-- World
|
189
|
+
IN
|
190
|
+
|
191
|
+
assert_equal 'World', mh[:hello]
|
192
|
+
end
|
193
|
+
end
|
data/test/test_parser.rb
CHANGED
@@ -1,47 +1,15 @@
|
|
1
1
|
require File.expand_path '../helper', __FILE__
|
2
2
|
|
3
|
-
class CustomParser < MetaHeader::Parser
|
4
|
-
def self.reset
|
5
|
-
@@called = false
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.called?
|
9
|
-
@@called
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.input
|
13
|
-
@@input
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.instance
|
17
|
-
@@instance
|
18
|
-
end
|
19
|
-
|
20
|
-
def parse(input)
|
21
|
-
return unless header[:run_custom]
|
22
|
-
|
23
|
-
header[:hello] = header[:hello].to_s * 2
|
24
|
-
|
25
|
-
@@input = input.read
|
26
|
-
@@instance = header
|
27
|
-
@@called = true
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
3
|
class TestParser < MiniTest::Test
|
32
|
-
def setup
|
33
|
-
CustomParser.reset
|
34
|
-
end
|
35
|
-
|
36
4
|
def test_basic_parser
|
37
|
-
mh = MetaHeader.
|
5
|
+
mh = MetaHeader.parse '@hello world'
|
38
6
|
|
39
7
|
assert_equal 'world', mh[:hello]
|
40
8
|
assert_equal 1, mh.size
|
41
9
|
end
|
42
10
|
|
43
11
|
def test_set_value
|
44
|
-
mh = MetaHeader.
|
12
|
+
mh = MetaHeader.parse String.new
|
45
13
|
|
46
14
|
assert_empty mh
|
47
15
|
mh[:hello] = 'world'
|
@@ -57,23 +25,23 @@ class TestParser < MiniTest::Test
|
|
57
25
|
end
|
58
26
|
|
59
27
|
def test_implicit_boolean
|
60
|
-
mh = MetaHeader.
|
28
|
+
mh = MetaHeader.parse "@hello"
|
61
29
|
assert_equal true, mh[:hello]
|
62
30
|
end
|
63
31
|
|
64
32
|
def test_explicit_boolean
|
65
|
-
mh = MetaHeader.
|
33
|
+
mh = MetaHeader.parse "@foo true\n@bar false"
|
66
34
|
assert_equal true, mh[:foo]
|
67
35
|
assert_equal false, mh[:bar]
|
68
36
|
end
|
69
37
|
|
70
38
|
def test_ignore_prefix
|
71
|
-
mh = MetaHeader.
|
39
|
+
mh = MetaHeader.parse '-- @chunky bacon'
|
72
40
|
assert_equal 'bacon', mh[:chunky]
|
73
41
|
end
|
74
42
|
|
75
43
|
def test_two_tags
|
76
|
-
mh = MetaHeader.
|
44
|
+
mh = MetaHeader.parse <<-IN
|
77
45
|
-- @chunky bacon
|
78
46
|
-- @hello world
|
79
47
|
IN
|
@@ -84,7 +52,7 @@ class TestParser < MiniTest::Test
|
|
84
52
|
end
|
85
53
|
|
86
54
|
def test_break_at_empty_line
|
87
|
-
mh = MetaHeader.
|
55
|
+
mh = MetaHeader.parse <<-IN
|
88
56
|
@hello world
|
89
57
|
\x20
|
90
58
|
@chunky bacon
|
@@ -95,7 +63,7 @@ class TestParser < MiniTest::Test
|
|
95
63
|
end
|
96
64
|
|
97
65
|
def test_ignore_c_comment_tokens
|
98
|
-
mh = MetaHeader.
|
66
|
+
mh = MetaHeader.parse <<-IN
|
99
67
|
/*
|
100
68
|
-- @hello world
|
101
69
|
*/
|
@@ -110,12 +78,12 @@ class TestParser < MiniTest::Test
|
|
110
78
|
end
|
111
79
|
|
112
80
|
def test_trailing_whitespace
|
113
|
-
mh = MetaHeader.
|
81
|
+
mh = MetaHeader.parse '@hello world '
|
114
82
|
assert_equal 'world', mh[:hello]
|
115
83
|
end
|
116
84
|
|
117
85
|
def test_empty_prefixed_line
|
118
|
-
mh = MetaHeader.
|
86
|
+
mh = MetaHeader.parse <<-IN
|
119
87
|
-- @first
|
120
88
|
--
|
121
89
|
-- @second
|
@@ -124,272 +92,145 @@ class TestParser < MiniTest::Test
|
|
124
92
|
refute_nil mh[:second]
|
125
93
|
end
|
126
94
|
|
127
|
-
def
|
128
|
-
|
129
|
-
|
130
|
-
Ipsum
|
131
|
-
IN
|
132
|
-
|
133
|
-
assert_equal "Lorem\nIpsum", mh[:test]
|
134
|
-
assert_equal 1, mh.size
|
135
|
-
end
|
136
|
-
|
137
|
-
def test_multiline_variant
|
138
|
-
mh = MetaHeader.new <<-IN
|
139
|
-
@test
|
140
|
-
Lorem
|
141
|
-
Ipsum
|
142
|
-
IN
|
143
|
-
|
144
|
-
assert_equal "Lorem\nIpsum", mh[:test]
|
145
|
-
end
|
146
|
-
|
147
|
-
def test_multiline_trailing_space
|
148
|
-
mh = MetaHeader.new <<-IN
|
149
|
-
@hello\x20
|
150
|
-
test\x20
|
151
|
-
@world\x20\x20
|
152
|
-
test\x20
|
153
|
-
IN
|
154
|
-
|
155
|
-
assert_equal 'test ', mh[:hello]
|
156
|
-
assert_equal 'test ', mh[:world]
|
157
|
-
end
|
158
|
-
|
159
|
-
def test_multiline_prefix
|
160
|
-
mh = MetaHeader.new <<-IN
|
161
|
-
-- @test Lorem
|
162
|
-
-- Ipsum
|
163
|
-
IN
|
164
|
-
|
165
|
-
assert_equal "Lorem\nIpsum", mh[:test]
|
166
|
-
assert_equal 1, mh.size
|
167
|
-
end
|
168
|
-
|
169
|
-
def test_multiline_wrong_indent
|
170
|
-
mh = MetaHeader.new <<-IN
|
171
|
-
@test Lorem
|
172
|
-
Ipsum
|
173
|
-
Test
|
174
|
-
IN
|
175
|
-
|
176
|
-
assert_equal 1, mh.size
|
177
|
-
assert_equal "Lorem", mh[:test]
|
178
|
-
end
|
179
|
-
|
180
|
-
def test_multiline_sub_alternate_syntax
|
181
|
-
mh = MetaHeader.new <<-IN
|
182
|
-
@test Lorem
|
183
|
-
Ipsum:
|
184
|
-
Dolor: sit amet
|
185
|
-
IN
|
95
|
+
def test_from_file
|
96
|
+
path = File.expand_path '../input/basic_tag', __FILE__
|
97
|
+
mh = MetaHeader.from_file path
|
186
98
|
|
187
|
-
assert_equal
|
99
|
+
assert_equal 'Hello World', mh[:test]
|
188
100
|
assert_equal 1, mh.size
|
189
101
|
end
|
190
102
|
|
191
|
-
def
|
192
|
-
mh = MetaHeader.new <<-IN
|
193
|
-
@test true
|
194
|
-
test
|
195
|
-
IN
|
196
|
-
|
197
|
-
assert_equal "true\ntest", mh[:test]
|
198
|
-
end
|
199
|
-
|
200
|
-
def test_multiline_empty_line_prefix
|
201
|
-
mh = MetaHeader.new <<-IN
|
202
|
-
--@test
|
203
|
-
-- Hello
|
204
|
-
--
|
205
|
-
-- World
|
206
|
-
--
|
207
|
-
--@chunky
|
208
|
-
-- bacon
|
209
|
-
IN
|
210
|
-
|
211
|
-
assert_equal "Hello\n\nWorld", mh[:test]
|
212
|
-
assert_equal 'bacon', mh[:chunky]
|
213
|
-
end
|
214
|
-
|
215
|
-
def test_multiline_empty_line_space_prefix
|
216
|
-
mh = MetaHeader.new <<-IN
|
217
|
-
-- @test
|
218
|
-
-- Hello
|
219
|
-
--
|
220
|
-
-- World
|
221
|
-
IN
|
222
|
-
|
223
|
-
assert_equal "Hello\n\nWorld", mh[:test]
|
224
|
-
end
|
225
|
-
|
226
|
-
def test_multiline_empty_line
|
227
|
-
mh = MetaHeader.new <<-IN
|
228
|
-
@test
|
229
|
-
Hello
|
230
|
-
|
231
|
-
World
|
232
|
-
@chunky bacon
|
233
|
-
|
234
|
-
@foo
|
235
|
-
IN
|
236
|
-
|
237
|
-
assert_equal "Hello\n\nWorld", mh[:test]
|
238
|
-
assert_equal 'bacon', mh[:chunky]
|
239
|
-
assert_nil mh[:foo]
|
240
|
-
end
|
241
|
-
|
242
|
-
def test_multiline_break_at_empty_line
|
243
|
-
mh = MetaHeader.new <<-IN
|
244
|
-
-- @hello world
|
245
|
-
|
246
|
-
@chunky bacon
|
247
|
-
IN
|
248
|
-
|
249
|
-
assert_equal 'world', mh[:hello]
|
250
|
-
assert_nil mh[:chunky]
|
251
|
-
end
|
252
|
-
|
253
|
-
def test_multiline_alternate_syntax
|
254
|
-
mh = MetaHeader.new <<-IN
|
255
|
-
-- Hello:
|
256
|
-
-- World
|
257
|
-
IN
|
258
|
-
|
259
|
-
assert_equal 'World', mh[:hello]
|
260
|
-
end
|
261
|
-
|
262
|
-
def test_read_file
|
103
|
+
def test_read_file_stream
|
263
104
|
path = File.expand_path '../input/basic_tag', __FILE__
|
264
|
-
mh = MetaHeader.
|
105
|
+
mh = MetaHeader.parse File.open(path)
|
265
106
|
|
266
107
|
assert_equal 'Hello World', mh[:test]
|
267
108
|
assert_equal 1, mh.size
|
268
109
|
end
|
269
110
|
|
111
|
+
def test_read_string_stream
|
112
|
+
stream = StringIO.new '@hello world'
|
113
|
+
MetaHeader.parse stream
|
114
|
+
end
|
115
|
+
|
270
116
|
def test_to_hash
|
271
|
-
mh = MetaHeader.
|
117
|
+
mh = MetaHeader.parse '@key value'
|
272
118
|
assert_equal Hash[key: 'value'], mh.to_h
|
273
119
|
end
|
274
120
|
|
275
121
|
def test_alternate_syntax
|
276
|
-
mh = MetaHeader.
|
122
|
+
mh = MetaHeader.parse 'Key Test: value'
|
277
123
|
assert_equal Hash[key_test: 'value'], mh.to_h
|
278
124
|
end
|
279
125
|
|
280
126
|
def test_alternate_syntax_prefix
|
281
|
-
mh = MetaHeader.
|
127
|
+
mh = MetaHeader.parse '-- Key Test: Value'
|
282
128
|
assert_equal Hash[key_test: 'Value'], mh.to_h
|
283
129
|
end
|
284
130
|
|
285
|
-
def
|
286
|
-
mh = MetaHeader.
|
131
|
+
def test_crlf_newlines
|
132
|
+
mh = MetaHeader.parse "key: value\r\n@run_custom"
|
287
133
|
assert_equal 'value', mh[:key]
|
288
|
-
assert_equal
|
134
|
+
assert_equal true, mh[:run_custom]
|
289
135
|
end
|
290
136
|
|
291
137
|
def test_alternate_syntax_trailing_space
|
292
|
-
mh = MetaHeader.
|
138
|
+
mh = MetaHeader.parse ' Key Test : Value'
|
293
139
|
assert_equal Hash[key_test: 'Value'], mh.to_h
|
294
140
|
end
|
295
141
|
|
296
142
|
def test_alternate_syntax_compact
|
297
|
-
mh = MetaHeader.
|
143
|
+
mh = MetaHeader.parse 'Key Test:Value'
|
298
144
|
assert_equal Hash[key_test: 'Value'], mh.to_h
|
299
145
|
end
|
300
146
|
|
301
147
|
def test_alternate_syntax_no_value
|
302
|
-
mh = MetaHeader.
|
148
|
+
mh = MetaHeader.parse 'Key Test:'
|
303
149
|
assert_equal Hash.new, mh.to_h
|
304
150
|
end
|
305
151
|
|
306
152
|
def test_inspect
|
307
|
-
mh = MetaHeader.
|
153
|
+
mh = MetaHeader.parse '@hello world'
|
308
154
|
|
309
155
|
hash = {hello: 'world'}
|
310
156
|
assert_equal "#<MetaHeader #{hash.inspect}>", mh.inspect
|
311
157
|
end
|
312
158
|
|
313
|
-
def test_default_parser_implementation
|
314
|
-
assert_raises NotImplementedError do
|
315
|
-
MetaHeader::Parser.new.parse String.new
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
def test_transform_from_text
|
320
|
-
input = "@run_custom\nHello\n\nWorld".freeze
|
321
|
-
|
322
|
-
mh = MetaHeader.new input
|
323
|
-
|
324
|
-
assert CustomParser.called?
|
325
|
-
assert_equal input, CustomParser.input
|
326
|
-
assert_same mh, CustomParser.instance
|
327
|
-
end
|
328
|
-
|
329
|
-
def test_transform_from_file
|
330
|
-
path = File.expand_path '../input/custom_parser', __FILE__
|
331
|
-
|
332
|
-
mh = MetaHeader.from_file path
|
333
|
-
assert_equal 'worldworld', mh[:hello]
|
334
|
-
|
335
|
-
assert CustomParser.called?
|
336
|
-
assert_equal File.read(path), CustomParser.input
|
337
|
-
assert_same mh, CustomParser.instance
|
338
|
-
end
|
339
|
-
|
340
159
|
def test_has_tag
|
341
|
-
mh = MetaHeader.
|
160
|
+
mh = MetaHeader.parse '@hello'
|
342
161
|
assert_equal true, mh.has?(:hello)
|
343
162
|
assert_equal false, mh.has?(:world)
|
344
163
|
end
|
345
164
|
|
346
165
|
def test_default_value
|
347
|
-
mh = MetaHeader.
|
166
|
+
mh = MetaHeader.parse String.new
|
348
167
|
assert_equal 'world', mh[:hello, 'world']
|
349
168
|
end
|
350
169
|
|
351
170
|
def test_delete
|
352
|
-
mh = MetaHeader.new
|
171
|
+
mh = MetaHeader.new
|
172
|
+
mh[:hello] = 'world'
|
353
173
|
assert mh.has?(:hello)
|
354
174
|
mh.delete :hello
|
355
175
|
refute mh.has?(:hello)
|
356
176
|
end
|
357
177
|
|
358
|
-
def test_construct_from_instance
|
359
|
-
mh = MetaHeader.new '@hello world'
|
360
|
-
assert_same mh, MetaHeader.parse(mh)
|
361
|
-
assert_equal mh.to_h, MetaHeader.parse('@hello world').to_h
|
362
|
-
end
|
363
|
-
|
364
178
|
def test_alias
|
365
|
-
mh = MetaHeader.new
|
179
|
+
mh = MetaHeader.new
|
180
|
+
mh[:a] = '1'
|
366
181
|
mh.alias :a, :b
|
367
182
|
refute mh.has?(:a)
|
368
183
|
assert_equal '1', mh[:b]
|
369
|
-
|
370
|
-
mh[:d] = '2'
|
371
|
-
mh.alias :c, :d
|
372
|
-
refute mh.has?(:c)
|
373
|
-
assert_equal '2', mh[:d]
|
374
184
|
end
|
375
185
|
|
376
186
|
def test_alias_hash
|
377
|
-
mh = MetaHeader.new
|
187
|
+
mh = MetaHeader.new
|
188
|
+
mh[:a] = '1'
|
189
|
+
mh[:b] = '2'
|
378
190
|
mh.alias a: :c, b: :d
|
379
191
|
assert_equal '1', mh[:c]
|
380
192
|
assert_equal '2', mh[:d]
|
381
193
|
end
|
382
194
|
|
383
195
|
def test_alias_array
|
384
|
-
mh = MetaHeader.new
|
196
|
+
mh = MetaHeader.new
|
197
|
+
mh[:a] = '1'
|
198
|
+
mh[:b] = '2'
|
385
199
|
mh.alias [:a, :b, :c], :d
|
386
200
|
assert [:a, :b, :c].none? {|t| mh.has? t }
|
387
201
|
assert_equal '2', mh[:d]
|
388
202
|
end
|
389
203
|
|
390
|
-
def
|
391
|
-
mh = MetaHeader.
|
392
|
-
|
393
|
-
|
204
|
+
def test_utf16_bom
|
205
|
+
mh = MetaHeader.parse "\xff\xfe@a b\n"
|
206
|
+
assert_equal 'b', mh[:a]
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_content_offset
|
210
|
+
mh = MetaHeader.new
|
211
|
+
input = "@hello\n@world s\n\nafter\n"
|
212
|
+
content_offset = mh.parse input
|
213
|
+
assert_equal input.index('after'), content_offset
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_content_offset_decorated
|
217
|
+
mh = MetaHeader.new
|
218
|
+
input = "# @hello\n# @world\n\nafter\n"
|
219
|
+
content_offset = mh.parse input
|
220
|
+
assert_equal input.index('after'), content_offset
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_content_offset_unicode
|
224
|
+
mh = MetaHeader.new
|
225
|
+
input = "@lang русский\n\nafter"
|
226
|
+
content_offset = mh.parse input
|
227
|
+
assert_equal input.index('after'), content_offset
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_content_offset_trailing_space
|
231
|
+
mh = MetaHeader.new
|
232
|
+
input = "@foo\x20\x20\n bar\n\nafter\n"
|
233
|
+
content_offset = mh.parse input
|
234
|
+
assert_equal input.index('after'), content_offset
|
394
235
|
end
|
395
236
|
end
|
data/test/test_validator.rb
CHANGED
@@ -2,42 +2,38 @@ require File.expand_path '../helper', __FILE__
|
|
2
2
|
|
3
3
|
class TestValidator < MiniTest::Test
|
4
4
|
def validate(input, rules)
|
5
|
-
MetaHeader.
|
5
|
+
MetaHeader.parse(input).validate(rules)
|
6
6
|
end
|
7
7
|
|
8
8
|
def test_unknown_strict
|
9
|
-
mh = MetaHeader.
|
10
|
-
mh.
|
11
|
-
|
12
|
-
actual = mh.validate Hash.new
|
13
|
-
assert_equal ["unknown tag 'hello'", "unknown tag 'WORLD'"], actual
|
9
|
+
mh = MetaHeader.parse "@hello\n@WORLD"
|
10
|
+
errors = mh.validate Hash.new, true
|
11
|
+
assert_equal ["unknown tag 'hello'", "unknown tag 'WORLD'"], errors
|
14
12
|
end
|
15
13
|
|
16
14
|
def test_unknown_tolerant
|
17
|
-
mh = MetaHeader.
|
18
|
-
|
19
|
-
|
20
|
-
assert_nil mh.validate(Hash.new)
|
15
|
+
mh = MetaHeader.parse "@hello\n@world"
|
16
|
+
assert_empty mh.validate(Hash.new, false)
|
21
17
|
end
|
22
18
|
|
23
19
|
def test_strict_optional
|
24
|
-
|
25
|
-
mh.strict = true
|
26
|
-
|
27
|
-
actual = mh.validate \
|
20
|
+
rules = {
|
28
21
|
hello: MetaHeader::OPTIONAL,
|
29
|
-
world: MetaHeader::OPTIONAL
|
22
|
+
world: MetaHeader::OPTIONAL,
|
23
|
+
}
|
30
24
|
|
31
|
-
|
25
|
+
mh = MetaHeader.parse "@hello"
|
26
|
+
errors = mh.validate rules, true
|
27
|
+
assert_empty errors
|
32
28
|
end
|
33
29
|
|
34
30
|
def test_required
|
35
|
-
|
36
|
-
assert_equal ["missing tag 'version'"],
|
31
|
+
errors = validate '@foobar', version: MetaHeader::REQUIRED, foobar: []
|
32
|
+
assert_equal ["missing tag 'version'"], errors
|
37
33
|
end
|
38
34
|
|
39
35
|
def test_singleline
|
40
|
-
mh = MetaHeader.
|
36
|
+
mh = MetaHeader.parse <<-IN
|
41
37
|
@hello
|
42
38
|
chunky
|
43
39
|
bacon
|
@@ -46,50 +42,50 @@ class TestValidator < MiniTest::Test
|
|
46
42
|
bar
|
47
43
|
IN
|
48
44
|
|
49
|
-
|
50
|
-
assert_equal ["tag 'hello' must be singleline"],
|
45
|
+
errors = mh.validate :hello => MetaHeader::SINGLELINE
|
46
|
+
assert_equal ["tag 'hello' must be singleline"], errors
|
51
47
|
end
|
52
48
|
|
53
49
|
def test_has_value
|
54
|
-
mh = MetaHeader.
|
50
|
+
mh = MetaHeader.parse '@hello'
|
55
51
|
|
56
|
-
|
57
|
-
assert_equal ["missing value for tag 'hello'"],
|
52
|
+
errors = mh.validate :hello => [MetaHeader::VALUE]
|
53
|
+
assert_equal ["missing value for tag 'hello'"], errors
|
58
54
|
end
|
59
55
|
|
60
56
|
def test_regex
|
61
|
-
|
62
|
-
assert_equal ["invalid value for tag 'hello'"],
|
57
|
+
errors = validate '@hello world', :hello => /\d+/
|
58
|
+
assert_equal ["invalid value for tag 'hello'"], errors
|
63
59
|
end
|
64
60
|
|
65
61
|
def test_regex_no_value
|
66
|
-
mh = MetaHeader.
|
62
|
+
mh = MetaHeader.parse '@hello'
|
67
63
|
|
68
|
-
|
69
|
-
assert_equal ["invalid value for tag 'hello'"],
|
64
|
+
errors = mh.validate :hello => [/./]
|
65
|
+
assert_equal ["invalid value for tag 'hello'"], errors
|
70
66
|
end
|
71
67
|
|
72
68
|
def test_custom_validator
|
73
|
-
|
69
|
+
errors = validate '@hello',
|
74
70
|
hello: Proc.new {|value| assert_equal true, value; nil }
|
75
|
-
|
71
|
+
assert_empty errors
|
76
72
|
|
77
|
-
|
73
|
+
errors = validate '@hello world',
|
78
74
|
hello: Proc.new {|value| assert_equal 'world', value; nil }
|
79
|
-
|
75
|
+
assert_empty errors
|
80
76
|
|
81
|
-
|
82
|
-
assert_equal ["invalid value for tag 'hello': Hello World!"],
|
77
|
+
errors = validate '@hello', hello: Proc.new {|value| 'Hello World!' }
|
78
|
+
assert_equal ["invalid value for tag 'hello': Hello World!"], errors
|
83
79
|
end
|
84
80
|
|
85
81
|
def test_error_use_original_case
|
86
|
-
|
87
|
-
assert_equal ["invalid value for tag 'HeLlO'"],
|
82
|
+
errors = validate 'HeLlO: world', hello: /\d+/
|
83
|
+
assert_equal ["invalid value for tag 'HeLlO'"], errors
|
88
84
|
end
|
89
85
|
|
90
86
|
def test_single_error_per_tag
|
91
|
-
|
92
|
-
assert_equal ["invalid value for tag 'hello'"],
|
87
|
+
errors = validate '@hello', hello: [/\d/, /\d/]
|
88
|
+
assert_equal ["invalid value for tag 'hello'"], errors
|
93
89
|
end
|
94
90
|
|
95
91
|
def test_invalid_rule
|
@@ -103,13 +99,13 @@ class TestValidator < MiniTest::Test
|
|
103
99
|
end
|
104
100
|
|
105
101
|
def test_boolean
|
106
|
-
|
102
|
+
errors = validate "@hello true\n@hello world",
|
107
103
|
hello: MetaHeader::BOOLEAN
|
108
|
-
assert_equal ["tag 'hello' cannot have a value"],
|
104
|
+
assert_equal ["tag 'hello' cannot have a value"], errors
|
109
105
|
end
|
110
106
|
|
111
107
|
def test_alias
|
112
|
-
mh = MetaHeader.
|
108
|
+
mh = MetaHeader.parse "@a"
|
113
109
|
mh.alias :a, :b
|
114
110
|
assert_equal ["missing value for tag 'a'"],
|
115
111
|
mh.validate(b: [MetaHeader::REQUIRED, MetaHeader::VALUE])
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metaheader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- cfillion
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,73 +16,59 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: coveralls
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.8'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0.8'
|
26
|
+
version: '2.0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: minitest
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
31
|
- - "~>"
|
46
32
|
- !ruby/object:Gem::Version
|
47
|
-
version: '5.
|
33
|
+
version: '5.10'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
38
|
- - "~>"
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: '5.
|
40
|
+
version: '5.10'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: rake
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - "~>"
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
47
|
+
version: '13.0'
|
62
48
|
type: :development
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
52
|
- - "~>"
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
54
|
+
version: '13.0'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: simplecov
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - "~>"
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0.
|
61
|
+
version: '0.13'
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0.
|
83
|
-
description:
|
68
|
+
version: '0.13'
|
69
|
+
description:
|
84
70
|
email:
|
85
|
-
- metaheader@cfillion.
|
71
|
+
- metaheader@cfillion.ca
|
86
72
|
executables: []
|
87
73
|
extensions: []
|
88
74
|
extra_rdoc_files: []
|
@@ -101,13 +87,14 @@ files:
|
|
101
87
|
- test/helper.rb
|
102
88
|
- test/input/basic_tag
|
103
89
|
- test/input/custom_parser
|
90
|
+
- test/test_multiline.rb
|
104
91
|
- test/test_parser.rb
|
105
92
|
- test/test_validator.rb
|
106
93
|
homepage: https://github.com/cfillion/metaheader
|
107
94
|
licenses:
|
108
95
|
- LGPL-3.0+
|
109
96
|
metadata: {}
|
110
|
-
post_install_message:
|
97
|
+
post_install_message:
|
111
98
|
rdoc_options: []
|
112
99
|
require_paths:
|
113
100
|
- lib
|
@@ -118,18 +105,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
118
105
|
version: '2.3'
|
119
106
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
107
|
requirements:
|
121
|
-
- - "
|
108
|
+
- - ">="
|
122
109
|
- !ruby/object:Gem::Version
|
123
|
-
version:
|
110
|
+
version: '0'
|
124
111
|
requirements: []
|
125
|
-
|
126
|
-
|
127
|
-
signing_key:
|
112
|
+
rubygems_version: 3.1.4
|
113
|
+
signing_key:
|
128
114
|
specification_version: 4
|
129
115
|
summary: Parser for metadata headers in plain-text files
|
130
116
|
test_files:
|
131
117
|
- test/helper.rb
|
132
118
|
- test/input/basic_tag
|
133
119
|
- test/input/custom_parser
|
120
|
+
- test/test_multiline.rb
|
134
121
|
- test/test_parser.rb
|
135
122
|
- test/test_validator.rb
|