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 +21 -0
- data/README.md +66 -0
- data/lib/vdf.tt +80 -0
- data/lib/vdf4r/parser.rb +61 -0
- data/lib/vdf4r/store.rb +11 -0
- data/lib/vdf4r/version.rb +3 -0
- data/spec/fixtures/bad/excessive_exit.txt +5 -0
- data/spec/fixtures/bad/insufficient_exit.txt +5 -0
- data/spec/fixtures/bad/no_preceding_key.txt +5 -0
- data/spec/fixtures/bad/too_recursive.txt +36 -0
- data/spec/fixtures/bad/ungrammatical_content.txt +4 -0
- data/spec/fixtures/items.txt +169 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/vdf4r/parser_spec.rb +130 -0
- data/spec/vdf4r/store_spec.rb +25 -0
- metadata +112 -0
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
|
data/lib/vdf4r/parser.rb
ADDED
@@ -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
|
data/lib/vdf4r/store.rb
ADDED
@@ -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
|
+
}
|
data/spec/spec_helper.rb
ADDED
@@ -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: []
|