vdf4r 0.1.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.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Joshua Morris
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # VDF4R
2
+
3
+ Parse Valve Data Format files easily and quickly.
4
+
5
+
6
+ ## Context
7
+
8
+ Valve has its own data format for storing game information. This library lets
9
+ you parse these files into a plain-old Ruby hash easily and quickly.
10
+
11
+ After that, you can do with the data what you will.
12
+
13
+
14
+ ## Installation
15
+
16
+ As normal:
17
+
18
+ gem install vdf4r
19
+
20
+ Or in your Gemfile:
21
+
22
+ gem 'vdf4r', '~>0.1.0'
23
+
24
+
25
+ ## Usage
26
+
27
+ require 'vdf4r'
28
+ require 'pp'
29
+
30
+ File.open('vdf_file.txt') do |file|
31
+ parser = VDF4R::Parser.new(file)
32
+ pp parser.parse # pretty-printed
33
+ end
34
+
35
+
36
+ ## Caveats
37
+
38
+ This library has only really been used on a few Dota 2 VDF files. It's not
39
+ battle-tested yet, and there are probably some minor issues.
40
+
41
+ If you find something you'd like to discuss, you can find me on #dota2replay
42
+ on quakenet IRC.
43
+
44
+ At least one of Dota 2's own VDF files have grammar mistakes.
45
+ (i.e. npc_abilities.txt) If you get an "ungrammatical content" error while
46
+ parsing, you will need to fix the error. It will give you the offending line:
47
+
48
+ (RuntimeError)parser.rb:30:in `block in parse': ungrammatical content: ' / Damage.
49
+ '
50
+
51
+ Indeed, in the VDF file, there are "comment" lines lacking the proper '//'
52
+ prefix. When I changed the file to contain '// Damage.' it parsed correctly.
53
+
54
+ I'll think of a way to make the parser more permissive as time allows.
55
+
56
+
57
+ ## Hacking
58
+
59
+ Just clone the source from here. If issuing a pull request, make sure your
60
+ change is on a topic branch accompanied by new tests; all behaviors must pass.
61
+
62
+
63
+ ## License
64
+
65
+ VDF4R is offered under the MIT license. See [LICENSE](https://github.com/skadistats/vdf4r/blob/master/README.md)
66
+ for the license itself.
data/lib/vdf.tt ADDED
@@ -0,0 +1,80 @@
1
+ module VDF4R
2
+ grammar KeyValues
3
+ rule line
4
+ (enter_object / exit_object / comment / key_value / key / blank) endline?
5
+ {
6
+ def value
7
+ elements[0].value
8
+ end
9
+ }
10
+ end
11
+
12
+ rule blank
13
+ whitespace*
14
+ {
15
+ def value
16
+ :blank
17
+ end
18
+ }
19
+ end
20
+
21
+ rule key_value
22
+ whitespace* token whitespace* token whitespace* comment?
23
+ {
24
+ def value
25
+ [elements[1].value, elements[3].value]
26
+ end
27
+ }
28
+ end
29
+
30
+ rule key
31
+ whitespace* token whitespace* comment?
32
+ {
33
+ def value
34
+ [elements[1].value]
35
+ end
36
+ }
37
+ end
38
+
39
+ rule token
40
+ '"' [^"]* '"'
41
+ {
42
+ def value
43
+ elements[1..-2].collect { |e| e.text_value }.join
44
+ end
45
+ }
46
+ end
47
+
48
+ rule enter_object
49
+ whitespace* '{' whitespace* comment? {
50
+ def value
51
+ :enter_object
52
+ end
53
+ }
54
+ end
55
+
56
+ rule exit_object
57
+ whitespace* '}' whitespace* comment? {
58
+ def value
59
+ :exit_object
60
+ end
61
+ }
62
+ end
63
+
64
+ rule comment
65
+ whitespace* [/]? [/] [^\n]* {
66
+ def value
67
+ :comment
68
+ end
69
+ }
70
+ end
71
+
72
+ rule whitespace
73
+ [\t ]
74
+ end
75
+
76
+ rule endline
77
+ [\r]? [\n]
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,61 @@
1
+ require 'treetop'
2
+ require 'vdf4r/store'
3
+
4
+ MAX_RECURSION = 10
5
+ GRAMMAR_PATH = File.join(File.dirname(__FILE__), '..', 'vdf.tt')
6
+
7
+ Treetop.load GRAMMAR_PATH
8
+
9
+ module VDF4R
10
+ class Parser
11
+ def initialize(input)
12
+ case
13
+ when input.respond_to?(:each_line)
14
+ @input = input.each_line.to_a
15
+ when input.respond_to?(:lines)
16
+ @input = input.lines
17
+ else
18
+ raise ArgumentError.new('input must respond to #each_line or #lines')
19
+ end
20
+ end
21
+
22
+ def parse
23
+ parser = VDF4R::KeyValuesParser.new
24
+ store = Store.new
25
+ key = nil
26
+ path = []
27
+
28
+ @input.each do |line|
29
+ node = parser.parse(line)
30
+ raise "ungrammatical content: '#{line}'" if node.nil?
31
+
32
+ next if [:blank, :comment].include?(node.value)
33
+
34
+ if node.value.respond_to?(:to_ary)
35
+ case node.value.length
36
+ when 1
37
+ key = node.value.first
38
+ when 2
39
+ k, v = node.value
40
+ store.traverse(path)[k] = v
41
+ end
42
+ elsif node.value.kind_of?(Symbol)
43
+ case node.value
44
+ when :enter_object
45
+ raise 'no preceding key for object' unless key
46
+ raise 'too recursive' if path.length > MAX_RECURSION
47
+ path.push key
48
+ key = nil
49
+ when :exit_object
50
+ raise 'nesting unbalanced (excessive exit)' if path.empty?
51
+ path.pop
52
+ end
53
+ end
54
+ end
55
+
56
+ raise 'nesting unbalanced (insufficient exit)' unless path.empty?
57
+
58
+ store
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,11 @@
1
+ module VDF4R
2
+ class Store < Hash
3
+ def initialize
4
+ super { |h, k| h[k] = Store.new } # defaultdict(dict)
5
+ end
6
+
7
+ def traverse(path)
8
+ path.inject(self) { |current, path_component| current[path_component] }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module VDF4R
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,5 @@
1
+ "DOTAAbilities"
2
+ {
3
+ "Version"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ "DOTAAbilities"
2
+ {
3
+ "Version"
4
+ {
5
+ }
@@ -0,0 +1,5 @@
1
+ "DOTAAbilities"
2
+ {
3
+ {
4
+ }
5
+ }
@@ -0,0 +1,36 @@
1
+ "DOTAAbilities"
2
+ {
3
+ "1"
4
+ {
5
+ "2"
6
+ {
7
+ "3"
8
+ {
9
+ "4"
10
+ {
11
+ "5"
12
+ {
13
+ "6"
14
+ {
15
+ "7"
16
+ {
17
+ "8"
18
+ {
19
+ "9"
20
+ {
21
+ "10"
22
+ {
23
+ "11"
24
+ {
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,4 @@
1
+ "DOTAAbilities"
2
+ {
3
+ The parser won't understand this line.
4
+ }
@@ -0,0 +1,169 @@
1
+ // Dota Heroes File
2
+ "DOTAAbilities"
3
+ {
4
+ "Version" "1"
5
+
6
+ //=================================================================================================================
7
+ // Blink dagger
8
+ //=================================================================================================================
9
+ "item_blink"
10
+ {
11
+ // General
12
+ //-------------------------------------------------------------------------------------------------------------
13
+ "ID" "1" // unique ID number for this item. Do not change this once established or it will invalidate collected stats.
14
+ "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_POINT | DOTA_ABILITY_BEHAVIOR_DIRECTIONAL | DOTA_ABILITY_BEHAVIOR_ROOT_DISABLES"
15
+
16
+ // Stats
17
+ //-------------------------------------------------------------------------------------------------------------
18
+ "AbilityCastRange" "0"
19
+ "AbilityCastPoint" "0.0"
20
+ "AbilityCooldown" "12.0"
21
+ "AbilityManaCost" "0"
22
+
23
+ // Item Info
24
+ //-------------------------------------------------------------------------------------------------------------
25
+ "ItemCost" "2150"
26
+ "ItemShopTags" "teleport"
27
+ "ItemQuality" "component"
28
+ "ItemAliases" "blink dagger"
29
+ "SideShop" "1"
30
+ "ItemDeclarations" "DECLARE_PURCHASES_TO_TEAMMATES | DECLARE_PURCHASES_IN_SPEECH | DECLARE_PURCHASES_TO_SPECTATORS"
31
+
32
+ // Special
33
+ //-------------------------------------------------------------------------------------------------------------
34
+ "AbilitySpecial"
35
+ {
36
+ "01"
37
+ {
38
+ "var_type" "FIELD_INTEGER"
39
+ "blink_range" "1200"
40
+ }
41
+ "02"
42
+ {
43
+ "var_type" "FIELD_INTEGER"
44
+ "blink_damage_cooldown" "3"
45
+ }
46
+ "03"
47
+ {
48
+ "var_type" "FIELD_INTEGER"
49
+ "blink_range_clamp" "960"
50
+ }
51
+ }
52
+ }
53
+
54
+ //=================================================================================================================
55
+ // Blades of Attack
56
+ //=================================================================================================================
57
+ "item_blades_of_attack"
58
+ {
59
+ // General
60
+ //-------------------------------------------------------------------------------------------------------------
61
+ "ID" "2" // unique ID number for this item. Do not change this once established or it will invalidate collected stats.
62
+ "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE"
63
+
64
+ // Item Info
65
+ //-------------------------------------------------------------------------------------------------------------
66
+ "ItemCost" "450"
67
+ "ItemShopTags" "damage;tutorial"
68
+ "ItemQuality" "component"
69
+ "ItemAliases" "blades of attack"
70
+ "SideShop" "1"
71
+
72
+ // Special
73
+ //-------------------------------------------------------------------------------------------------------------
74
+ "AbilitySpecial"
75
+ {
76
+ "01"
77
+ {
78
+ "var_type" "FIELD_INTEGER"
79
+ "bonus_damage" "9"
80
+ }
81
+ }
82
+ }
83
+
84
+ //=================================================================================================================
85
+ // Broadsword
86
+ //=================================================================================================================
87
+ "item_broadsword"
88
+ {
89
+ // General
90
+ //-------------------------------------------------------------------------------------------------------------
91
+ "ID" "3" // unique ID number for this item. Do not change this once established or it will invalidate collected stats.
92
+ "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE"
93
+
94
+ // Item Info
95
+ //-------------------------------------------------------------------------------------------------------------
96
+ "ItemCost" "1200"
97
+ "ItemShopTags" "damage"
98
+ "ItemQuality" "component"
99
+ "ItemAliases" "broadsword"
100
+
101
+ // Special
102
+ //-------------------------------------------------------------------------------------------------------------
103
+ "AbilitySpecial"
104
+ {
105
+ "01"
106
+ {
107
+ "var_type" "FIELD_INTEGER"
108
+ "bonus_damage" "18"
109
+ }
110
+ }
111
+ }
112
+
113
+ //=================================================================================================================
114
+ // Black King Bar
115
+ //=================================================================================================================
116
+ "item_black_king_bar"
117
+ {
118
+ // General
119
+ //-------------------------------------------------------------------------------------------------------------
120
+ "ID" "116" // unique ID number for this item. Do not change this once established or it will invalidate collected stats.
121
+ "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_IMMEDIATE | DOTA_ABILITY_BEHAVIOR_NO_TARGET"
122
+
123
+ // Stats
124
+ //-------------------------------------------------------------------------------------------------------------
125
+ "AbilityCooldown" "80 75 70 65 60 55 50"
126
+
127
+ // Item Info
128
+ //-------------------------------------------------------------------------------------------------------------
129
+ "ItemCost" "3975"
130
+ "ItemShopTags" "str;damage;hard_to_tag"
131
+ "ItemQuality" "epic"
132
+ "ItemAliases" "bkb;black king bar"
133
+ "ItemSellable" "0"
134
+ "ItemDeclarations" "DECLARE_PURCHASES_TO_TEAMMATES | DECLARE_PURCHASES_IN_SPEECH | DECLARE_PURCHASES_TO_SPECTATORS"
135
+
136
+ // Special
137
+ //-------------------------------------------------------------------------------------------------------------
138
+ "AbilitySpecial"
139
+ {
140
+ "01"
141
+ {
142
+ "var_type" "FIELD_INTEGER"
143
+ "bonus_strength" "10"
144
+ }
145
+ "02"
146
+ {
147
+ "var_type" "FIELD_INTEGER"
148
+ "bonus_damage" "24"
149
+ }
150
+ "03"
151
+ {
152
+ "var_type" "FIELD_FLOAT"
153
+ "duration" "10.0 9.0 8.0 7.0 6.0 5.0 4.0"
154
+ }
155
+ "04"
156
+ {
157
+ "var_type" "FIELD_INTEGER"
158
+ "max_level" "6"
159
+ }
160
+ "05"
161
+ {
162
+ "var_type" "FIELD_INTEGER"
163
+ "model_scale" "30" // Percentage over model scale
164
+ }
165
+ }
166
+ }
167
+
168
+ // next free ID: 242
169
+ }
@@ -0,0 +1,12 @@
1
+ module FixtureHelpers
2
+ def with_fixture(name, &block)
3
+ abspath = File.join(File.dirname(__FILE__), 'fixtures', *name.split('/'))
4
+ File.open("#{abspath}.txt") do |fixture|
5
+ yield fixture
6
+ end
7
+ end
8
+ end
9
+
10
+ RSpec.configure do |c|
11
+ c.include FixtureHelpers
12
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ require 'vdf4r'
4
+ require 'stringio'
5
+
6
+ module VDF4R
7
+ describe Parser do
8
+ subject { VDF4R::Parser }
9
+
10
+ describe 'instantiation' do
11
+ shared_examples_for "doesn't raise" do
12
+ it 'does not raise' do
13
+ expect {
14
+ subject.new(input)
15
+ }.not_to raise_error
16
+ end
17
+ end
18
+
19
+ context 'with string' do
20
+ let(:input) { 'foo\nbar\n'}
21
+ it_behaves_like "doesn't raise"
22
+ end
23
+
24
+ context 'with io' do
25
+ let(:input) { StringIO.new('input') }
26
+ it_behaves_like "doesn't raise"
27
+ end
28
+
29
+ context 'quacks right' do
30
+ let(:input) do
31
+ quacker = Class.new do
32
+ def lines
33
+ ['line 1', 'line 2']
34
+ end
35
+ end
36
+ quacker.new
37
+ end
38
+
39
+ it_behaves_like "doesn't raise"
40
+ end
41
+
42
+ context 'with inappropriate input' do
43
+ let(:input) { 1234 }
44
+
45
+ it 'raises' do
46
+ expect {
47
+ subject.new(input)
48
+ }.to raise_error
49
+ end
50
+ end
51
+ end
52
+
53
+ describe 'items.txt fixture translation output' do
54
+ let(:result) do
55
+ with_fixture('items') do |fixture|
56
+ subject.new(fixture).parse
57
+ end
58
+ end
59
+
60
+ it 'quacks like hash' do
61
+ expect(result).to respond_to(:keys)
62
+ end
63
+
64
+ it 'has the correct root' do
65
+ expect(result.keys.length).to eq(1)
66
+ expect(result.keys).to include('DOTAAbilities')
67
+ end
68
+
69
+ it 'has a sampling of correct top-level child entries' do
70
+ abilities = result['DOTAAbilities']
71
+ expect(abilities).to include('item_blink')
72
+ expect(abilities).to include('item_blades_of_attack')
73
+ expect(abilities).to include('item_broadsword')
74
+ expect(abilities).to include('item_black_king_bar')
75
+ end
76
+ end
77
+
78
+ describe 'bad input' do
79
+ context 'unbalanced nesting (insufficient exit)' do
80
+ it 'raises TranslationError' do
81
+ expect {
82
+ with_fixture('bad/insufficient_exit') do |fixture|
83
+ subject.new(fixture).parse
84
+ end
85
+ }.to raise_error /insufficient exit/
86
+ end
87
+ end
88
+
89
+ context 'unbalanced nesting (excessive exit)' do
90
+ it 'raises TranslationError' do
91
+ expect {
92
+ with_fixture('bad/excessive_exit') do |fixture|
93
+ subject.new(fixture).parse
94
+ end
95
+ }.to raise_error /excessive exit/
96
+ end
97
+ end
98
+
99
+ context 'ungrammatical content' do
100
+ it 'raises TranslationError' do
101
+ expect {
102
+ with_fixture('bad/ungrammatical_content') do |fixture|
103
+ subject.new(fixture).parse
104
+ end
105
+ }.to raise_error /ungrammatical content/
106
+ end
107
+ end
108
+
109
+ context 'too recursive' do
110
+ it 'raises TranslationError' do
111
+ expect {
112
+ with_fixture('bad/too_recursive') do |fixture|
113
+ subject.new(fixture).parse
114
+ end
115
+ }.to raise_error /too recursive/
116
+ end
117
+ end
118
+
119
+ context 'no preceding key' do
120
+ it 'raises TranslationError' do
121
+ expect {
122
+ with_fixture('bad/no_preceding_key') do |fixture|
123
+ subject.new(fixture).parse
124
+ end
125
+ }.to raise_error /no preceding key/
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'vdf4r/store'
3
+
4
+ module VDF4R
5
+ describe Store do
6
+ it 'defaults to a nested store for unknown keys' do
7
+ subject['foo'].should be_kind_of(Store)
8
+ end
9
+
10
+ it 'preserves original on second access of unknown key' do
11
+ store = subject['foo']['bar']
12
+ subject['foo']['bar']['ohai'] = 1
13
+ subject['foo']['bar'].should === store
14
+ end
15
+
16
+ describe '#traverse' do
17
+ let(:path) { ['foo', 'bar'] }
18
+
19
+ it 'follows path recursively to arbitrary depths' do
20
+ subject['foo']['bar'] = 'ohai'
21
+ subject.traverse(path).should eq('ohai')
22
+ end
23
+ end
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vdf4r
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joshua Morris
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-04-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: treetop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.5.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.5.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 2.14.1
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.14.1
46
+ description: ! "# VDF4R\n\nParse Valve Data Format files easily and quickly.\n\n\n##
47
+ Context\n\nValve has its own data format for storing game information. This library
48
+ lets\nyou parse these files into a plain-old Ruby hash easily and quickly.\n\nAfter
49
+ that, you can do with the data what you will.\n\n\n## Installation\n\nAs normal:\n\n
50
+ \ gem install vdf4r\n\nOr in your Gemfile:\n\n gem 'vdf4r', '~>0.1.0'\n\n\n##
51
+ Usage\n\n require 'vdf4r'\n require 'pp'\n\n File.open('vdf_file.txt')
52
+ do |file|\n parser = VDF4R::Parser.new(file)\n pp parser.parse # pretty-printed\n
53
+ \ end\n\n\n## Caveats\n\nThis library has only really been used on a few Dota
54
+ 2 VDF files. It's not\nbattle-tested yet, and there are probably some minor issues.\n\nIf
55
+ you find something you'd like to discuss, you can find me on #dota2replay\non quakenet
56
+ IRC.\n\nAt least one of Dota 2's own VDF files have grammar mistakes.\n(i.e. npc_abilities.txt)
57
+ If you get an \"ungrammatical content\" error while\nparsing, you will need to fix
58
+ the error. It will give you the offending line:\n\n (RuntimeError)parser.rb:30:in
59
+ `block in parse': ungrammatical content: ' / Damage.\n '\n\nIndeed, in
60
+ the VDF file, there are \"comment\" lines lacking the proper '//'\nprefix. When
61
+ I changed the file to contain '// Damage.' it parsed correctly.\n\nI'll think of
62
+ a way to make the parser more permissive as time allows.\n\n\n## Hacking\n\nJust
63
+ clone the source from here. If issuing a pull request, make sure your\nchange is
64
+ on a topic branch accompanied by new tests; all behaviors must pass.\n\n\n## License\n\nVDF4R
65
+ is offered under the MIT license. See [LICENSE](https://github.com/skadistats/vdf4r/blob/master/README.md)\nfor
66
+ the license itself.\n"
67
+ email: onethirtyfive@skadistats.com
68
+ executables: []
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - lib/vdf.tt
73
+ - lib/vdf4r/parser.rb
74
+ - lib/vdf4r/store.rb
75
+ - lib/vdf4r/version.rb
76
+ - spec/fixtures/bad/excessive_exit.txt
77
+ - spec/fixtures/bad/insufficient_exit.txt
78
+ - spec/fixtures/bad/no_preceding_key.txt
79
+ - spec/fixtures/bad/too_recursive.txt
80
+ - spec/fixtures/bad/ungrammatical_content.txt
81
+ - spec/fixtures/items.txt
82
+ - spec/spec_helper.rb
83
+ - spec/vdf4r/parser_spec.rb
84
+ - spec/vdf4r/store_spec.rb
85
+ - README.md
86
+ - LICENSE
87
+ homepage: https://github.com/skadistats/vdf4r
88
+ licenses:
89
+ - MIT
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.23.2
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Valve Data Format (VDF) file parser
112
+ test_files: []