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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 88dca1c4f81765e7568da7a5aa48b2d272975deb
4
- data.tar.gz: f04beb0bd69cac43a5d9c044bdc5142475afa558
3
+ metadata.gz: b27a892f14adfb3c27237943a6092a25e7976b4a
4
+ data.tar.gz: 066516c6ee0af9a85544ef3ab226c249d2ed6d78
5
5
  SHA512:
6
- metadata.gz: 741a2bb4f73c74bee313be60df2a82ef8fcee75bc6a848eed0917200cb8ef652a0df3b92211d638c60d8ff19b5b36e9e279d6d675d535bc89f5d25370bd1ba59
7
- data.tar.gz: 492e930364f5af886d59a0accabd44aedf4787990da7725a2f0f579632faf0d67036bdecdf5249f4d6698cb4e1910dd213a34d06129be2420c2f9ab73050754e
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::HAS_VALUE
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
 
@@ -1,4 +1,4 @@
1
1
  class MetaHeader
2
2
  # MetaHeader's version
3
- VERSION = '1.1.1'.freeze
3
+ VERSION = '1.2'.freeze
4
4
  end
data/lib/metaheader.rb CHANGED
@@ -28,10 +28,11 @@ class MetaHeader
28
28
  end
29
29
  end
30
30
 
31
- REQUIRED = Object.new.freeze
31
+ BOOLEAN = Object.new.freeze
32
32
  OPTIONAL = Object.new.freeze
33
- VALUE = Object.new.freeze
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] and tag.value
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 {|v| [v.first, v.last.value] }]
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 REQUIRED
141
+ # @see BOOLEAN
120
142
  # @see OPTIONAL
121
- # @see VALUE
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
- if tag.value.is_a? String
163
- tag.value += "\n"
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
- @last_key = match[:key].downcase.gsub(/[^\w]/, '_').to_sym
201
+ key = match[:key].downcase.gsub(/[^\w]/, '_')
181
202
 
182
- value = match[:value] || true
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 test_unrequired_value
57
- mh = MetaHeader.new '@hello'
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
@@ -93,8 +93,18 @@ class TestValidator < MiniTest::Test
93
93
  end
94
94
 
95
95
  def test_invalid_rule
96
- assert_raises ArgumentError do
97
- validate '@hello world', hello: Object.new
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.1.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-07 00:00:00.000000000 Z
11
+ date: 2016-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler