metaheader 1.3.1 → 2.0.0

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
- SHA1:
3
- metadata.gz: 3bff210852c9274797ff6242ea138f86806fad05
4
- data.tar.gz: 0734b967a2fc8997a7f8ed59945bbfedfd2bcdb3
2
+ SHA256:
3
+ metadata.gz: 12c2b91a83ff05bf044e54acbd9b5143fc72fa5e68b08d2b21c6517054626146
4
+ data.tar.gz: ad3f13076f50ce44745b8ef87d15b1105f189f6081b52a88e749df11464df9cb
5
5
  SHA512:
6
- metadata.gz: 20cf4d00d0029c9c7e3ba81d8a5924182b8be576414dd8f1127306d20453ef85d85f9d39cc3ad99e0141d6edd87eb2654780018790598f05a2bf255668f4ad8e
7
- data.tar.gz: 6a07446e9cdc3d3cdad714459b3f58874efb32dc06da2b304c65b2f8954f79d004195e44339ae57756503bfdf4f01b019793ffb10f3d59fe56924d706e095f43
6
+ metadata.gz: 7e94db96de5e751cc89fe3bf6f38d12105c278dd47623f6eee6bbb66d4d061b281219e1d0db3df97af6fda8e29da2c07cae9daa7c59eaafa22f834083d79ba91
7
+ data.tar.gz: 9455e3d58f9a796f662a5cad16225ce5c114e124110ebbfd9015e5163f21c0c34349aff57ad1dc8f609de76416bee3ac2c6c7266d19a791f43c7bd8379d497a5
data/Gemfile CHANGED
@@ -1,5 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'pry'
4
-
5
3
  gemspec
data/README.md CHANGED
@@ -19,7 +19,8 @@
19
19
  @key false
20
20
  ```
21
21
 
22
- 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):
23
24
 
24
25
  ```cpp
25
26
  /*
@@ -27,13 +28,14 @@ Any kind of comment syntax or prefix can be used:
27
28
  */
28
29
  ```
29
30
 
30
- An alternative syntax is also supported:
31
+ An alternative syntax that allows spaces in the key name is also supported:
31
32
 
32
33
  ```
