nodaire 0.3.0 → 0.4.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 +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
|