metaheader 1.3.1 → 2.0.0

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