metaheader 1.1.1 → 1.2

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 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