metaheader 1.1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/lib/metaheader/version.rb +1 -1
- data/lib/metaheader.rb +58 -16
- data/test/test_parser.rb +50 -2
- data/test/test_validator.rb +12 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b27a892f14adfb3c27237943a6092a25e7976b4a
|
4
|
+
data.tar.gz: 066516c6ee0af9a85544ef3ab226c249d2ed6d78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0062129b4f626c499d89d340f6a43eb5f4a65ecf15632c186c9e3dbae68f5799c719e19d30b7c6835f5c98ccc7baf936fb5c0734a53e776d0d871ffa23bf29c7
|
7
|
+
data.tar.gz: 99f9068fbc6118c4bab541d6c96b8344ad80838f73990d9197ca0d2231a18af454975134ddf46afe1613c38842cd8bbd383d6e190f97c323507a51f0ee7e49d8
|
data/README.md
CHANGED
@@ -14,6 +14,10 @@
|
|
14
14
|
value line 2
|
15
15
|
|
16
16
|
@key
|
17
|
+
@key true
|
18
|
+
|
19
|
+
@nokey
|
20
|
+
@key false
|
17
21
|
```
|
18
22
|
|
19
23
|
Any kind of comment syntax or prefix can be used:
|
@@ -50,9 +54,10 @@ mh = MetaHeader.new input
|
|
50
54
|
errors = mh.validate key: MetaHeader::REQUIRED
|
51
55
|
|
52
56
|
# other validators are available:
|
57
|
+
# mh.validate key: MetaHeader::BOOLEAN
|
53
58
|
# mh.validate key: MetaHeader::OPTIONAL
|
54
59
|
# mh.validate key: MetaHeader::SINGLELINE
|
55
|
-
# mh.validate key: MetaHeader::
|
60
|
+
# mh.validate key: MetaHeader::VALUE
|
56
61
|
# mh.validate key: /^\w{2,}$/
|
57
62
|
# mh.validate key: proc {|value| 'return nil or error' }
|
58
63
|
|
data/lib/metaheader/version.rb
CHANGED
data/lib/metaheader.rb
CHANGED
@@ -28,10 +28,11 @@ class MetaHeader
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
BOOLEAN = Object.new.freeze
|
32
32
|
OPTIONAL = Object.new.freeze
|
33
|
-
|
33
|
+
REQUIRED = Object.new.freeze
|
34
34
|
SINGLELINE = Object.new.freeze
|
35
|
+
VALUE = Object.new.freeze
|
35
36
|
|
36
37
|
# Whether to fail validation if unknown tags are encoutered.
|
37
38
|
# @see #validate
|
@@ -71,15 +72,23 @@ class MetaHeader
|
|
71
72
|
end
|
72
73
|
|
73
74
|
# Returns the value of a tag by its name, or nil if not found.
|
75
|
+
# @param key [Symbol] tag name
|
76
|
+
# @param default [Object] value to return if key doesn't exist
|
74
77
|
# @return [Object, nil]
|
75
|
-
def [](key)
|
76
|
-
tag = @data[key]
|
78
|
+
def [](key, default = nil)
|
79
|
+
if tag = @data[key]
|
80
|
+
tag.value
|
81
|
+
else
|
82
|
+
default
|
83
|
+
end
|
77
84
|
end
|
78
85
|
|
79
86
|
# Replaces the value of a tag.
|
80
87
|
# @param value the new value
|
81
88
|
# @return value
|
82
89
|
def []=(key, value)
|
90
|
+
raise ArgumentError, 'value cannot be nil' if value.nil?
|
91
|
+
|
83
92
|
@data[key] ||= Tag.new key
|
84
93
|
@data[key].value = value
|
85
94
|
end
|
@@ -96,10 +105,23 @@ class MetaHeader
|
|
96
105
|
@data.empty?
|
97
106
|
end
|
98
107
|
|
108
|
+
# Whether a tag was found in the input.
|
109
|
+
# @param tag [Symbol] the tag to lookup
|
110
|
+
# @return [Boolean]
|
111
|
+
def has?(tag)
|
112
|
+
@data.has_key? tag.to_sym
|
113
|
+
end
|
114
|
+
|
115
|
+
# Removes a given tag from the list.
|
116
|
+
# @param tag [Symbol] the tag to remove
|
117
|
+
def delete(tag)
|
118
|
+
@data.delete tag
|
119
|
+
end
|
120
|
+
|
99
121
|
# Make a hash from the parsed data
|
100
122
|
# @return [Hash]
|
101
123
|
def to_h
|
102
|
-
Hash[@data.map {|
|
124
|
+
Hash[@data.map {|name, tag| [name, tag.value] }]
|
103
125
|
end
|
104
126
|
|
105
127
|
# Makes a human-readable representation of the current instance.
|
@@ -116,10 +138,11 @@ class MetaHeader
|
|
116
138
|
# chunky: proc {|value| 'not bacon' unless value == 'bacon' }
|
117
139
|
# @param rules [Hash] tag_name => rule or array_of_rules
|
118
140
|
# @return [Array, nil] error list or nil
|
119
|
-
# @see
|
141
|
+
# @see BOOLEAN
|
120
142
|
# @see OPTIONAL
|
121
|
-
# @see
|
143
|
+
# @see REQUIRED
|
122
144
|
# @see SINGLELINE
|
145
|
+
# @see VALUE
|
123
146
|
def validate(rules)
|
124
147
|
errors = Array.new
|
125
148
|
|
@@ -148,6 +171,8 @@ private
|
|
148
171
|
\Z/x.freeze
|
149
172
|
|
150
173
|
def parse(line)
|
174
|
+
line.rstrip!
|
175
|
+
|
151
176
|
# multiline value must have the same prefix
|
152
177
|
if @last_key && line.index(@last_prefix) == 0
|
153
178
|
# remove the line prefix
|
@@ -159,12 +184,8 @@ private
|
|
159
184
|
if indent_level > 0
|
160
185
|
tag = @data[@last_key]
|
161
186
|
|
162
|
-
|
163
|
-
|
164
|
-
else
|
165
|
-
tag.value = String.new
|
166
|
-
end
|
167
|
-
|
187
|
+
tag.value = @raw_value.to_s unless tag.value.is_a? String
|
188
|
+
tag.value += "\n" unless tag.value.empty?
|
168
189
|
tag.value += stripped
|
169
190
|
|
170
191
|
return
|
@@ -177,12 +198,29 @@ private
|
|
177
198
|
|
178
199
|
# single line
|
179
200
|
@last_prefix = match[:prefix]
|
180
|
-
|
201
|
+
key = match[:key].downcase.gsub(/[^\w]/, '_')
|
181
202
|
|
182
|
-
|
203
|
+
@raw_value = match[:value]
|
204
|
+
key, value = parse_value key, @raw_value
|
205
|
+
|
206
|
+
@last_key = key.to_sym
|
183
207
|
@data[@last_key] = Tag.new match[:key].freeze, value
|
184
208
|
end
|
185
209
|
|
210
|
+
def parse_value(key, value)
|
211
|
+
value ||= true
|
212
|
+
|
213
|
+
case value
|
214
|
+
when 'true'
|
215
|
+
value = true
|
216
|
+
when 'false'
|
217
|
+
value = false
|
218
|
+
when String
|
219
|
+
value = nil if value.empty?
|
220
|
+
end
|
221
|
+
|
222
|
+
[key, value]
|
223
|
+
end
|
186
224
|
|
187
225
|
def validate_key(key, rules)
|
188
226
|
rules = Array(rules)
|
@@ -212,6 +250,10 @@ private
|
|
212
250
|
if str_value.empty?
|
213
251
|
return "missing value for tag '%s'" % tag.name
|
214
252
|
end
|
253
|
+
when BOOLEAN
|
254
|
+
unless [TrueClass, FalseClass].include? tag.value.class
|
255
|
+
return "tag '%s' cannot have a value" % tag.name
|
256
|
+
end
|
215
257
|
when Regexp
|
216
258
|
unless rule.match str_value
|
217
259
|
return "invalid value for tag '%s'" % tag.name
|
@@ -221,7 +263,7 @@ private
|
|
221
263
|
return "invalid value for tag '%s': %s" % [tag.name, error]
|
222
264
|
end
|
223
265
|
else
|
224
|
-
raise ArgumentError
|
266
|
+
raise ArgumentError, "unsupported validator #{rule}"
|
225
267
|
end
|
226
268
|
}
|
227
269
|
|
data/test/test_parser.rb
CHANGED
@@ -51,13 +51,22 @@ class TestParser < MiniTest::Test
|
|
51
51
|
mh[:hello] = 'bacon'
|
52
52
|
assert_equal 'bacon', mh[:hello]
|
53
53
|
assert_equal 1, mh.size
|
54
|
+
|
55
|
+
error = assert_raises(ArgumentError) { mh[:hello] = nil }
|
56
|
+
assert_equal 'value cannot be nil', error.message
|
54
57
|
end
|
55
58
|
|
56
|
-
def
|
57
|
-
mh = MetaHeader.new
|
59
|
+
def test_implicit_boolean
|
60
|
+
mh = MetaHeader.new "@hello"
|
58
61
|
assert_equal true, mh[:hello]
|
59
62
|
end
|
60
63
|
|
64
|
+
def test_explicit_boolean
|
65
|
+
mh = MetaHeader.new "@foo true\n@bar false"
|
66
|
+
assert_equal true, mh[:foo]
|
67
|
+
assert_equal false, mh[:bar]
|
68
|
+
end
|
69
|
+
|
61
70
|
def test_ignore_prefix
|
62
71
|
mh = MetaHeader.new '-- @chunky bacon'
|
63
72
|
assert_equal 'bacon', mh[:chunky]
|
@@ -120,6 +129,18 @@ class TestParser < MiniTest::Test
|
|
120
129
|
assert_equal "Lorem\nIpsum", mh[:test]
|
121
130
|
end
|
122
131
|
|
132
|
+
def test_multiline_trailing_space
|
133
|
+
mh = MetaHeader.new <<-IN
|
134
|
+
@hello\x20
|
135
|
+
test\x20
|
136
|
+
@world\x20\x20
|
137
|
+
test\x20
|
138
|
+
IN
|
139
|
+
|
140
|
+
assert_equal 'test', mh[:hello]
|
141
|
+
assert_equal 'test', mh[:world]
|
142
|
+
end
|
143
|
+
|
123
144
|
def test_multiline_prefix
|
124
145
|
mh = MetaHeader.new <<-IN
|
125
146
|
-- @test Lorem
|
@@ -152,6 +173,15 @@ class TestParser < MiniTest::Test
|
|
152
173
|
assert_equal 1, mh.size
|
153
174
|
end
|
154
175
|
|
176
|
+
def test_multiline_explicit_boolean
|
177
|
+
mh = MetaHeader.new <<-IN
|
178
|
+
@test true
|
179
|
+
test
|
180
|
+
IN
|
181
|
+
|
182
|
+
assert_equal "true\ntest", mh[:test]
|
183
|
+
end
|
184
|
+
|
155
185
|
def test_read_file
|
156
186
|
path = File.expand_path '../input/basic_tag', __FILE__
|
157
187
|
mh = MetaHeader.from_file path
|
@@ -219,4 +249,22 @@ class TestParser < MiniTest::Test
|
|
219
249
|
assert_equal File.read(path), CustomParser.input
|
220
250
|
assert_same mh, CustomParser.instance
|
221
251
|
end
|
252
|
+
|
253
|
+
def test_has_tag
|
254
|
+
mh = MetaHeader.new '@hello'
|
255
|
+
assert_equal true, mh.has?(:hello)
|
256
|
+
assert_equal false, mh.has?(:world)
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_default_value
|
260
|
+
mh = MetaHeader.new String.new
|
261
|
+
assert_equal 'world', mh[:hello, 'world']
|
262
|
+
end
|
263
|
+
|
264
|
+
def test_delete
|
265
|
+
mh = MetaHeader.new '@hello world'
|
266
|
+
assert mh.has?(:hello)
|
267
|
+
mh.delete :hello
|
268
|
+
refute mh.has?(:hello)
|
269
|
+
end
|
222
270
|
end
|
data/test/test_validator.rb
CHANGED
@@ -93,8 +93,18 @@ class TestValidator < MiniTest::Test
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def test_invalid_rule
|
96
|
-
|
97
|
-
|
96
|
+
obj = Object.new.freeze
|
97
|
+
|
98
|
+
error = assert_raises ArgumentError do
|
99
|
+
validate '@hello world', hello: obj
|
98
100
|
end
|
101
|
+
|
102
|
+
assert_equal "unsupported validator #{obj.inspect}", error.message
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_boolean
|
106
|
+
actual = validate "@hello true\n@hello world",
|
107
|
+
hello: MetaHeader::BOOLEAN
|
108
|
+
assert_equal ["tag 'hello' cannot have a value"], actual
|
99
109
|
end
|
100
110
|
end
|
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: '1.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- cfillion
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|