33
- Key Name: Value
34
+ Key: Value
34
35
  ```
35
36
 
36
- Parsing stops at the first empty line (ignoring white space).
37
+ Parsing stops at the first empty line outside of a multiline value
38
+ (ignoring white space).
37
39
 
38
40
  ## Usage
39
41
 
@@ -41,14 +43,11 @@ Parsing stops at the first empty line (ignoring white space).
41
43
  require 'metaheader'
42
44
 
43
45
  input = '@key value'
44
- mh = MetaHeader.new input
46
+ mh = MetaHeader.parse input
45
47
 
46
48
  # alternatively:
47
49
  # mh = MetaHeader.from_file path
48
50
 
49
- # mark unknown keys as invalid
50
- # mh.strict = true
51
-
52
51
  # set @key as mandatory
53
52
  errors = mh.validate key: MetaHeader::REQUIRED
54
53
 
@@ -65,13 +64,12 @@ value = mh[:key]
65
64
 
66
65
  ## Documentation
67
66
 
68
- MetaHeader's documentation is hosted at
69
- [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>.
70
68
 
71
69
  ## Contributing
72
70
 
73
71
  1. [Fork this repository](https://github.com/cfillion/metaheader/fork)
74
- 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 2. Create your feature branch (`git switch -c my-new-feature`)
75
73
  3. Commit your changes (`git commit -am 'Add some feature'`)
76
74
  4. Push to the branch (`git push -u origin my-new-feature`)
77
75
  5. Create a new Pull Request
data/lib/metaheader.rb CHANGED
@@ -1,84 +1,67 @@
1
1
  require 'metaheader/version'
2
2
 
3
+ # Parser for metadata header in plain-text files
4
+ # @example
5
+ # mh = MetaHeader.parse '@hello world'
6
+ # puts mh[:hello]
3
7
  class MetaHeader
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
18
-
19
- # @return [MetaHeader] the current instance
20
- def header
21
- @mh
22
- end
8
+ # Allow a tag to be omitted when validating in strict mode.
9
+ OPTIONAL = Object.new.freeze
23
10
 
24
- # @param raw_input [IO]
25
- # @return [void]
26
- def parse(raw_input)
27
- raise NotImplementedError
28
- end
29
- end
11
+ # Ensure a tag exists during validation.
12
+ REQUIRED = Object.new.freeze
30
13
 
14
+ # The tag cannot hold a value beside true or false during validation.
31
15
  BOOLEAN = Object.new.freeze
32
- OPTIONAL = Object.new.freeze
33
- REQUIRED = Object.new.freeze
34
- SINGLELINE = Object.new.freeze
16
+
17
+ # The tag must have a string value (non-boolean tag) during validation.
35
18
  VALUE = Object.new.freeze
36
19
 
37
- # Whether to fail validation if unknown tags are encoutered.
38
- # @see #validate
39
- # @return [Boolean]
40
- attr_accessor :strict
20
+ # Don't allow multiline values during validation.
21
+ SINGLELINE = Object.new.freeze
41
22
 
42
23
  # Create a new instance from the contents of a file.
43
24
  # @param path [String] path to the file to be read
44
25
  # @return [MetaHeader]
45
26
  def self.from_file(path)
46
- File.open(path) {|file| self.new file }
27
+ File.open(path) {|file| self.parse file }
47
28
  end
48
29
 
49
- # Construct a new MetaHeader object or return the object untouched
50
- # @param input [String, MetaHeader]
30
+ # Construct a MetaHeader object and parse every tags found in the input up to
31
+ # the first newline.
32
+ # @param input [String, IO, StringIO]
51
33
  # @return [MetaHeader]
52
34
  def self.parse(input)
53
- if input.is_a? self
54
- input
55
- else
56
- self.new input
57
- end
35
+ mh = MetaHeader.new
36
+ mh.parse input
37
+ mh
58
38
  end
59
39
 
60
- # Parse every tags found in input up to the first newline.
61
- # @param input [String, IO]
62
- def initialize(input)
63
- @strict = false
40
+ # Construct a blank MetaHeader object
41
+ def initialize
64
42
  @data = {}
43
+ end
65
44
 
66
- @last_tag = nil
67
- @empty_lines = 0
68
-
69
- unless input.is_a? IO
45
+ # Parse every tags found in the input up to the first newline.
46
+ # @param input [String, IO, StringIO]
47
+ # @return [Integer] Character position of the first content line in the input
48
+ # data following the header.
49
+ def parse(input)
50
+ if input.is_a? String
70
51
  input = StringIO.new input.encode universal_newline: true
71
52
  end
72
53
 
73
- input.each_line {|line| break unless parse line }
74
-
75
- Parser.each {|klass|
76
- input.rewind
54
+ @last_tag = nil
55
+ @empty_lines = 0
77
56
 
78
- parser = klass.new
79
- parser.instance_variable_set :@mh, self
80
- parser.parse input
81
- }
57
+ content_offset = 0
58
+ input.each_line do |line|
59
+ full_line_size = line.size # parse_line can trim the line
60
+ continue = parse_line line
61
+ content_offset += full_line_size if continue || line.empty?
62
+ break unless continue
63
+ end
64
+ content_offset
82
65
  end
83
66
 
84
67
  # Returns the value of a tag by its name, or nil if not found.
@@ -95,7 +78,7 @@ class MetaHeader
95
78
 
96
79
  # Replaces the value of a tag.
97
80
  # @param value the new value
98
- # @return value
81
+ # @return [Object] value
99
82
  def []=(key, value)
100
83
  raise ArgumentError, 'value cannot be nil' if value.nil?
101
84
 
@@ -129,7 +112,7 @@ class MetaHeader
129
112
  end
130
113
 
131
114
  # Make a hash from the parsed data
132
- # @return [Hash]
115
+ # @return [Hash<Symbol, Object>]
133
116
  def to_h
134
117
  Hash[@data.map {|name, tag| [name, tag.value] }]
135
118
  end
@@ -141,22 +124,25 @@ class MetaHeader
141
124
  end
142
125
 
143
126
  # Validates parsed data according to a custom set of rules.
127
+ # A rule can be one of the predefined constants, a regex, a proc or a
128
+ # method (returing nil if the tag is valid or an error string otherwise).
144
129
  # @example
145
130
  # mh = MetaHeader.new "@hello world\n@chunky bacon"
146
131
  # mh.validate \
147
132
  # hello: [MetaHeader::REQUIRED, MetaHeader::SINGLELINE, /\d/],
148
133
  # chunky: proc {|value| 'not bacon' unless value == 'bacon' }
149
- # @param rules [Hash] tag_name => rule or array_of_rules
150
- # @return [Array, nil] error list or nil
151
- # @see BOOLEAN
134
+ # @param rules [Hash] :tag_name => rule or [rule1, rule2, ...]
135
+ # @param strict [Boolean] Whether to report unknown tags as errors
136
+ # @return [Array<String>] List of error messasges
152
137
  # @see OPTIONAL
153
138
  # @see REQUIRED
154
- # @see SINGLELINE
139
+ # @see BOOLEAN
155
140
  # @see VALUE
156
- def validate(rules)
157
- errors = Array.new
141
+ # @see SINGLELINE
142
+ def validate(rules, strict = false)
143
+ errors = []
158
144
 
159
- if @strict
145
+ if strict
160
146
  @data.each {|key, tag|
161
147
  errors << "unknown tag '%s'" % tag.name unless rules.has_key? key
162
148
  }
@@ -168,27 +154,22 @@ class MetaHeader
168
154
  end
169
155
  }
170
156
 
171
- errors unless errors.empty?
157
+ errors
172
158
  end
173
159
 
174
160
  # Rename one or more tags.
175
- # @param old [Symbol, Hash]
176
- # @param new [Symbol]
161
+ # @param old [Symbol, Array<Symbol>, Hash<Symbol, Symbol>]
162
+ # @param new [Symbol] Ignored if old is a Hash
177
163
  # @example
178
164
  # mh.alias :old, :new
179
- # mh.alias :old1, :old2, :new
180
165
  # mh.alias [:old1, :old2], :new
181
166
  # mh.alias old1: :new1, old2: :new2
182
- def alias(*args)
183
- raise ArgumentError, 'wrong number of arguments' unless args.size.between? 1, 2
184
-
185
- tags, new = args
186
-
187
- if args.size == 1
188
- tags.each {|k, v| self.alias k, v }
167
+ def alias(old, new = nil)
168
+ if old.is_a? Hash
169
+ old.each {|k, v| self.alias k, v }
189
170
  else
190
- Array(tags).each {|old|
191
- @data[new] = delete old if has? old
171
+ Array(old).each {|tag|
172
+ @data[new] = delete tag if has? tag
192
173
  }
193
174
  end
194
175
  end
@@ -202,7 +183,7 @@ private
202
183
  (?:\s*(?<value>[^\n]+))?
203
184
  \Z/x.freeze
204
185
 
205
- def parse(line)
186
+ def parse_line(line)
206
187
  line.chomp!
207
188
  line.encode! Encoding::UTF_8, invalid: :replace
208
189
 
@@ -1,4 +1,4 @@
1
1
  class MetaHeader
2
2
  # MetaHeader's version
3
- VERSION = '1.3.1'.freeze
3
+ VERSION = '2.0.0'.freeze
4
4
  end
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.tk"]
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,8 +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', '~> 1.14'
23
+ spec.add_development_dependency 'bundler', '~> 2.0'
24
24
  spec.add_development_dependency 'minitest', '~> 5.10'
25
- spec.add_development_dependency 'rake', '~> 12.0'
25
+ spec.add_development_dependency 'rake', '~> 13.0'
26
26
  spec.add_development_dependency 'simplecov', '~> 0.13'
27
27
  end
@@ -2,7 +2,7 @@ require File.expand_path '../helper', __FILE__
2
2
 
3
3
  class TestMultiline < MiniTest::Test
4
4
  def test_multiline
5
- mh = MetaHeader.new <<-IN
5
+ mh = MetaHeader.parse <<-IN
6
6
  @test Lorem
7
7
  Ipsum
8
8
  IN
@@ -12,7 +12,7 @@ class TestMultiline < MiniTest::Test
12
12
  end
13
13
 
14
14
  def test_variant
15
- mh = MetaHeader.new <<-IN
15
+ mh = MetaHeader.parse <<-IN
16
16
  @test
17
17
  Lorem
18
18
  Ipsum
@@ -22,7 +22,7 @@ class TestMultiline < MiniTest::Test
22
22
  end
23
23
 
24
24
  def test_trailing_space
25
- mh = MetaHeader.new <<-IN
25
+ mh = MetaHeader.parse <<-IN
26
26
  @hello\x20
27
27
  test\x20
28
28
  @world\x20\x20
@@ -34,7 +34,7 @@ class TestMultiline < MiniTest::Test
34
34
  end
35
35
 
36
36
  def test_prefix
37
- mh = MetaHeader.new <<-IN
37
+ mh = MetaHeader.parse <<-IN
38
38
  -- @test Lorem
39
39
  -- Ipsum
40
40
  IN
@@ -44,7 +44,7 @@ class TestMultiline < MiniTest::Test
44
44
  end
45
45
 
46
46
  def test_no_indent
47
- mh = MetaHeader.new <<-IN
47
+ mh = MetaHeader.parse <<-IN
48
48
  @test Lorem
49
49
  Ipsum
50
50
  Test
@@ -55,7 +55,7 @@ class TestMultiline < MiniTest::Test
55
55
  end
56
56
 
57
57
  def test_lose_indent
58
- mh = MetaHeader.new <<-IN
58
+ mh = MetaHeader.parse <<-IN
59
59
  @test
60
60
  Hello
61
61
 
@@ -71,7 +71,7 @@ class TestMultiline < MiniTest::Test
71
71
  end
72
72
 
73
73
  def test_fewer_indent
74
- mh = MetaHeader.new <<-IN
74
+ mh = MetaHeader.parse <<-IN
75
75
  @test Lorem
76
76
  Ipsum
77
77
  Hello
@@ -83,7 +83,7 @@ class TestMultiline < MiniTest::Test
83
83
  end
84
84
 
85
85
  def test_extra_indent
86
- mh = MetaHeader.new <<-IN
86
+ mh = MetaHeader.parse <<-IN
87
87
  @test Lorem
88
88
  Ipsum
89
89
  Hello
@@ -95,7 +95,7 @@ class TestMultiline < MiniTest::Test
95
95
  end
96
96
 
97
97
  def test_do_not_reuse_indent
98
- mh = MetaHeader.new <<-IN
98
+ mh = MetaHeader.parse <<-IN
99
99
  @a
100
100
  Lorem
101
101
  Ipsum
@@ -110,7 +110,7 @@ class TestMultiline < MiniTest::Test
110
110
  end
111
111
 
112
112
  def test_sub_alternate_syntax
113
- mh = MetaHeader.new <<-IN
113
+ mh = MetaHeader.parse <<-IN
114
114
  @test Lorem
115
115
  Ipsum:
116
116
  Dolor: sit amet
@@ -121,7 +121,7 @@ class TestMultiline < MiniTest::Test
121
121
  end
122
122
 
123
123
  def test_explicit_boolean
124
- mh = MetaHeader.new <<-IN
124
+ mh = MetaHeader.parse <<-IN
125
125
  @test true
126
126
  test
127
127
  IN
@@ -130,7 +130,7 @@ class TestMultiline < MiniTest::Test
130
130
  end
131
131
 
132
132
  def test_empty_line_prefix
133
- mh = MetaHeader.new <<-IN
133
+ mh = MetaHeader.parse <<-IN
134
134
  --@test
135
135
  -- Hello
136
136
  --
@@ -145,7 +145,7 @@ class TestMultiline < MiniTest::Test
145
145
  end
146
146
 
147
147
  def test_empty_line_prefix_with_space
148
- mh = MetaHeader.new <<-IN
148
+ mh = MetaHeader.parse <<-IN
149
149
  -- @test
150
150
  -- Hello
151
151
  --
@@ -156,7 +156,7 @@ class TestMultiline < MiniTest::Test
156
156
  end
157
157
 
158
158
  def test_empty_line
159
- mh = MetaHeader.new <<-IN
159
+ mh = MetaHeader.parse <<-IN
160
160
  @test
161
161
  Hello
162
162
 
@@ -172,7 +172,7 @@ class TestMultiline < MiniTest::Test
172
172
  end
173
173
 
174
174
  def test_break_at_empty_line
175
- mh = MetaHeader.new <<-IN
175
+ mh = MetaHeader.parse <<-IN
176
176
  -- @hello world
177
177
 
178
178
  @chunky bacon
@@ -183,7 +183,7 @@ class TestMultiline < MiniTest::Test
183
183
  end
184
184
 
185
185
  def test_alternate_syntax
186
- mh = MetaHeader.new <<-IN
186
+ mh = MetaHeader.parse <<-IN
187
187
  -- Hello:
188
188
  -- World
189
189
  IN
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.new '@hello world'
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.new String.new
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.new "@hello"
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.new "@foo true\n@bar false"
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.new '-- @chunky bacon'
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.new <<-IN
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.new <<-IN
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.new <<-IN
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.new '@hello world '
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.new <<-IN
86
+ mh = MetaHeader.parse <<-IN
119
87
  -- @first
120
88
  --
121
89
  -- @second
@@ -124,7 +92,7 @@ class TestParser < MiniTest::Test
124
92
  refute_nil mh[:second]
125
93
  end
126
94
 
127
- def test_read_file
95
+ def test_from_file
128
96
  path = File.expand_path '../input/basic_tag', __FILE__
129
97
  mh = MetaHeader.from_file path
130
98
 
@@ -132,134 +100,137 @@ class TestParser < MiniTest::Test
132
100
  assert_equal 1, mh.size
133
101
  end
134
102
 
103
+ def test_read_file_stream
104
+ path = File.expand_path '../input/basic_tag', __FILE__
105
+ mh = MetaHeader.parse File.open(path)
106
+
107
+ assert_equal 'Hello World', mh[:test]
108
+ assert_equal 1, mh.size
109
+ end
110
+
111
+ def test_read_string_stream
112
+ stream = StringIO.new '@hello world'
113
+ MetaHeader.parse stream
114
+ end
115
+
135
116
  def test_to_hash
136
- mh = MetaHeader.new '@key value'
117
+ mh = MetaHeader.parse '@key value'
137
118
  assert_equal Hash[key: 'value'], mh.to_h
138
119
  end
139
120
 
140
121
  def test_alternate_syntax
141
- mh = MetaHeader.new 'Key Test: value'
122
+ mh = MetaHeader.parse 'Key Test: value'
142
123
  assert_equal Hash[key_test: 'value'], mh.to_h
143
124
  end
144
125
 
145
126
  def test_alternate_syntax_prefix
146
- mh = MetaHeader.new '-- Key Test: Value'
127
+ mh = MetaHeader.parse '-- Key Test: Value'
147
128
  assert_equal Hash[key_test: 'Value'], mh.to_h
148
129
  end
149
130
 
150
- def test_windows_newlines
151
- mh = MetaHeader.new "key: value\r\n@run_custom"
131
+ def test_crlf_newlines
132
+ mh = MetaHeader.parse "key: value\r\n@run_custom"
152
133
  assert_equal 'value', mh[:key]
153
- assert_equal "key: value\n@run_custom", CustomParser.input
134
+ assert_equal true, mh[:run_custom]
154
135
  end
155
136
 
156
137
  def test_alternate_syntax_trailing_space
157
- mh = MetaHeader.new ' Key Test : Value'
138
+ mh = MetaHeader.parse ' Key Test : Value'
158
139
  assert_equal Hash[key_test: 'Value'], mh.to_h
159
140
  end
160
141
 
161
142
  def test_alternate_syntax_compact
162
- mh = MetaHeader.new 'Key Test:Value'
143
+ mh = MetaHeader.parse 'Key Test:Value'
163
144
  assert_equal Hash[key_test: 'Value'], mh.to_h
164
145
  end
165
146
 
166
147
  def test_alternate_syntax_no_value
167
- mh = MetaHeader.new 'Key Test:'
148
+ mh = MetaHeader.parse 'Key Test:'
168
149
  assert_equal Hash.new, mh.to_h
169
150
  end
170
151
 
171
152
  def test_inspect
172
- mh = MetaHeader.new '@hello world'
153
+ mh = MetaHeader.parse '@hello world'
173
154
 
174
155
  hash = {hello: 'world'}
175
156
  assert_equal "#<MetaHeader #{hash.inspect}>", mh.inspect
176
157
  end
177
158
 
178
- def test_default_parser_implementation
179
- assert_raises NotImplementedError do
180
- MetaHeader::Parser.new.parse String.new
181
- end
182
- end
183
-
184
- def test_transform_from_text
185
- input = "@run_custom\nHello\n\nWorld".freeze
186
-
187
- mh = MetaHeader.new input
188
-
189
- assert CustomParser.called?
190
- assert_equal input, CustomParser.input
191
- assert_same mh, CustomParser.instance
192
- end
193
-
194
- def test_transform_from_file
195
- path = File.expand_path '../input/custom_parser', __FILE__
196
-
197
- mh = MetaHeader.from_file path
198
- assert_equal 'worldworld', mh[:hello]
199
-
200
- assert CustomParser.called?
201
- assert_equal File.read(path), CustomParser.input
202
- assert_same mh, CustomParser.instance
203
- end
204
-
205
159
  def test_has_tag
206
- mh = MetaHeader.new '@hello'
160
+ mh = MetaHeader.parse '@hello'
207
161
  assert_equal true, mh.has?(:hello)
208
162
  assert_equal false, mh.has?(:world)
209
163
  end
210
164
 
211
165
  def test_default_value
212
- mh = MetaHeader.new String.new
166
+ mh = MetaHeader.parse String.new
213
167
  assert_equal 'world', mh[:hello, 'world']
214
168
  end
215
169
 
216
170
  def test_delete
217
- mh = MetaHeader.new '@hello world'
171
+ mh = MetaHeader.new
172
+ mh[:hello] = 'world'
218
173
  assert mh.has?(:hello)
219
174
  mh.delete :hello
220
175
  refute mh.has?(:hello)
221
176
  end
222
177
 
223
- def test_construct_from_instance
224
- mh = MetaHeader.new '@hello world'
225
- assert_same mh, MetaHeader.parse(mh)
226
- assert_equal mh.to_h, MetaHeader.parse('@hello world').to_h
227
- end
228
-
229
178
  def test_alias
230
- mh = MetaHeader.new '@a 1'
179
+ mh = MetaHeader.new
180
+ mh[:a] = '1'
231
181
  mh.alias :a, :b
232
182
  refute mh.has?(:a)
233
183
  assert_equal '1', mh[:b]
234
-
235
- mh[:d] = '2'
236
- mh.alias :c, :d
237
- refute mh.has?(:c)
238
- assert_equal '2', mh[:d]
239
184
  end
240
185
 
241
186
  def test_alias_hash
242
- mh = MetaHeader.new "@a 1\n@b 2"
187
+ mh = MetaHeader.new
188
+ mh[:a] = '1'
189
+ mh[:b] = '2'
243
190
  mh.alias a: :c, b: :d
244
191
  assert_equal '1', mh[:c]
245
192
  assert_equal '2', mh[:d]
246
193
  end
247
194
 
248
195
  def test_alias_array
249
- mh = MetaHeader.new "@a 1\n@b 2"
196
+ mh = MetaHeader.new
197
+ mh[:a] = '1'
198
+ mh[:b] = '2'
250
199
  mh.alias [:a, :b, :c], :d
251
200
  assert [:a, :b, :c].none? {|t| mh.has? t }
252
201
  assert_equal '2', mh[:d]
253
202
  end
254
203
 
255
- def test_alias_invalid_args
256
- mh = MetaHeader.new "@a 1\n@b 2"
257
- assert_raises(ArgumentError) { mh.alias }
258
- assert_raises(ArgumentError) { mh.alias 1, 2, 3 }
259
- end
260
-
261
204
  def test_utf16_bom
262
- mh = MetaHeader.new "\xff\xfe@a b\n"
205
+ mh = MetaHeader.parse "\xff\xfe@a b\n"
263
206
  assert_equal 'b', mh[:a]
264
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
235
+ end
265
236
  end
@@ -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.new(input).validate(rules)
5
+ MetaHeader.parse(input).validate(rules)
6
6
  end
7
7
 
8
8
  def test_unknown_strict
9
- mh = MetaHeader.new "@hello\n@WORLD"
10
- mh.strict = true
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.new "@hello\n@world"
18
- refute mh.strict
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
- mh = MetaHeader.new "@hello"
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
- assert_nil actual
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
- actual = validate '@foobar', version: MetaHeader::REQUIRED, foobar: []
36
- assert_equal ["missing tag 'version'"], actual
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.new <<-IN
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
- actual = mh.validate :hello => MetaHeader::SINGLELINE
50
- assert_equal ["tag 'hello' must be singleline"], actual
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.new '@hello'
50
+ mh = MetaHeader.parse '@hello'
55
51
 
56
- actual = mh.validate :hello => [MetaHeader::VALUE]
57
- assert_equal ["missing value for tag 'hello'"], actual
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
- actual = validate '@hello world', :hello => /\d+/
62
- assert_equal ["invalid value for tag 'hello'"], actual
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.new '@hello'
62
+ mh = MetaHeader.parse '@hello'
67
63
 
68
- actual = mh.validate :hello => [/./]
69
- assert_equal ["invalid value for tag 'hello'"], actual
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
- actual = validate '@hello',
69
+ errors = validate '@hello',
74
70
  hello: Proc.new {|value| assert_equal true, value; nil }
75
- assert_nil actual
71
+ assert_empty errors
76
72
 
77
- actual = validate '@hello world',
73
+ errors = validate '@hello world',
78
74
  hello: Proc.new {|value| assert_equal 'world', value; nil }
79
- assert_nil actual
75
+ assert_empty errors
80
76
 
81
- actual = validate '@hello', hello: Proc.new {|value| 'Hello World!' }
82
- assert_equal ["invalid value for tag 'hello': Hello World!"], actual
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
- actual = validate 'HeLlO: world', hello: /\d+/
87
- assert_equal ["invalid value for tag 'HeLlO'"], actual
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
- actual = validate '@hello', hello: [/\d/, /\d/]
92
- assert_equal ["invalid value for tag 'hello'"], actual
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
- actual = validate "@hello true\n@hello world",
102
+ errors = validate "@hello true\n@hello world",
107
103
  hello: MetaHeader::BOOLEAN
108
- assert_equal ["tag 'hello' cannot have a value"], actual
104
+ assert_equal ["tag 'hello' cannot have a value"], errors
109
105
  end
110
106
 
111
107
  def test_alias
112
- mh = MetaHeader.new "@a"
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.3.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - cfillion
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-17 00:00:00.000000000 Z
11
+ date: 2020-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.14'
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: '1.14'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '12.0'
47
+ version: '13.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '12.0'
54
+ version: '13.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -68,7 +68,7 @@ dependencies:
68
68
  version: '0.13'
69
69
  description:
70
70
  email:
71
- - metaheader@cfillion.tk
71
+ - metaheader@cfillion.ca
72
72
  executables: []
73
73
  extensions: []
74
74
  extra_rdoc_files: []
@@ -109,8 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  requirements: []
112
- rubyforge_project:
113
- rubygems_version: 2.6.8
112
+ rubygems_version: 3.0.2
114
113
  signing_key:
115
114
  specification_version: 4
116
115
  summary: Parser for metadata headers in plain-text files