nodaire 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/nodaire/base.rb +2 -2
- data/lib/nodaire/errors.rb +2 -0
- data/lib/nodaire/indental.rb +1 -1
- data/lib/nodaire/{api → indental}/indental.rb +1 -1
- data/lib/nodaire/indental/lexer.rb +65 -0
- data/lib/nodaire/indental/parser.rb +122 -0
- data/lib/nodaire/lexer.rb +24 -0
- data/lib/nodaire/{parsers/parser.rb → parser.rb} +2 -1
- data/lib/nodaire/tablatal.rb +1 -1
- data/lib/nodaire/tablatal/parser.rb +76 -0
- data/lib/nodaire/{api → tablatal}/tablatal.rb +1 -1
- data/lib/nodaire/util.rb +35 -0
- metadata +10 -7
- data/lib/nodaire/parsers/indental_parser.rb +0 -129
- data/lib/nodaire/parsers/tablatal_parser.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76e0ecf8fe992a52851c1c2f92aba32c4fa1e8c66a5442f7766521860624bdda
|
4
|
+
data.tar.gz: ecee4fc90ee93e2728177cce12b4d53383a0cbd6133cf8b4fc8a1e321a54b8e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47c3f94ae222712071c9a0cce903c9b400cfbc6ebda95fb3ff384b49cc42cade26cb3207bbaf298bfad7e5d60bfa6d402ddd48b0999f936b7cf5bcfa86040f52
|
7
|
+
data.tar.gz: f326c0167bcb4ba1af947e6689cb3059693d96116d83a65e6f1bb12f21801bf7d70f8898ac4c196a1d8abc01f1211314021d9a031b18d717fec6f8e1556e7a08
|
data/README.md
CHANGED
data/lib/nodaire/base.rb
CHANGED
data/lib/nodaire/errors.rb
CHANGED
data/lib/nodaire/indental.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lexer'
|
4
|
+
require_relative '../util'
|
5
|
+
|
6
|
+
class Nodaire::Indental
|
7
|
+
# @private
|
8
|
+
INDENT_CHARS_ERROR = 'Indented with non-space characters'
|
9
|
+
# @private
|
10
|
+
INDENT_LEVEL_ERROR = 'Unexpected indent level'
|
11
|
+
|
12
|
+
# @private
|
13
|
+
class Lexer < Nodaire::Lexer
|
14
|
+
Token = Struct.new(:type, :key, :value, :line_num)
|
15
|
+
|
16
|
+
def self.tokenize(source)
|
17
|
+
lines_with_number(strip_js_wrapper(source))
|
18
|
+
.reject { |line, _| line.match(/^\s*(;.*)?$/) }
|
19
|
+
.map { |line, num| token_for_line(line, num) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.token_for_line(line, num)
|
23
|
+
return error_token(INDENT_CHARS_ERROR, num) unless spaces_indent?(line)
|
24
|
+
|
25
|
+
case line.match(/^\s*/)[0].size
|
26
|
+
when 0 then category_token(line, num)
|
27
|
+
when 2 then key_or_list_token(line, num)
|
28
|
+
when 4 then list_item_token(line, num)
|
29
|
+
else error_token(INDENT_LEVEL_ERROR, num)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.spaces_indent?(line)
|
34
|
+
indent = line.match(/^\s*/)[0]
|
35
|
+
indent.match(/[^ ]/).nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.category_token(string, line_num)
|
39
|
+
Token.new :category, normalize(string), nil, line_num
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.key_or_list_token(string, line_num)
|
43
|
+
key_value = string.match(/^(.+?) :( .+)?$/)
|
44
|
+
|
45
|
+
if key_value
|
46
|
+
key, value = key_value.captures
|
47
|
+
Token.new :key_value, normalize(key), normalize(value), line_num
|
48
|
+
else
|
49
|
+
Token.new :list_name, normalize(string), nil, line_num
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.list_item_token(string, line_num)
|
54
|
+
Token.new :list_item, nil, normalize(string), line_num
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.error_token(message, line_num)
|
58
|
+
Token.new :error, nil, normalize(message), line_num
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.normalize(input)
|
62
|
+
Nodaire.squeeze(input)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lexer'
|
4
|
+
require_relative '../parser'
|
5
|
+
require_relative '../util'
|
6
|
+
|
7
|
+
class Nodaire::Indental
|
8
|
+
# @private
|
9
|
+
class Parser < Nodaire::Parser
|
10
|
+
attr_reader :data
|
11
|
+
|
12
|
+
def initialize(source, strict, options = {})
|
13
|
+
super(strict, options)
|
14
|
+
|
15
|
+
@symbolize_names = option(:symbolize_names, false)
|
16
|
+
@data = {}
|
17
|
+
@category = nil
|
18
|
+
|
19
|
+
parse! Nodaire::Indental::Lexer.tokenize(source)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
Category = Struct.new(:name, :list_id, keyword_init: true)
|
25
|
+
|
26
|
+
attr_accessor :category
|
27
|
+
attr_reader :symbolize_names
|
28
|
+
|
29
|
+
def parse!(tokens)
|
30
|
+
tokens.each { |token| parse_token!(normalize_token(token)) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_token!(token)
|
34
|
+
case token.type
|
35
|
+
when :category then parse_category! token
|
36
|
+
when :key_value then parse_key_value! token
|
37
|
+
when :list_name then parse_list_name! token
|
38
|
+
when :list_item then parse_list_item! token
|
39
|
+
when :error then parse_error! token
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_category!(token)
|
44
|
+
if data.include?(token.key)
|
45
|
+
oops! 'Duplicate category', token.line_num
|
46
|
+
self.category = nil
|
47
|
+
else
|
48
|
+
add_category! token
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_key_value!(token)
|
53
|
+
return oops!('No category specified', token.line_num) if category.nil?
|
54
|
+
|
55
|
+
if data[category.name].include?(token.key)
|
56
|
+
oops! 'Duplicate key', token.line_num
|
57
|
+
category.list_id = nil
|
58
|
+
else
|
59
|
+
add_key_value! token
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_list_name!(token)
|
64
|
+
return oops!('No category specified', token.line_num) if category.nil?
|
65
|
+
|
66
|
+
if data[category.name].include?(token.key)
|
67
|
+
oops! 'Duplicate key for list', token.line_num
|
68
|
+
category.list_id = nil
|
69
|
+
else
|
70
|
+
add_list! token
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_list_item!(token)
|
75
|
+
if category.nil? || category.list_id.nil?
|
76
|
+
oops! 'No list specified', token.line_num
|
77
|
+
else
|
78
|
+
add_list_item! token
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_category!(token)
|
83
|
+
self.category = Category.new(
|
84
|
+
name: token.key,
|
85
|
+
list_id: nil
|
86
|
+
)
|
87
|
+
data[token.key] = {}
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_key_value!(token)
|
91
|
+
data[category.name][token.key] = token.value
|
92
|
+
category.list_id = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_list!(token)
|
96
|
+
data[category.name][token.key] = []
|
97
|
+
category.list_id = token.key
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_list_item!(token)
|
101
|
+
data[category.name][category.list_id] << token.value
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_error!(token)
|
105
|
+
oops! token.value, token.line_num
|
106
|
+
end
|
107
|
+
|
108
|
+
def normalize_token(token)
|
109
|
+
token.tap { |t| t.key = normalize_key(t.key) }
|
110
|
+
end
|
111
|
+
|
112
|
+
def normalize_key(key)
|
113
|
+
return if key.nil?
|
114
|
+
|
115
|
+
if symbolize_names
|
116
|
+
Nodaire.symbolize(key)
|
117
|
+
else
|
118
|
+
key.upcase
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module Nodaire
|
6
|
+
# @private
|
7
|
+
class Lexer
|
8
|
+
JS_WRAPPER_REGEXP = %r{
|
9
|
+
^ \s*[^\n`]+ = [[:blank:]]* ` [[:blank:]]* \n
|
10
|
+
(.*\n)
|
11
|
+
[[:blank:]]* ` \s* $
|
12
|
+
}mx.freeze
|
13
|
+
|
14
|
+
def self.lines_with_number(source)
|
15
|
+
(source || '')
|
16
|
+
.split("\n").each_with_index
|
17
|
+
.map { |line, idx| [line, idx + 1] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.strip_js_wrapper(source)
|
21
|
+
(source || '').sub(JS_WRAPPER_REGEXP, '\1')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/nodaire/tablatal.rb
CHANGED
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../parser'
|
4
|
+
require_relative '../util'
|
5
|
+
|
6
|
+
class Nodaire::Tablatal
|
7
|
+
# @private
|
8
|
+
class Parser < Nodaire::Parser
|
9
|
+
attr_reader :data
|
10
|
+
|
11
|
+
def initialize(string, strict, options = {})
|
12
|
+
super(strict, options)
|
13
|
+
|
14
|
+
@symbolize_names = option(:symbolize_names, false)
|
15
|
+
@data = []
|
16
|
+
@keys = []
|
17
|
+
|
18
|
+
parse! string
|
19
|
+
end
|
20
|
+
|
21
|
+
def keys
|
22
|
+
@keys.map(&:name)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :symbolize_names
|
28
|
+
|
29
|
+
Key = Struct.new(:name, :range, keyword_init: true)
|
30
|
+
|
31
|
+
def parse!(string)
|
32
|
+
lines = (string || '').strip.split("\n")
|
33
|
+
.reject { |line| line.match(/^\s*(;.*)?$/) }
|
34
|
+
return if lines.empty?
|
35
|
+
|
36
|
+
@keys = filter_keys(make_keys(lines.shift.scan(/(\S+\s*)/).flatten))
|
37
|
+
@data = lines.map { |line| make_line(line) }.compact
|
38
|
+
end
|
39
|
+
|
40
|
+
def make_keys(segs)
|
41
|
+
keys = []
|
42
|
+
start = 0
|
43
|
+
segs.each_with_index do |seg, idx|
|
44
|
+
len = seg.size if idx < segs.size - 1
|
45
|
+
range_end = len ? start + len - 1 : -1
|
46
|
+
keys << Key.new(name: normalize_key(seg), range: start..range_end)
|
47
|
+
start += len if len
|
48
|
+
end
|
49
|
+
keys
|
50
|
+
end
|
51
|
+
|
52
|
+
def filter_keys(keys)
|
53
|
+
result = []
|
54
|
+
keys.each do |key|
|
55
|
+
if result.any? { |k| k.name == key.name }
|
56
|
+
oops! "Duplicate key #{key.name}", 1
|
57
|
+
else
|
58
|
+
result << key
|
59
|
+
end
|
60
|
+
end
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
def make_line(line)
|
65
|
+
@keys.map { |key| [key.name, Nodaire.squeeze(line[key.range])] }.to_h
|
66
|
+
end
|
67
|
+
|
68
|
+
def normalize_key(string)
|
69
|
+
if symbolize_names
|
70
|
+
Nodaire.symbolize(string)
|
71
|
+
else
|
72
|
+
Nodaire.squeeze(string).upcase
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/nodaire/util.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module Nodaire
|
6
|
+
##
|
7
|
+
# Normalize the whitespace in a string.
|
8
|
+
#
|
9
|
+
# This strips the string and replaces each sequence of whitespace with
|
10
|
+
# a single space.
|
11
|
+
#
|
12
|
+
# @param [String] string
|
13
|
+
#
|
14
|
+
# @since 0.4.0
|
15
|
+
# @return [String]
|
16
|
+
#
|
17
|
+
def self.squeeze(string)
|
18
|
+
(string || '').gsub(/\s+/, ' ').strip
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Convert a string into a normalized symbol.
|
23
|
+
#
|
24
|
+
# This converts to lower case and replaces each sequence of non-alphanumeric
|
25
|
+
# characters with an underscore.
|
26
|
+
#
|
27
|
+
# @param [String] string
|
28
|
+
#
|
29
|
+
# @since 0.4.0
|
30
|
+
# @return [Symbol]
|
31
|
+
#
|
32
|
+
def self.symbolize(string)
|
33
|
+
squeeze(string).downcase.gsub(/[^a-z0-9]+/, '_').to_sym
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nodaire
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Liam Cooke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Nodaire is a collection of text file parsers.
|
14
14
|
email: nodaire@liamcooke.com
|
@@ -19,15 +19,18 @@ files:
|
|
19
19
|
- LICENSE
|
20
20
|
- README.md
|
21
21
|
- lib/nodaire.rb
|
22
|
-
- lib/nodaire/api/indental.rb
|
23
|
-
- lib/nodaire/api/tablatal.rb
|
24
22
|
- lib/nodaire/base.rb
|
25
23
|
- lib/nodaire/errors.rb
|
26
24
|
- lib/nodaire/indental.rb
|
27
|
-
- lib/nodaire/
|
28
|
-
- lib/nodaire/
|
29
|
-
- lib/nodaire/
|
25
|
+
- lib/nodaire/indental/indental.rb
|
26
|
+
- lib/nodaire/indental/lexer.rb
|
27
|
+
- lib/nodaire/indental/parser.rb
|
28
|
+
- lib/nodaire/lexer.rb
|
29
|
+
- lib/nodaire/parser.rb
|
30
30
|
- lib/nodaire/tablatal.rb
|
31
|
+
- lib/nodaire/tablatal/parser.rb
|
32
|
+
- lib/nodaire/tablatal/tablatal.rb
|
33
|
+
- lib/nodaire/util.rb
|
31
34
|
homepage: https://github.com/slisne/nodaire
|
32
35
|
licenses:
|
33
36
|
- MIT
|
@@ -1,129 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'parser'
|
4
|
-
|
5
|
-
class Nodaire::Indental
|
6
|
-
# @private
|
7
|
-
class Parser < Nodaire::Parser
|
8
|
-
attr_reader :data
|
9
|
-
|
10
|
-
def initialize(string, strict, options = {})
|
11
|
-
super(strict, options)
|
12
|
-
|
13
|
-
@symbolize_names = option(:symbolize_names, false)
|
14
|
-
@data = {}
|
15
|
-
@category_ids = {}
|
16
|
-
@category = nil
|
17
|
-
|
18
|
-
parse! string
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
Category = Struct.new(:name, :key_ids, :list_id, keyword_init: true)
|
24
|
-
|
25
|
-
attr_accessor :category
|
26
|
-
attr_reader :symbolize_names, :category_ids
|
27
|
-
|
28
|
-
def parse!(string)
|
29
|
-
lines = lines_to_parse(string)
|
30
|
-
lines = lines[1...-1] if js_wrapper?(lines)
|
31
|
-
lines.each { |line, num| parse_line! line, num }
|
32
|
-
end
|
33
|
-
|
34
|
-
def lines_to_parse(string)
|
35
|
-
(string || '')
|
36
|
-
.split("\n").each_with_index
|
37
|
-
.reject { |line, _| line.match(/^\s*(;.*)?$/) }
|
38
|
-
.map { |line, idx| [line, idx + 1] }
|
39
|
-
end
|
40
|
-
|
41
|
-
def js_wrapper?(lines)
|
42
|
-
!lines.empty? &&
|
43
|
-
lines.first[0].match(/=\s*`\s*$/) &&
|
44
|
-
lines.last[0].strip == '`'
|
45
|
-
end
|
46
|
-
|
47
|
-
def parse_line!(line, num)
|
48
|
-
case line.match(/^\s*/)[0].size
|
49
|
-
when 0 then parse_category! line.strip, num
|
50
|
-
when 2 then parse_key_or_list! line.strip, num
|
51
|
-
when 4 then parse_list_item! line.strip, num
|
52
|
-
else oops! 'Unexpected indent', num
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def parse_category!(cat, num)
|
57
|
-
id = normalize_sym(cat)
|
58
|
-
|
59
|
-
if category_ids.include?(id)
|
60
|
-
oops! 'Duplicate category', num
|
61
|
-
self.category = nil
|
62
|
-
else
|
63
|
-
self.category = Category.new(
|
64
|
-
name: symbolize_names ? id : normalize_text(cat),
|
65
|
-
key_ids: {},
|
66
|
-
list_id: nil
|
67
|
-
)
|
68
|
-
data[category.name] = {}
|
69
|
-
category_ids[id] = category.name
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def parse_key_or_list!(line, num)
|
74
|
-
return oops!('No category specified', num) if category.nil?
|
75
|
-
|
76
|
-
if line.include?(' : ')
|
77
|
-
key, value = line.split(' : ', 2)
|
78
|
-
parse_key_value!(key, value, num)
|
79
|
-
else
|
80
|
-
parse_list!(line, num)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def parse_key_value!(key, value, num)
|
85
|
-
id = normalize_sym(key)
|
86
|
-
key_name = symbolize_names ? id : normalize_text(key)
|
87
|
-
|
88
|
-
if category.key_ids.include?(id)
|
89
|
-
oops! 'Duplicate key', num
|
90
|
-
else
|
91
|
-
data[category.name][key_name] = normalize_text(value)
|
92
|
-
category.key_ids[id] = key_name
|
93
|
-
end
|
94
|
-
|
95
|
-
category.list_id = nil
|
96
|
-
end
|
97
|
-
|
98
|
-
def parse_list!(key, num)
|
99
|
-
id = normalize_sym(key)
|
100
|
-
list_name = symbolize_names ? id : normalize_text(key)
|
101
|
-
|
102
|
-
if category.key_ids.include?(id)
|
103
|
-
oops! 'Duplicate key for list', num
|
104
|
-
category.list_id = nil
|
105
|
-
else
|
106
|
-
data[category.name][list_name] = []
|
107
|
-
category.key_ids[id] = list_name
|
108
|
-
category.list_id = id
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def parse_list_item!(item, num)
|
113
|
-
if category.nil? || category.list_id.nil?
|
114
|
-
oops! 'No list specified', num
|
115
|
-
else
|
116
|
-
list_name = category.key_ids[category.list_id]
|
117
|
-
data[category.name][list_name] << normalize_text(item)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def normalize_text(string)
|
122
|
-
string.split.join(' ')
|
123
|
-
end
|
124
|
-
|
125
|
-
def normalize_sym(key)
|
126
|
-
key.downcase.gsub(/[\s_-]+/, ' ').split.join('_').to_sym
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'parser'
|
4
|
-
|
5
|
-
class Nodaire::Tablatal
|
6
|
-
# @private
|
7
|
-
class Parser < Nodaire::Parser
|
8
|
-
attr_reader :data
|
9
|
-
|
10
|
-
def initialize(string, strict, options = {})
|
11
|
-
super(strict, options)
|
12
|
-
|
13
|
-
@symbolize_names = option(:symbolize_names, false)
|
14
|
-
|
15
|
-
@data = []
|
16
|
-
@keys = []
|
17
|
-
@key_ids = {}
|
18
|
-
|
19
|
-
parse! string
|
20
|
-
end
|
21
|
-
|
22
|
-
def keys
|
23
|
-
@keys.map(&:name)
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
attr_reader :symbolize_names, :key_ids
|
29
|
-
|
30
|
-
Key = Struct.new(:name, :range, keyword_init: true)
|
31
|
-
|
32
|
-
def parse!(string)
|
33
|
-
lines = (string || '').strip.split("\n")
|
34
|
-
.reject { |line| line.match(/^\s*(;.*)?$/) }
|
35
|
-
return if lines.empty?
|
36
|
-
|
37
|
-
@keys = make_keys(lines.shift.scan(/(\S+\s*)/).flatten)
|
38
|
-
@data = lines.map { |line| make_line(line) }.compact
|
39
|
-
end
|
40
|
-
|
41
|
-
def make_keys(segs)
|
42
|
-
[].tap do |keys|
|
43
|
-
start = 0
|
44
|
-
segs.each_with_index do |seg, idx|
|
45
|
-
len = seg.size if idx < segs.size - 1
|
46
|
-
id = normalize_sym(seg)
|
47
|
-
key_name = symbolize_names ? id : normalize_text(seg)
|
48
|
-
|
49
|
-
if key_ids.include?(id)
|
50
|
-
oops! "Duplicate key #{key_name}", 1
|
51
|
-
else
|
52
|
-
range_end = len ? start + len - 1 : -1
|
53
|
-
key_ids[id] = key_name
|
54
|
-
keys << Key.new(name: key_name, range: start..range_end)
|
55
|
-
end
|
56
|
-
|
57
|
-
start += len if len
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def make_line(line)
|
63
|
-
@keys.map { |key| [key.name, normalize_text(line[key.range])] }.to_h
|
64
|
-
end
|
65
|
-
|
66
|
-
def normalize_text(string)
|
67
|
-
string ? string.split.join(' ') : ''
|
68
|
-
end
|
69
|
-
|
70
|
-
def normalize_sym(key)
|
71
|
-
key.downcase.gsub(/[_-]+/, ' ').split.join('_').to_sym
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|