archieml 0.1.1 → 0.2.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/.gitignore +2 -0
- data/.gitmodules +4 -0
- data/README.md +2 -1
- data/lib/archieml/loader.rb +81 -38
- data/lib/archieml/version.rb +1 -1
- data/spec/lib/archieml/loader_spec.rb +12 -564
- data/spec/spec_helper.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1769d7385a90df46fb88685e1be8cef959943c1
|
4
|
+
data.tar.gz: c92bea5829cc1729197ee1035f7398256b3205c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 045a8a57c8676be985fd6194c708f09eb157b93ea97ef032d3a88bfe0e4d6431f94924c25e66e0e9265bef9fd54ce07a47a3a2bdbc87e94bbee8d395abc81d00
|
7
|
+
data.tar.gz: 7bbd0d896a7ae3348814ab36ec6cbe795739ab9543b1971542671d21b629b26b1c15f32b4e7594ff83898c80dec090e3b8bd12e317b92a1023d0748d9742b19d
|
data/.gitignore
CHANGED
data/.gitmodules
ADDED
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Parse Archie Markup Language (ArchieML) documents into Ruby Hashes.
|
|
4
4
|
|
5
5
|
Read about the ArchieML specification at [archieml.org](http://archieml.org).
|
6
6
|
|
7
|
-
The current version is `v0.
|
7
|
+
The current version is `v0.2.0`.
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -173,5 +173,6 @@ There is a full test suite using rspec. `bundle install`, and then `rspec` to ex
|
|
173
173
|
|
174
174
|
## Changelog
|
175
175
|
|
176
|
+
* `0.2.0` - Updated to support an updated ArchieML spec: [2015-05-09](http://archieml.org/spec/1.0/CR-20150509.html). Adds support for nested arrays.
|
176
177
|
* `0.1.1` - More consistent handling of newlines. Fixed bugs around detecting the scope of multi-line values.
|
177
178
|
* `0.1.0` - Initial release supporting the first version of the ArchieML spec, published [2015-03-06](http://archieml.org/spec/1.0/CR-20150306.html).
|
data/lib/archieml/loader.rb
CHANGED
@@ -7,29 +7,36 @@ module Archieml
|
|
7
7
|
ARRAY_ELEMENT = /^\s*\*[ \t\r]*(.*(?:\n|\r|$))/
|
8
8
|
SCOPE_PATTERN = /^\s*(\[|\{)[ \t\r]*([A-Za-z0-9\-_\.]*)[ \t\r]*(?:\]|\}).*?(\n|\r|$)/
|
9
9
|
|
10
|
-
def initialize
|
10
|
+
def initialize(options = {})
|
11
11
|
@data = @scope = {}
|
12
12
|
|
13
|
-
@
|
13
|
+
@stack = []
|
14
|
+
@stack_scope = nil
|
15
|
+
|
16
|
+
@buffer_scope = @buffer_key = nil
|
14
17
|
@buffer_string = ''
|
15
18
|
|
16
|
-
@is_skipping
|
19
|
+
@is_skipping = false
|
17
20
|
@done_parsing = false
|
18
21
|
|
19
|
-
|
22
|
+
@default_options = {
|
23
|
+
comments: false
|
24
|
+
}.merge(options)
|
20
25
|
end
|
21
26
|
|
22
|
-
def load(stream)
|
27
|
+
def load(stream, options = {})
|
28
|
+
@options = @default_options.merge(options)
|
29
|
+
|
23
30
|
stream.each_line do |line|
|
24
31
|
return @data if @done_parsing
|
25
32
|
|
26
33
|
if match = line.match(COMMAND_KEY)
|
27
34
|
self.parse_command_key(match[1].downcase)
|
28
35
|
|
29
|
-
elsif !@is_skipping && (match = line.match(START_KEY)) && (!@
|
36
|
+
elsif !@is_skipping && (match = line.match(START_KEY)) && (!@stack_scope || @stack_scope[:array_type] != :simple)
|
30
37
|
self.parse_start_key(match[1], match[2] || '')
|
31
38
|
|
32
|
-
elsif !@is_skipping && (match = line.match(ARRAY_ELEMENT)) && @
|
39
|
+
elsif !@is_skipping && (match = line.match(ARRAY_ELEMENT)) && @stack_scope && @stack_scope[:array_type] != :complex
|
33
40
|
self.parse_array_element(match[1])
|
34
41
|
|
35
42
|
elsif !@is_skipping && match = line.match(SCOPE_PATTERN)
|
@@ -47,37 +54,27 @@ module Archieml
|
|
47
54
|
def parse_start_key(key, rest_of_line)
|
48
55
|
self.flush_buffer!
|
49
56
|
|
50
|
-
|
51
|
-
@array_type ||= 'complex'
|
52
|
-
|
53
|
-
# Ignore complex keys inside simple arrays
|
54
|
-
return if @array_type == 'simple'
|
55
|
-
|
56
|
-
if [nil, key].include?(@array_first_key)
|
57
|
-
@array << (@scope = {})
|
58
|
-
end
|
59
|
-
|
60
|
-
@array_first_key ||= key
|
61
|
-
end
|
57
|
+
self.increment_array_element(key)
|
62
58
|
|
63
59
|
@buffer_key = key
|
64
60
|
@buffer_string = rest_of_line
|
65
61
|
|
66
62
|
self.flush_buffer_into(key, replace: true)
|
63
|
+
@buffer_key = key
|
67
64
|
end
|
68
65
|
|
69
66
|
def parse_array_element(value)
|
70
67
|
self.flush_buffer!
|
71
68
|
|
72
|
-
@array_type ||=
|
69
|
+
@stack_scope[:array_type] ||= :simple
|
73
70
|
|
74
71
|
# Ignore simple array elements inside complex arrays
|
75
|
-
return if @array_type ==
|
72
|
+
return if @stack_scope[:array_type] == :complex
|
76
73
|
|
77
|
-
@array << ''
|
78
|
-
@buffer_key = @array
|
74
|
+
@stack_scope[:array] << ''
|
79
75
|
@buffer_string = value
|
80
|
-
self.flush_buffer_into(@array, replace: true)
|
76
|
+
self.flush_buffer_into(@stack_scope[:array], replace: true)
|
77
|
+
@buffer_key = @stack_scope[:array]
|
81
78
|
end
|
82
79
|
|
83
80
|
def parse_command_key(command)
|
@@ -99,39 +96,86 @@ module Archieml
|
|
99
96
|
when "endskip"
|
100
97
|
@is_skipping = false
|
101
98
|
end
|
99
|
+
|
100
|
+
self.flush_buffer!
|
102
101
|
end
|
103
102
|
|
104
103
|
def parse_scope(scope_type, scope_key)
|
105
104
|
self.flush_buffer!
|
106
|
-
self.flush_scope!
|
107
105
|
|
108
106
|
if scope_key == ''
|
109
|
-
|
107
|
+
case scope_type
|
108
|
+
when '{'
|
109
|
+
@scope = @data
|
110
|
+
@stack_scope = nil
|
111
|
+
@stack = []
|
112
|
+
when '['
|
113
|
+
# Move up a level
|
114
|
+
if last_stack_item = @stack.pop
|
115
|
+
@scope = last_stack_item[:scope] || @data
|
116
|
+
@stack_scope = @stack.last
|
117
|
+
end
|
118
|
+
end
|
110
119
|
|
111
120
|
elsif %w([ {).include?(scope_type)
|
121
|
+
nesting = false
|
112
122
|
key_scope = @data
|
123
|
+
|
124
|
+
if scope_key.match(/^\./)
|
125
|
+
scope_key = scope_key[1..-1]
|
126
|
+
self.increment_array_element(scope_key)
|
127
|
+
nesting = true
|
128
|
+
key_scope = @scope if @stack_scope
|
129
|
+
end
|
130
|
+
|
113
131
|
key_bits = scope_key.split('.')
|
114
132
|
key_bits[0...-1].each do |bit|
|
115
133
|
key_scope = key_scope[bit] ||= {}
|
116
134
|
end
|
117
135
|
|
118
136
|
if scope_type == '['
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
137
|
+
stack_scope_item = {
|
138
|
+
array: key_scope[key_bits.last] = [],
|
139
|
+
array_type: nil,
|
140
|
+
array_first_key: nil,
|
141
|
+
scope: @scope
|
142
|
+
}
|
143
|
+
|
144
|
+
if nesting
|
145
|
+
@stack << stack_scope_item
|
146
|
+
else
|
147
|
+
@stack = [stack_scope_item]
|
124
148
|
end
|
149
|
+
@stack_scope = @stack.last
|
125
150
|
|
126
151
|
elsif scope_type == '{'
|
127
|
-
@scope = key_scope[key_bits.last]
|
152
|
+
@scope = key_scope[key_bits.last] = key_scope[key_bits.last].is_a?(Hash) ? key_scope[key_bits.last] : {}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def increment_array_element(key)
|
158
|
+
# Special handling for arrays. If this is the start of the array, remember
|
159
|
+
# which key was encountered first. If this is a duplicate encounter of
|
160
|
+
# that key, start a new object.
|
161
|
+
|
162
|
+
if @stack_scope && @stack_scope[:array]
|
163
|
+
# If we're within a simple array, ignore
|
164
|
+
@stack_scope[:array_type] ||= :complex
|
165
|
+
return if @stack_scope[:array_type] == :simple
|
166
|
+
|
167
|
+
# array_first_key may be either another key, or nil
|
168
|
+
if @stack_scope[:array_first_key] == nil || @stack_scope[:array_first_key] == key
|
169
|
+
@stack_scope[:array] << (@scope = {})
|
128
170
|
end
|
171
|
+
@stack_scope[:array_first_key] ||= key
|
129
172
|
end
|
130
173
|
end
|
131
174
|
|
132
175
|
def flush_buffer!
|
133
176
|
result = @buffer_string.dup
|
134
177
|
@buffer_string = ''
|
178
|
+
@buffer_key = nil
|
135
179
|
return result
|
136
180
|
end
|
137
181
|
|
@@ -163,10 +207,6 @@ module Archieml
|
|
163
207
|
end
|
164
208
|
end
|
165
209
|
|
166
|
-
def flush_scope!
|
167
|
-
@array = @array_type = @array_first_key = @buffer_key = nil
|
168
|
-
end
|
169
|
-
|
170
210
|
# type can be either :replace or :append.
|
171
211
|
# If it's :replace, then the string is assumed to be the first line of a
|
172
212
|
# value, and no escaping takes place.
|
@@ -174,8 +214,11 @@ module Archieml
|
|
174
214
|
# by prepending the line with a backslash.
|
175
215
|
# (:, [, {, *, \) surrounding the first token of any line.
|
176
216
|
def format_value(value, type)
|
177
|
-
|
178
|
-
|
217
|
+
# Deprecated
|
218
|
+
if @options[:comments]
|
219
|
+
value.gsub!(/(?:^\\)?\[[^\[\]\n\r]*\](?!\])/, '') # remove comments
|
220
|
+
value.gsub!(/\[\[([^\[\]\n\r]*)\]\]/, '[\1]') # [[]] => []
|
221
|
+
end
|
179
222
|
|
180
223
|
if type == :append
|
181
224
|
value.gsub!(/^(\s*)\\/, '\1')
|
data/lib/archieml/version.rb
CHANGED
@@ -1,573 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Archieml::Loader do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
allow(@loader).to receive(:parse_start_key).with(any_args).and_call_original
|
8
|
-
allow(@loader).to receive(:parse_command_key).with(any_args).and_call_original
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "parsing values" do
|
12
|
-
it "parses key value pairs" do
|
13
|
-
@loader.load("key:value")['key'].should == 'value'
|
14
|
-
end
|
15
|
-
it "ignores spaces on either side of the key" do
|
16
|
-
@loader.load(" key :value")['key'].should == 'value'
|
17
|
-
end
|
18
|
-
it "ignores tabs on either side of the key" do
|
19
|
-
@loader.load("\t\tkey\t\t:value")['key'].should == 'value'
|
20
|
-
end
|
21
|
-
it "ignores spaces on either side of the value" do
|
22
|
-
@loader.load("key: value ")['key'].should == 'value'
|
23
|
-
end
|
24
|
-
it "ignores tabs on either side of the value" do
|
25
|
-
@loader.load("key:\t\tvalue\t\t")['key'].should == 'value'
|
26
|
-
end
|
27
|
-
it "dupliate keys are assigned the last given value" do
|
28
|
-
@loader.load("key:value\nkey:newvalue")['key'].should == 'newvalue'
|
29
|
-
end
|
30
|
-
it "allows non-letter characters at the start of values" do
|
31
|
-
@loader.load("key::value")['key'].should == ':value'
|
32
|
-
end
|
33
|
-
it "keys are case sensitive" do
|
34
|
-
@loader.load("key:value\nKey:Value").keys.should == ['key', 'Key']
|
35
|
-
end
|
36
|
-
it "non-keys don't affect parsing" do
|
37
|
-
@loader.load("other stuff\nkey:value\nother stuff")['key'].should == 'value'
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe "valid keys" do
|
42
|
-
|
43
|
-
it "letters, numbers, dashes and underscores are valid key components" do
|
44
|
-
@loader.load("a-_1:value")['a-_1'].should == 'value'
|
45
|
-
end
|
46
|
-
it "spaces are not allowed in keys" do
|
47
|
-
@loader.load("k ey:value").keys.length.should == 0
|
48
|
-
end
|
49
|
-
it "symbols are not allowed in keys" do
|
50
|
-
@loader.load("k&ey:value").keys.length.should == 0
|
51
|
-
end
|
52
|
-
it "keys can be nested using dot-notation" do
|
53
|
-
@loader.load("scope.key:value")['scope']['key'].should == 'value'
|
54
|
-
end
|
55
|
-
it "earlier keys within scopes aren't deleted when using dot-notation" do
|
56
|
-
@loader.load("scope.key:value\nscope.otherkey:value")['scope']['key'].should == 'value'
|
57
|
-
@loader.load("scope.key:value\nscope.otherkey:value")['scope']['otherkey'].should == 'value'
|
58
|
-
end
|
59
|
-
it "the value of key that used to be a string should be replaced with an object if necessary" do
|
60
|
-
@loader.load("scope.level:value\nscope.level.level:value")['scope']['level']['level'].should == 'value'
|
61
|
-
end
|
62
|
-
it "the value of key that used to be a parent object should be replaced with a string if necessary" do
|
63
|
-
@loader.load("scope.level.level:value\nscope.level:value")['scope']['level'].should == 'value'
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
describe "valid values" do
|
69
|
-
|
70
|
-
it "HTML is allowed" do
|
71
|
-
@loader.load("key:<strong>value</strong>")['key'].should == '<strong>value</strong>'
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
describe "skip" do
|
77
|
-
|
78
|
-
it "ignores spaces on either side of :skip" do
|
79
|
-
expect(@loader).to receive(:parse_command_key).with('skip').once
|
80
|
-
@loader.load(" :skip \nkey:value\n:endskip").keys.length.should == 0
|
81
|
-
end
|
82
|
-
it "ignores tabs on either side of :skip" do
|
83
|
-
expect(@loader).to receive(:parse_command_key).with('skip').once
|
84
|
-
@loader.load("\t\t:skip\t\t\nkey:value\n:endskip").keys.length.should == 0
|
85
|
-
end
|
86
|
-
it "ignores spaces on either side of :endskip" do
|
87
|
-
expect(@loader).to receive(:parse_command_key).with('endskip').once
|
88
|
-
@loader.load(":skip\nkey:value\n :endskip ").keys.length.should == 0
|
89
|
-
end
|
90
|
-
it "ignores tabs on either side of :endskip" do
|
91
|
-
expect(@loader).to receive(:parse_command_key).with('endskip').once
|
92
|
-
@loader.load(":skip\nkey:value\n\t\t:endskip\t\t").keys.length.should == 0
|
93
|
-
end
|
94
|
-
it "starts parsing again after :endskip" do
|
95
|
-
expect(@loader).to receive(:parse_start_key).with('key', 'value').once
|
96
|
-
@loader.load(":skip\n:endskip\nkey:value").keys.length.should == 1
|
97
|
-
end
|
98
|
-
it ":skip and :endskip are case insensitive" do
|
99
|
-
expect(@loader).to receive(:parse_command_key).with('skip').once
|
100
|
-
expect(@loader).to receive(:parse_command_key).with('endskip').once
|
101
|
-
@loader.load(":sKiP\nkey:value\n:eNdSkIp").keys.length.should == 0
|
102
|
-
end
|
103
|
-
it "parse :skip as a special command even if more is appended to word" do
|
104
|
-
expect(@loader).to receive(:parse_command_key).with('skip')
|
105
|
-
@loader.load(":skipthis\nkey:value\n:endskip").keys.length.should == 0
|
106
|
-
end
|
107
|
-
it "ignores all content on line after :skip + space" do
|
108
|
-
expect(@loader).to receive(:parse_command_key).with('skip').once
|
109
|
-
expect(@loader).to_not receive(:parse_start_key).with('key', 'value')
|
110
|
-
@loader.load(":skip this text \nkey:value\n:endskip").keys.length.should == 0
|
111
|
-
end
|
112
|
-
it "ignores all content on line after :skip + tab" do
|
113
|
-
expect(@loader).to receive(:parse_command_key).with('skip').once
|
114
|
-
expect(@loader).to_not receive(:parse_start_key).with('key', 'value')
|
115
|
-
@loader.load(":skip\tthis text\t\t\nkey:value\n:endskip").keys.length.should == 0
|
116
|
-
end
|
117
|
-
it "parse :endskip as a special command even if more is appended to word" do
|
118
|
-
expect(@loader).to receive(:parse_command_key).with('endskip')
|
119
|
-
@loader.load(":skip\n:endskiptheabove\nkey:value").keys.length.should == 1
|
120
|
-
end
|
121
|
-
it "ignores all content on line after :endskip + space" do
|
122
|
-
expect(@loader).to receive(:parse_command_key).with('endskip').once
|
123
|
-
expect(@loader).to receive(:parse_start_key).with('key', 'value').once
|
124
|
-
@loader.load(":skip\n:endskip the above\nkey:value").keys.length.should == 1
|
125
|
-
end
|
126
|
-
it "ignores all content on line after :endskip + tab" do
|
127
|
-
expect(@loader).to receive(:parse_command_key).with('endskip').once
|
128
|
-
expect(@loader).to receive(:parse_start_key).with('key', 'value').once
|
129
|
-
@loader.load(":skip\n:endskip\tthe above\nkey:value").keys.length.should == 1
|
130
|
-
end
|
131
|
-
it "does not parse :end as an :endskip" do
|
132
|
-
expect(@loader).to_not receive(:parse_command_key).with('endskip')
|
133
|
-
@loader.load(":skip\n:end\tthe above\nkey:value").keys.length.should == 0
|
134
|
-
end
|
135
|
-
it "ignores keys within a skip block" do
|
136
|
-
expect(@loader).to_not receive(:parse_start_key).with('other', 'value')
|
137
|
-
@loader.load("key1:value1\n:skip\nother:value\n\n:endskip\n\nkey2:value2").keys.should == ['key1', 'key2']
|
138
|
-
end
|
139
|
-
|
140
|
-
end
|
141
|
-
|
142
|
-
describe "ignore" do
|
143
|
-
|
144
|
-
it "text before ':ignore' should be included" do
|
145
|
-
@loader.load("key:value\n:ignore")['key'].should == 'value'
|
146
|
-
end
|
147
|
-
it "text after ':ignore' should be ignored" do
|
148
|
-
expect(@loader).to_not receive(:parse_start_key)
|
149
|
-
@loader.load(":ignore\nkey:value").keys.length.should == 0
|
150
|
-
end
|
151
|
-
it "':ignore' is case insensitive" do
|
152
|
-
expect(@loader).to receive(:parse_command_key).with('ignore').once
|
153
|
-
@loader.load(":iGnOrE\nkey:value").keys.length.should == 0
|
154
|
-
end
|
155
|
-
it "ignores spaces on either side of :ignore" do
|
156
|
-
expect(@loader).to receive(:parse_command_key).with('ignore').once
|
157
|
-
@loader.load(":iGnOrE\nkey:value").keys.length.should == 0
|
158
|
-
@loader.load(" :ignore \nkey:value")
|
159
|
-
end
|
160
|
-
it "ignores tabs on either side of :ignore" do
|
161
|
-
expect(@loader).to receive(:parse_command_key).with('ignore').once
|
162
|
-
@loader.load(":iGnOrE\nkey:value").keys.length.should == 0
|
163
|
-
@loader.load("\t\t:ignore\t\t\nkey:value")
|
164
|
-
end
|
165
|
-
it "parses :ignore as a special command even if more is appended to word" do
|
166
|
-
expect(@loader).to receive(:parse_command_key).with('ignore')
|
167
|
-
@loader.load(":ignorethis\nkey:value").keys.length.should == 0
|
168
|
-
end
|
169
|
-
it "ignores all content on line after :ignore + space" do
|
170
|
-
expect(@loader).to receive(:parse_command_key).with('ignore').once
|
171
|
-
@loader.load(":iGnOrE\nkey:value").keys.length.should == 0
|
172
|
-
@loader.load(":ignore the below\nkey:value")
|
173
|
-
end
|
174
|
-
it "ignores all content on line after :ignore + tab" do
|
175
|
-
expect(@loader).to receive(:parse_command_key).with('ignore').once
|
176
|
-
@loader.load(":iGnOrE\nkey:value").keys.length.should == 0
|
177
|
-
@loader.load(":ignore\tthe below\nkey:value")
|
178
|
-
end
|
179
|
-
|
180
|
-
end
|
181
|
-
|
182
|
-
describe "multi line values" do
|
183
|
-
|
184
|
-
it "adds additional lines to value if followed by an ':end'" do
|
185
|
-
@loader.load("key:value\nextra\n:end")['key'].should == "value\nextra"
|
186
|
-
end
|
187
|
-
it "':end' is case insensitive" do
|
188
|
-
expect(@loader).to receive(:parse_command_key).with('end').once
|
189
|
-
@loader.load("key:value\nextra\n:EnD")
|
190
|
-
end
|
191
|
-
it "preserves blank lines and whitespace lines in the middle of content" do
|
192
|
-
@loader.load("key:value\n\n\t \nextra\n:end")['key'].should == "value\n\n\t \nextra"
|
193
|
-
end
|
194
|
-
it "doesn't preserve whitespace at the end of the key" do
|
195
|
-
@loader.load("key:value\nextra\t \n:end")['key'].should == "value\nextra"
|
196
|
-
end
|
197
|
-
it "preserves whitespace at the end of the original line" do
|
198
|
-
@loader.load("key:value\t \nextra\n:end")['key'].should == "value\t \nextra"
|
199
|
-
end
|
200
|
-
it "ignores whitespace and newlines before the ':end'" do
|
201
|
-
@loader.load("key:value\nextra\n \n\t\n:end")['key'].should == "value\nextra"
|
202
|
-
end
|
203
|
-
it "ignores spaces on either side of :end" do
|
204
|
-
expect(@loader).to receive(:parse_command_key).with('end').once
|
205
|
-
@loader.load("key:value\nextra\n :end ")
|
206
|
-
end
|
207
|
-
it "ignores tabs on either side of :end" do
|
208
|
-
expect(@loader).to receive(:parse_command_key).with('end').once
|
209
|
-
@loader.load("key:value\nextra\n\t\t:end\t\t")
|
210
|
-
end
|
211
|
-
it "parses :end as a special command even if more is appended to word" do
|
212
|
-
expect(@loader).to receive(:parse_command_key).with('end')
|
213
|
-
@loader.load("key:value\nextra\n:endthis")['key'].should == "value\nextra"
|
214
|
-
end
|
215
|
-
it "does not parse :endskip as an :end" do
|
216
|
-
expect(@loader).to_not receive(:parse_command_key).with('end')
|
217
|
-
@loader.load("key:value\nextra\n:endskip")['key'].should == "value"
|
218
|
-
end
|
219
|
-
it "ordinary text that starts with a colon is included" do
|
220
|
-
@loader.load("key:value\n:notacommand\n:end")['key'].should == "value\n:notacommand"
|
221
|
-
end
|
222
|
-
it "ignores all content on line after :end + space" do
|
223
|
-
expect(@loader).to receive(:parse_command_key).with('end').once
|
224
|
-
@loader.load("key:value\nextra\n:end this")['key'].should == "value\nextra"
|
225
|
-
end
|
226
|
-
it "ignores all content on line after :end + tab" do
|
227
|
-
expect(@loader).to receive(:parse_command_key).with('end').once
|
228
|
-
@loader.load("key:value\nextra\n:end\tthis")['key'].should == "value\nextra"
|
229
|
-
end
|
230
|
-
it "doesn't escape colons on first line" do
|
231
|
-
@loader.load("key::value\n:end")['key'].should == ":value"
|
232
|
-
@loader.load("key:\\:value\n:end")['key'].should == "\\:value"
|
233
|
-
end
|
234
|
-
it "does not allow escaping keys" do
|
235
|
-
@loader.load("key:value\nkey2\\:value\n:end")['key'].should == "value\nkey2\\:value"
|
236
|
-
end
|
237
|
-
it "allows escaping key lines with a leading backslash" do
|
238
|
-
@loader.load("key:value\n\\key2:value\n:end")['key'].should == "value\nkey2:value"
|
239
|
-
end
|
240
|
-
it "allows escaping commands at the beginning of lines" do
|
241
|
-
@loader.load("key:value\n\\:end\n:end")['key'].should == "value\n:end"
|
242
|
-
end
|
243
|
-
it "allows escaping commands with extra text at the beginning of lines" do
|
244
|
-
@loader.load("key:value\n\\:endthis\n:end")['key'].should == "value\n:endthis"
|
245
|
-
end
|
246
|
-
it "allows escaping of non-commandc at the beginning of lines" do
|
247
|
-
@loader.load("key:value\n\\:notacommand\n:end")['key'].should == "value\n:notacommand"
|
248
|
-
end
|
249
|
-
it "allows simple array style lines" do
|
250
|
-
@loader.load("key:value\n* value\n:end")['key'].should == "value\n* value"
|
251
|
-
end
|
252
|
-
it "escapes '*' within multi-line values when not in a simple array" do
|
253
|
-
@loader.load("key:value\n\\* value\n:end")['key'].should == "value\n* value"
|
254
|
-
end
|
255
|
-
it "allows escaping scope keys at the beginning of lines" do
|
256
|
-
@loader.load("key:value\n\\{scope}\n:end")['key'].should == "value\n{scope}"
|
257
|
-
@loader.load("key:value\n\\[comment]\n:end")['key'].should == "value"
|
258
|
-
@loader.load("key:value\n\\[[array]]\n:end")['key'].should == "value\n[array]"
|
259
|
-
end
|
260
|
-
it "arrays within a multi-line value breaks up the value" do
|
261
|
-
@loader.load("key:value\ntext\n[array]\nmore text\n:end")['key'].should == "value"
|
262
|
-
end
|
263
|
-
it "objects within a multi-line value breaks up the value" do
|
264
|
-
@loader.load("key:value\ntext\n{scope}\nmore text\n:end")['key'].should == "value"
|
265
|
-
end
|
266
|
-
it "bullets within a multi-line value do not break up the value" do
|
267
|
-
@loader.load("key:value\ntext\n* value\nmore text\n:end")['key'].should == "value\ntext\n* value\nmore text"
|
268
|
-
end
|
269
|
-
it "skips within a multi-line value do not break up the value" do
|
270
|
-
@loader.load("key:value\ntext\n:skip\n:endskip\nmore text\n:end")['key'].should == "value\ntext\nmore text"
|
271
|
-
end
|
272
|
-
it "allows escaping initial backslash at the beginning of lines" do
|
273
|
-
@loader.load("key:value\n\\\\:end\n:end")['key'].should == "value\n\\:end"
|
274
|
-
end
|
275
|
-
it "escapes only one initial backslash" do
|
276
|
-
@loader.load("key:value\n\\\\\\:end\n:end")['key'].should == "value\n\\\\:end"
|
277
|
-
end
|
278
|
-
it "allows escaping multiple lines in a value" do
|
279
|
-
@loader.load("key:value\n\\:end\n\\:ignore\n\\:endskip\n\\:skip\n:end'")['key'].should == "value\n:end\n:ignore\n:endskip\n:skip"
|
280
|
-
end
|
281
|
-
it "doesn't escape colons after beginning of lines" do
|
282
|
-
@loader.load("key:value\nLorem key2\\:value\n:end")['key'].should == "value\nLorem key2\\:value"
|
283
|
-
end
|
284
|
-
|
285
|
-
end
|
286
|
-
|
287
|
-
describe "scopes" do
|
288
|
-
|
289
|
-
it "{scope} creates an empty object at 'scope'" do
|
290
|
-
@loader.load("{scope}")['scope'].class.should == Hash
|
291
|
-
end
|
292
|
-
it "ignores spaces on either side of {scope}" do
|
293
|
-
expect(@loader).to receive(:parse_scope).with('{', 'scope').once
|
294
|
-
@loader.load(" {scope} ")
|
295
|
-
end
|
296
|
-
it "ignores tabs on either side of {scope}" do
|
297
|
-
expect(@loader).to receive(:parse_scope).with('{', 'scope').once
|
298
|
-
@loader.load("\t\t{scope}\t\t")['scope'].should == {}
|
299
|
-
end
|
300
|
-
it "ignores text after {scope}" do
|
301
|
-
expect(@loader).to receive(:parse_scope).with('{', 'scope').once
|
302
|
-
@loader.load("{scope}a")['scope'].should == {}
|
303
|
-
end
|
304
|
-
it "ignores spaces on either side of {scope} variable name" do
|
305
|
-
expect(@loader).to receive(:parse_scope).with('{', 'scope').once
|
306
|
-
@loader.load("{ scope }")['scope'].should == {}
|
307
|
-
end
|
308
|
-
it "ignores tabs on either side of {scope} variable name" do
|
309
|
-
expect(@loader).to receive(:parse_scope).with('{', 'scope').once
|
310
|
-
@loader.load("{\t\tscope\t\t}")['scope'].should == {}
|
311
|
-
end
|
312
|
-
it "items before a {scope} are not namespaced" do
|
313
|
-
@loader.load("key:value\n{scope}")['key'].should == 'value'
|
314
|
-
end
|
315
|
-
it "items after a {scope} are namespaced" do
|
316
|
-
@loader.load("{scope}\nkey:value")['key'].should == nil
|
317
|
-
@loader.load("{scope}\nkey:value")['scope']['key'].should == 'value'
|
318
|
-
end
|
319
|
-
it "scopes can be nested using dot-notaion" do
|
320
|
-
@loader.load("{scope.scope}\nkey:value")['scope']['scope']['key'].should == 'value'
|
321
|
-
end
|
322
|
-
it "scopes can be reopened" do
|
323
|
-
@loader.load("{scope}\nkey:value\n{}\n{scope}\nother:value")['scope'].keys.should =~ ["key", "other"]
|
324
|
-
end
|
325
|
-
it "scopes do not overwrite existing values" do
|
326
|
-
@loader.load("{scope.scope}\nkey:value\n{scope.otherscope}key:value")['scope']['scope']['key'].should == 'value'
|
327
|
-
end
|
328
|
-
it "{} resets to the global scope" do
|
329
|
-
expect(@loader).to receive(:parse_scope).with('{', '').once
|
330
|
-
@loader.load("{scope}\n{}\nkey:value")['key'].should == 'value'
|
331
|
-
end
|
332
|
-
it "ignore spaces inside {}" do
|
333
|
-
expect(@loader).to receive(:parse_scope).with('{', '').once
|
334
|
-
@loader.load("{scope}\n{ }\nkey:value")['key'].should == 'value'
|
335
|
-
end
|
336
|
-
it "ignore tabs inside {}" do
|
337
|
-
expect(@loader).to receive(:parse_scope).with('{', '').once
|
338
|
-
@loader.load("{scope}\n{\t\t}\nkey:value")['key'].should == 'value'
|
339
|
-
end
|
340
|
-
it "ignore spaces on either side of {}" do
|
341
|
-
expect(@loader).to receive(:parse_scope).with('{', '').once
|
342
|
-
@loader.load("{scope}\n {} \nkey:value")['key'].should == 'value'
|
343
|
-
end
|
344
|
-
it "ignore tabs on either side of {}" do
|
345
|
-
expect(@loader).to receive(:parse_scope).with('{', '').once
|
346
|
-
@loader.load("{scope}\n\t\t{}\t\t\nkey:value")['key'].should == 'value'
|
347
|
-
end
|
348
|
-
|
349
|
-
end
|
350
|
-
|
351
|
-
describe "arrays" do
|
352
|
-
|
353
|
-
it "[array] creates an empty array at 'array'" do
|
354
|
-
@loader.load("[array]")['array'].should == []
|
355
|
-
end
|
356
|
-
it "ignores spaces on either side of [array]" do
|
357
|
-
expect(@loader).to receive(:parse_scope).with('[', 'array').once
|
358
|
-
@loader.load(" [array] ")
|
359
|
-
end
|
360
|
-
it "ignores tabs on either side of [array]" do
|
361
|
-
expect(@loader).to receive(:parse_scope).with('[', 'array').once
|
362
|
-
@loader.load("\t\t[array]\t\t")
|
363
|
-
end
|
364
|
-
it "ignores text after [array]" do
|
365
|
-
expect(@loader).to receive(:parse_scope).with('[', 'array').once
|
366
|
-
@loader.load("[array]a")['array'].should == []
|
367
|
-
end
|
368
|
-
it "ignores spaces on either side of [array] variable name" do
|
369
|
-
expect(@loader).to receive(:parse_scope).with('[', 'array').once
|
370
|
-
@loader.load("[ array ]")
|
371
|
-
end
|
372
|
-
it "ignores tabs on either side of [array] variable name" do
|
373
|
-
expect(@loader).to receive(:parse_scope).with('[', 'array').once
|
374
|
-
@loader.load("[\t\tarray\t\t]")
|
375
|
-
end
|
376
|
-
it "arrays can be nested using dot-notaion" do
|
377
|
-
@loader.load("[scope.array]")['scope']['array'].should == []
|
378
|
-
end
|
379
|
-
it "array values can be nested using dot-notaion" do
|
380
|
-
@loader.load("[array]\nscope.key: value\nscope.key: value")['array'].should == [{'scope' => {'key' => 'value'}}, {'scope' => {'key' => 'value'}}]
|
381
|
-
end
|
382
|
-
it "[] resets to the global scope" do
|
383
|
-
@loader.load("[array]\n[]\nkey:value")['key'].should == 'value'
|
384
|
-
end
|
385
|
-
it "ignore spaces inside []" do
|
386
|
-
expect(@loader).to receive(:parse_scope).with('[', '').once
|
387
|
-
@loader.load("[array]\n[ ]\nkey:value")['key'].should == 'value'
|
388
|
-
end
|
389
|
-
it "ignore tabs inside []" do
|
390
|
-
expect(@loader).to receive(:parse_scope).with('[', '').once
|
391
|
-
@loader.load("[array]\n[\t\t]\nkey:value")['key'].should == 'value'
|
392
|
-
end
|
393
|
-
it "ignore spaces on either side of []" do
|
394
|
-
expect(@loader).to receive(:parse_scope).with('[', '').once
|
395
|
-
@loader.load("[array]\n [] \nkey:value")['key'].should == 'value'
|
396
|
-
end
|
397
|
-
it "ignore tabs on either side of []" do
|
398
|
-
expect(@loader).to receive(:parse_scope).with('[', '').once
|
399
|
-
@loader.load("[array]\n\t\t[]\t\t\nkey:value")['key'].should == 'value'
|
400
|
-
end
|
401
|
-
|
402
|
-
end
|
403
|
-
|
404
|
-
describe "simple arrays" do
|
405
|
-
|
406
|
-
it "creates a simple array when an '*' is encountered first" do
|
407
|
-
@loader.load("[array]\n*Value")['array'].first.should == 'Value'
|
408
|
-
end
|
409
|
-
it "ignores spaces on either side of '*'" do
|
410
|
-
@loader.load("[array]\n * Value")['array'].first.should == 'Value'
|
411
|
-
end
|
412
|
-
it "ignores tabs on either side of '*'" do
|
413
|
-
@loader.load("[array]\n\t\t*\t\tValue")['array'].first.should == 'Value'
|
414
|
-
end
|
415
|
-
it "adds multiple elements" do
|
416
|
-
@loader.load("[array]\n*Value1\n*Value2")['array'].should == ['Value1', 'Value2']
|
417
|
-
end
|
418
|
-
it "ignores all other text between elements" do
|
419
|
-
@loader.load("[array]\n*Value1\nNon-element\n*Value2")['array'].should == ['Value1', 'Value2']
|
420
|
-
end
|
421
|
-
it "ignores key:value pairs between elements" do
|
422
|
-
@loader.load("[array]\n*Value1\nkey:value\n*Value2")['array'].should == ['Value1', 'Value2']
|
423
|
-
end
|
424
|
-
it "parses key:values normally after an end-array" do
|
425
|
-
@loader.load("[array]\n*Value1\n[]\nkey:value")['key'].should == 'value'
|
426
|
-
end
|
427
|
-
it "multi-line values are allowed" do
|
428
|
-
@loader.load("[array]\n*Value1\nextra\n:end")['array'].first.should == "Value1\nextra"
|
429
|
-
end
|
430
|
-
it "allows escaping of '*' within multi-line values in simple arrays" do
|
431
|
-
@loader.load("[array]\n*Value\n\\* extra\n:end")['array'].first.should == "Value\n* extra"
|
432
|
-
end
|
433
|
-
it "allows escaping of command keys within multi-line values" do
|
434
|
-
@loader.load("[array]\n*Value\n\\:end\n:end")['array'].first.should == "Value\n:end"
|
435
|
-
end
|
436
|
-
it "does not allow escaping of keys within multi-line values" do
|
437
|
-
@loader.load("[array]\n*Value\nkey\\:value\n:end")['array'].first.should == "Value\nkey\\:value"
|
438
|
-
end
|
439
|
-
it "allows escaping key lines with a leading backslash" do
|
440
|
-
@loader.load("[array]\n*Value\n\\key:value\n:end")['array'].first.should == "Value\nkey:value"
|
441
|
-
end
|
442
|
-
it "does not allow escaping of colons not at the beginning of lines" do
|
443
|
-
@loader.load("[array]\n*Value\nword key\\:value\n:end")['array'].first.should == "Value\nword key\\:value"
|
444
|
-
end
|
445
|
-
it "arrays within a multi-line value breaks up the value" do
|
446
|
-
@loader.load("[array]\n* value\n[array]\nmore text\n:end")['array'].first.should == "value"
|
447
|
-
end
|
448
|
-
it "objects within a multi-line value breaks up the value" do
|
449
|
-
@loader.load("[array]\n* value\n{scope}\nmore text\n:end")['array'].first.should == "value"
|
450
|
-
end
|
451
|
-
it "key/values within a multi-line value do not break up the value" do
|
452
|
-
@loader.load("[array]\n* value\nkey: value\nmore text\n:end")['array'].first.should == "value\nkey: value\nmore text"
|
453
|
-
end
|
454
|
-
it "bullets within a multi-line value break up the value" do
|
455
|
-
@loader.load("[array]\n* value\n* value\nmore text\n:end")['array'].first.should == "value"
|
456
|
-
end
|
457
|
-
it "skips within a multi-line value do not break up the value" do
|
458
|
-
@loader.load("[array]\n* value\n:skip\n:endskip\nmore text\n:end")['array'].first.should == "value\nmore text"
|
459
|
-
end
|
460
|
-
it "arrays that are reopened add to existing array" do
|
461
|
-
@loader.load("[array]\n*Value\n[]\n[array]\n*Value")['array'].should == ['Value', 'Value']
|
462
|
-
end
|
463
|
-
it "simple arrays that are reopened remain simple" do
|
464
|
-
@loader.load("[array]\n*Value\n[]\n[array]\nkey:value")['array'].should == ['Value']
|
465
|
-
end
|
466
|
-
it "simple arrays overwrite existing keys" do
|
467
|
-
@loader.load("a.b:complex value\n[a.b]\n*simple value")['a']['b'][0].should == 'simple value'
|
468
|
-
end
|
4
|
+
Dir.glob(File.expand_path('../../../archieml.org/test/1.0/*.aml', __FILE__)).each do |f|
|
5
|
+
data = File.read(f)
|
6
|
+
slug, idx = File.basename(f).split('.')
|
469
7
|
|
470
|
-
|
8
|
+
# Parse without inline comments
|
9
|
+
metadata = Archieml::Loader.new.load(data, comments: false)
|
10
|
+
test = metadata['test']
|
11
|
+
result = JSON.parse(metadata['result'])
|
471
12
|
|
472
|
-
|
13
|
+
aml = Archieml::Loader.new.load(data)
|
14
|
+
aml.delete('test')
|
15
|
+
aml.delete('result')
|
473
16
|
|
474
|
-
it "
|
475
|
-
|
476
|
-
end
|
477
|
-
it "array items can have multiple keys" do
|
478
|
-
@loader.load("[array]\nkey:value\nsecond:value")['array'].first.keys.should =~ ['key', 'second']
|
479
|
-
end
|
480
|
-
it "when a duplicate key is encountered, a new item in the array is started" do
|
481
|
-
@loader.load("[array]\nkey:value\nsecond:value\nkey:value")['array'].length.should == 2
|
482
|
-
@loader.load("[array]\nkey:first\nkey:second")['array'].last.should == {'key' => 'second'}
|
483
|
-
@loader.load("[array]\nscope.key:first\nscope.key:second")['array'].last.should == {'scope' => {'key' => 'second'}}
|
484
|
-
end
|
485
|
-
it "duplicate keys must match on dot-notation scope" do
|
486
|
-
@loader.load("[array]\nkey:value\nscope.key:value")['array'].length.should == 1
|
487
|
-
end
|
488
|
-
it "duplicate keys must match on dot-notation scope" do
|
489
|
-
@loader.load("[array]\nscope.key:value\nkey:value\notherscope.key:value")['array'].length.should == 1
|
490
|
-
end
|
491
|
-
it "arrays within a multi-line value breaks up the value" do
|
492
|
-
@loader.load("[array]\nkey:value\n[array]\nmore text\n:end")['array'].first['key'].should == "value"
|
493
|
-
end
|
494
|
-
it "objects within a multi-line value breaks up the value" do
|
495
|
-
@loader.load("[array]\nkey:value\n{scope}\nmore text\n:end")['array'].first['key'].should == "value"
|
496
|
-
end
|
497
|
-
it "key/values within a multi-line value break up the value" do
|
498
|
-
@loader.load("[array]\nkey:value\nother: value\nmore text\n:end")['array'].first['key'].should == "value"
|
499
|
-
end
|
500
|
-
it "bullets within a multi-line value do not break up the value" do
|
501
|
-
@loader.load("[array]\nkey:value\n* value\nmore text\n:end")['array'].first['key'].should == "value\n* value\nmore text"
|
502
|
-
end
|
503
|
-
it "skips within a multi-line value do not break up the value" do
|
504
|
-
@loader.load("[array]\nkey:value\n:skip\n:endskip\nmore text\n:end")['array'].first['key'].should == "value\nmore text"
|
505
|
-
end
|
506
|
-
it "arrays that are reopened add to existing array" do
|
507
|
-
@loader.load("[array]\nkey:value\n[]\n[array]\nkey:value")['array'].length.should == 2
|
508
|
-
end
|
509
|
-
it "complex arrays that are reopened remain complex" do
|
510
|
-
@loader.load("[array]\nkey:value\n[]\n[array]\n*Value")['array'].should == [{'key' => 'value'}]
|
511
|
-
end
|
512
|
-
it "complex arrays overwrite existing keys" do
|
513
|
-
@loader.load("a.b:complex value\n[a.b]\nkey:value")['a']['b'][0]['key'].should == 'value'
|
17
|
+
it "#{slug}.#{idx} #{test}" do
|
18
|
+
aml.should == result
|
514
19
|
end
|
515
|
-
|
516
|
-
end
|
517
|
-
|
518
|
-
describe "inline comments" do
|
519
|
-
|
520
|
-
it "ignore comments inside of [single brackets]" do
|
521
|
-
@loader.load("key:value [inline comments] value")['key'].should == "value value"
|
522
|
-
end
|
523
|
-
it "supports multiple inline comments on a single line" do
|
524
|
-
@loader.load("key:value [inline comments] value [inline comments] value")['key'].should == "value value value"
|
525
|
-
end
|
526
|
-
it "supports adjacent comments" do
|
527
|
-
@loader.load("key:value [inline comments] [inline comments] value")['key'].should == "value value"
|
528
|
-
end
|
529
|
-
it "supports no-space adjacent comments" do
|
530
|
-
@loader.load("key:value [inline comments][inline comments] value")['key'].should == "value value"
|
531
|
-
end
|
532
|
-
it "supports comments at beginning of string" do
|
533
|
-
@loader.load("key:[inline comments] value")['key'].should == "value"
|
534
|
-
end
|
535
|
-
it "supports comments at end of string" do
|
536
|
-
@loader.load("key:value [inline comments]")['key'].should == "value"
|
537
|
-
end
|
538
|
-
it "whitespace before a comment that appears at end of line is ignored" do
|
539
|
-
@loader.load("key:value [inline comments] value [inline comments]")['key'].should == "value value"
|
540
|
-
end
|
541
|
-
it "unmatched single brackets are preserved" do
|
542
|
-
@loader.load("key:value ][ value")['key'].should == "value ][ value"
|
543
|
-
end
|
544
|
-
|
545
|
-
it "inline comments are supported on the first of multi-line values" do
|
546
|
-
@loader.load("key:value [inline comments] on\nmultiline\n:end")['key'].should == "value on\nmultiline"
|
547
|
-
end
|
548
|
-
it "inline comments are supported on subsequent lines of multi-line values" do
|
549
|
-
@loader.load("key:value\nmultiline [inline comments]\n:end")['key'].should == "value\nmultiline"
|
550
|
-
end
|
551
|
-
it "whitespace around comments is preserved, except at the beinning and end of a value" do
|
552
|
-
@loader.load("key: [] value [] \n multiline [] \n:end")['key'].should == "value \n multiline"
|
553
|
-
end
|
554
|
-
|
555
|
-
it "inline comments cannot span multiple lines" do
|
556
|
-
@loader.load("key:value [inline\ncomments] value\n:end")['key'].should == "value [inline\ncomments] value"
|
557
|
-
@loader.load("key:value \n[inline\ncomments] value\n:end")['key'].should == "value \n[inline\ncomments] value"
|
558
|
-
end
|
559
|
-
it "text inside [[double brackets]] is included as [single brackets]" do
|
560
|
-
@loader.load("key:value [[brackets]] value")['key'].should == "value [brackets] value"
|
561
|
-
end
|
562
|
-
it "unmatched double brackets are preserved" do
|
563
|
-
@loader.load("key:value ]][[ value")['key'].should == "value ]][[ value"
|
564
|
-
end
|
565
|
-
it "comments work in simple arrays" do
|
566
|
-
@loader.load("[array]\n*Val[comment]ue")['array'].first.should == "Value"
|
567
|
-
end
|
568
|
-
it "double brackets work in simple arrays" do
|
569
|
-
@loader.load("[array]\n*Val[[real]]ue")['array'].first.should == "Val[real]ue"
|
570
|
-
end
|
571
|
-
|
572
20
|
end
|
573
21
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: archieml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Strickland
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Parse Archie Markup Language documents
|
14
14
|
email:
|
@@ -18,6 +18,7 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- ".gitignore"
|
21
|
+
- ".gitmodules"
|
21
22
|
- Gemfile
|
22
23
|
- LICENSE
|
23
24
|
- README.md
|