fonte 0.0.1
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/.gitignore +4 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/README.md +39 -0
- data/Rakefile +1 -0
- data/fonte.gemspec +22 -0
- data/lib/fonte/nodes/date_time_node.rb +14 -0
- data/lib/fonte/nodes/integer_node.rb +9 -0
- data/lib/fonte/nodes/ip_node.rb +9 -0
- data/lib/fonte/nodes/rcon_command_node.rb +13 -0
- data/lib/fonte/nodes/single_word_node.rb +9 -0
- data/lib/fonte/nodes/steam_id_node.rb +29 -0
- data/lib/fonte/nodes/steam_id_universe_node.rb +46 -0
- data/lib/fonte/nodes.rb +11 -0
- data/lib/fonte/parsers/address.treetop +23 -0
- data/lib/fonte/parsers/log.treetop +90 -0
- data/lib/fonte/parsers/number.treetop +40 -0
- data/lib/fonte/parsers/player.treetop +33 -0
- data/lib/fonte/parsers/rcon.treetop +36 -0
- data/lib/fonte/parsers/steam_id.treetop +67 -0
- data/lib/fonte/parsers/temporal.treetop +43 -0
- data/lib/fonte/parsers/word.treetop +33 -0
- data/lib/fonte/parsers.rb +7 -0
- data/lib/fonte/version.rb +3 -0
- data/lib/fonte.rb +8 -0
- data/spec/parsers/address_parser_spec.rb +47 -0
- data/spec/parsers/log_parser_spec.rb +194 -0
- data/spec/parsers/number_parser_spec.rb +80 -0
- data/spec/parsers/player_parser_spec.rb +38 -0
- data/spec/parsers/rcon_parser_spec.rb +21 -0
- data/spec/parsers/steam_id_parser_spec.rb +144 -0
- data/spec/parsers/temporal_parser_spec.rb +31 -0
- data/spec/parsers/word_parser_spec.rb +17 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/shared_examples/rcon_command.rb +4 -0
- metadata +118 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Fonte
|
2
|
+
Fonte is a Valve Source engine log parser. Currently supporting only the basic HL Engine commands.
|
3
|
+
|
4
|
+
More info can be found at [HL Engine Log Standard documentation](https://developer.valvesoftware.com/wiki/HL_Log_Standard)
|
5
|
+
|
6
|
+
[](http://travis-ci.org/reu/fonte)
|
7
|
+
|
8
|
+
## Examples
|
9
|
+
|
10
|
+
Fonte comes with a bunch of parsers that can be used individually. Also, there is the `LogParser` which will then includes all the other `_*Parser_` to parse a full log file.
|
11
|
+
|
12
|
+
For example, if you wish to parse only a player informaton, you could use the PlayerParser:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
player = Fonte::Parsers::PlayerParser.new.parse("Reu<2><STEAM_1:1:24968171><Red>")
|
16
|
+
|
17
|
+
player.nickname.value # => "Reu"
|
18
|
+
player.steam_id.value # => "STEAM_1:1:24968171"
|
19
|
+
player.steam_id.account_number.value # => 24968171
|
20
|
+
player.team.value # => "Red"
|
21
|
+
```
|
22
|
+
|
23
|
+
Or you could parse a rcon authentication individually or within a log:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
rcon_authentication = Fonte::Parsers::PlayerParser.new.parse('Rcon: "rcon challenge "super secret" command" from "192.168.10.1:17015"')
|
27
|
+
rcon_authentication.password.value # => "super secret"
|
28
|
+
```
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
log = Fonte::Parsers::LogParser.new.parse('L 12/26/2011 - 02:14:33: Rcon: "rcon challenge "super secret" command" from "192.168.10.1:17015"')
|
32
|
+
|
33
|
+
log.command.password.value # => "super secret"
|
34
|
+
log.date_time.day # => 26
|
35
|
+
```
|
36
|
+
|
37
|
+
## Disclaimer
|
38
|
+
|
39
|
+
This is a very early approach and can be a considered a proof of concept in the current version. Lots of things should (and will) be changed. For instance, I am not quite happy with the forced _".value"_ API, but for now, it was the easiest way to implement. Also, it doesn't support real games log yet, so, there should be a way to easily "plug" specific game "log commands".
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/fonte.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fonte/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fonte"
|
7
|
+
s.version = Fonte::VERSION
|
8
|
+
s.authors = ["Rodrigo Navarro"]
|
9
|
+
s.email = ["rnavarro1@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/reu/fonte"
|
11
|
+
s.summary = "Valve Source Engine log parser"
|
12
|
+
|
13
|
+
s.rubyforge_project = "fonte"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_development_dependency "rspec"
|
21
|
+
s.add_runtime_dependency "treetop"
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Nodes
|
3
|
+
class DateTimeNode < Treetop::Runtime::SyntaxNode
|
4
|
+
def value
|
5
|
+
DateTime.new date.year.value,
|
6
|
+
date.month.value,
|
7
|
+
date.day.value,
|
8
|
+
time.hours.value,
|
9
|
+
time.minutes.value,
|
10
|
+
time.seconds.value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Nodes
|
3
|
+
class SteamIDNode < Treetop::Runtime::SyntaxNode
|
4
|
+
def value
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def real_player?
|
9
|
+
!unknown? && !bot? && !pending? && !console?
|
10
|
+
end
|
11
|
+
|
12
|
+
def unknown?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def bot?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def pending?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def console?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Nodes
|
3
|
+
class SteamIDUniverseNode < Treetop::Runtime::SyntaxNode
|
4
|
+
def value
|
5
|
+
case universe.text_value.to_i
|
6
|
+
when 0
|
7
|
+
"Unspecified"
|
8
|
+
when 1
|
9
|
+
"Public"
|
10
|
+
when 2
|
11
|
+
"Beta"
|
12
|
+
when 3
|
13
|
+
"Internal"
|
14
|
+
when 4
|
15
|
+
"Dev"
|
16
|
+
when 5
|
17
|
+
"RC"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def unspecified?
|
22
|
+
value == "Unspecified"
|
23
|
+
end
|
24
|
+
|
25
|
+
def public?
|
26
|
+
value == "Public"
|
27
|
+
end
|
28
|
+
|
29
|
+
def beta?
|
30
|
+
value == "Beta"
|
31
|
+
end
|
32
|
+
|
33
|
+
def internal?
|
34
|
+
value == "Internal"
|
35
|
+
end
|
36
|
+
|
37
|
+
def developer?
|
38
|
+
value = "Dev"
|
39
|
+
end
|
40
|
+
|
41
|
+
def rc?
|
42
|
+
value == "RC"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/fonte/nodes.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Nodes
|
3
|
+
autoload :IntegerNode, "fonte/nodes/integer_node"
|
4
|
+
autoload :SingleWordNode, "fonte/nodes/single_word_node"
|
5
|
+
autoload :DateTimeNode, "fonte/nodes/date_time_node"
|
6
|
+
autoload :IpNode, "fonte/nodes/ip_node"
|
7
|
+
autoload :SteamIDNode, "fonte/nodes/steam_id_node"
|
8
|
+
autoload :SteamIDUniverseNode, "fonte/nodes/steam_id_universe_node"
|
9
|
+
autoload :RconCommandNode, "fonte/nodes/rcon_command_node"
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Parsers
|
3
|
+
grammar Address
|
4
|
+
include Number
|
5
|
+
|
6
|
+
rule ip
|
7
|
+
octet "." octet "." octet "." octet port:port? <Fonte::Nodes::IpNode>
|
8
|
+
end
|
9
|
+
|
10
|
+
rule octet
|
11
|
+
[0-9] 1..3
|
12
|
+
end
|
13
|
+
|
14
|
+
rule port
|
15
|
+
":" integer {
|
16
|
+
def value
|
17
|
+
integer.value
|
18
|
+
end
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Parsers
|
3
|
+
grammar Log
|
4
|
+
include Word
|
5
|
+
include Temporal
|
6
|
+
include Rcon
|
7
|
+
|
8
|
+
rule entry
|
9
|
+
"L" SPACE date_time ":" SPACE command
|
10
|
+
end
|
11
|
+
|
12
|
+
rule command
|
13
|
+
log_started
|
14
|
+
/ log_ended
|
15
|
+
/ cvar_start
|
16
|
+
/ cvar_end
|
17
|
+
/ cvar_set
|
18
|
+
/ map_loading
|
19
|
+
/ map_started
|
20
|
+
/ server_name
|
21
|
+
/ server_say
|
22
|
+
/ rcon_command
|
23
|
+
end
|
24
|
+
|
25
|
+
rule log_ended
|
26
|
+
"Log file closed"
|
27
|
+
end
|
28
|
+
|
29
|
+
rule log_started
|
30
|
+
"Log file started" SPACE log_filename SPACE log_game_path SPACE log_game_version
|
31
|
+
end
|
32
|
+
|
33
|
+
rule log_filename
|
34
|
+
"(file" SPACE quoted_word ")" <Fonte::Nodes::SingleWordNode>
|
35
|
+
end
|
36
|
+
|
37
|
+
rule log_game_path
|
38
|
+
"(game" SPACE quoted_word ")" <Fonte::Nodes::SingleWordNode>
|
39
|
+
end
|
40
|
+
|
41
|
+
rule log_game_version
|
42
|
+
"(version" SPACE quoted_word ")" <Fonte::Nodes::SingleWordNode>
|
43
|
+
end
|
44
|
+
|
45
|
+
rule cvar_start
|
46
|
+
[Ss] "erver cvars start"
|
47
|
+
end
|
48
|
+
|
49
|
+
rule cvar_end
|
50
|
+
[Ss] "erver cvars end"
|
51
|
+
end
|
52
|
+
|
53
|
+
rule cvar_set
|
54
|
+
([Ss] "erver cvar" SPACE)? key:quoted_word SPACE "=" SPACE val:quoted_word {
|
55
|
+
def name
|
56
|
+
key.value
|
57
|
+
end
|
58
|
+
|
59
|
+
def value
|
60
|
+
val.value
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
rule map_loading
|
66
|
+
"Loading map" SPACE map_name
|
67
|
+
end
|
68
|
+
|
69
|
+
rule map_started
|
70
|
+
"Started map" SPACE map_name SPACE "(" map_crc ")"
|
71
|
+
end
|
72
|
+
|
73
|
+
rule map_name
|
74
|
+
quoted_word
|
75
|
+
end
|
76
|
+
|
77
|
+
rule map_crc
|
78
|
+
"CRC" SPACE quoted_word <Fonte::Nodes::SingleWordNode>
|
79
|
+
end
|
80
|
+
|
81
|
+
rule server_name
|
82
|
+
"Server name is" SPACE server_name:quoted_word <Fonte::Nodes::SingleWordNode>
|
83
|
+
end
|
84
|
+
|
85
|
+
rule server_say
|
86
|
+
"Server say" SPACE server_say:quoted_word <Fonte::Nodes::SingleWordNode>
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Parsers
|
3
|
+
grammar Number
|
4
|
+
rule number
|
5
|
+
float / integer
|
6
|
+
end
|
7
|
+
|
8
|
+
rule float
|
9
|
+
(
|
10
|
+
# Scientific notation (eg 5e7, 2.2E-4)
|
11
|
+
[+-]? ([0-9]* '.')? [0-9]+ [eE] [+-]? [0-9]+
|
12
|
+
/
|
13
|
+
# Standard float (eg -43.21, .05)
|
14
|
+
[+-]? [0-9]* '.' [0-9]+
|
15
|
+
) {
|
16
|
+
def value
|
17
|
+
Float(text_value)
|
18
|
+
end
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
rule integer
|
23
|
+
(
|
24
|
+
# Binary (eg 0b101, -0B0010)
|
25
|
+
[+-]? '0' [bB] [01]+
|
26
|
+
/
|
27
|
+
# Hex (eg 0xfff, +0XA30)
|
28
|
+
[+-]? '0' [xX] [0-9a-fA-F]+
|
29
|
+
/
|
30
|
+
# Decimal (eg 27, -421)
|
31
|
+
[+-]? [0-9]+
|
32
|
+
) {
|
33
|
+
def value
|
34
|
+
Integer(text_value)
|
35
|
+
end
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Parsers
|
3
|
+
grammar Player
|
4
|
+
include Number
|
5
|
+
include Word
|
6
|
+
include SteamID
|
7
|
+
|
8
|
+
rule player
|
9
|
+
nickname "<" uid ">" "<" steam_id ">" "<" team ">"
|
10
|
+
end
|
11
|
+
|
12
|
+
rule nickname
|
13
|
+
(![<>] .)* {
|
14
|
+
def value
|
15
|
+
text_value
|
16
|
+
end
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
rule uid
|
21
|
+
integer
|
22
|
+
end
|
23
|
+
|
24
|
+
rule team
|
25
|
+
(![<>] .)* {
|
26
|
+
def value
|
27
|
+
text_value == "Unassigned" || text_value == "Console" || text_value == "" ? nil : text_value
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Parsers
|
3
|
+
grammar Rcon
|
4
|
+
include Word
|
5
|
+
include Address
|
6
|
+
|
7
|
+
rule rcon_command
|
8
|
+
rcon_failure / rcon_success
|
9
|
+
end
|
10
|
+
|
11
|
+
rule rcon_success
|
12
|
+
"Rcon:" SPACE rcon_challenge <Fonte::Nodes::RconCommandNode>
|
13
|
+
end
|
14
|
+
|
15
|
+
rule rcon_failure
|
16
|
+
"Bad Rcon:" SPACE rcon_challenge <Fonte::Nodes::RconCommandNode>
|
17
|
+
end
|
18
|
+
|
19
|
+
rule rcon_challenge
|
20
|
+
QUOTE "rcon challenge" SPACE rcon_password SPACE "command" QUOTE SPACE rcon_origin
|
21
|
+
end
|
22
|
+
|
23
|
+
rule rcon_password
|
24
|
+
quoted_word
|
25
|
+
end
|
26
|
+
|
27
|
+
rule rcon_origin
|
28
|
+
"from" SPACE QUOTE ip QUOTE {
|
29
|
+
def value
|
30
|
+
ip.value
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Parsers
|
3
|
+
grammar SteamID
|
4
|
+
include Number
|
5
|
+
|
6
|
+
rule steam_id
|
7
|
+
steam_id_pending
|
8
|
+
/ steam_id_bot
|
9
|
+
/ steam_id_console
|
10
|
+
/ steam_id_invalid
|
11
|
+
/ universe:steam_universe ":" id_part:steam_id_part ":" account_number:steam_account_number <Fonte::Nodes::SteamIDNode> {
|
12
|
+
def value
|
13
|
+
text_value
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
rule steam_id_pending
|
19
|
+
"STEAM_ID_PENDING" <Fonte::Nodes::SteamIDNode> {
|
20
|
+
def pending?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
rule steam_id_bot
|
27
|
+
"Bot" <Fonte::Nodes::SteamIDNode> {
|
28
|
+
def bot?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
rule steam_id_console
|
35
|
+
"Console" <Fonte::Nodes::SteamIDNode> {
|
36
|
+
def console?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
rule steam_id_invalid
|
43
|
+
"UNKNOWN" <Fonte::Nodes::SteamIDNode> {
|
44
|
+
def unknown?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
rule steam_universe
|
51
|
+
"STEAM_" universe:[0-9] <Fonte::Nodes::SteamIDUniverseNode>
|
52
|
+
end
|
53
|
+
|
54
|
+
rule steam_id_part
|
55
|
+
[01] {
|
56
|
+
def value
|
57
|
+
text_value.to_i
|
58
|
+
end
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
rule steam_account_number
|
63
|
+
integer
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Parsers
|
3
|
+
grammar Temporal
|
4
|
+
include Word
|
5
|
+
|
6
|
+
rule date_time
|
7
|
+
date SPACE "-" SPACE time <Fonte::Nodes::DateTimeNode>
|
8
|
+
end
|
9
|
+
|
10
|
+
rule date
|
11
|
+
month "/" day "/" year
|
12
|
+
end
|
13
|
+
|
14
|
+
rule day
|
15
|
+
[0-3] [0-9] <Fonte::Nodes::IntegerNode>
|
16
|
+
end
|
17
|
+
|
18
|
+
rule month
|
19
|
+
[0-1] [0-9] <Fonte::Nodes::IntegerNode>
|
20
|
+
end
|
21
|
+
|
22
|
+
rule year
|
23
|
+
[0-9] [0-9] [0-9] [0-9] <Fonte::Nodes::IntegerNode>
|
24
|
+
end
|
25
|
+
|
26
|
+
rule time
|
27
|
+
hours ":" minutes ":" seconds
|
28
|
+
end
|
29
|
+
|
30
|
+
rule hours
|
31
|
+
[0-9] [0-9] <Fonte::Nodes::IntegerNode>
|
32
|
+
end
|
33
|
+
|
34
|
+
rule minutes
|
35
|
+
[0-9] [0-9] <Fonte::Nodes::IntegerNode>
|
36
|
+
end
|
37
|
+
|
38
|
+
rule seconds
|
39
|
+
[0-9] [0-9] <Fonte::Nodes::IntegerNode>
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Fonte
|
2
|
+
module Parsers
|
3
|
+
grammar Word
|
4
|
+
rule quoted_word
|
5
|
+
QUOTE word:word? QUOTE {
|
6
|
+
def value
|
7
|
+
word.value
|
8
|
+
end
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
rule word
|
13
|
+
(ESCAPED_QUOTE / !QUOTE .)* {
|
14
|
+
def value
|
15
|
+
text_value
|
16
|
+
end
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
rule SPACE
|
21
|
+
" "+
|
22
|
+
end
|
23
|
+
|
24
|
+
rule QUOTE
|
25
|
+
'"'
|
26
|
+
end
|
27
|
+
|
28
|
+
rule ESCAPED_QUOTE
|
29
|
+
'\"'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/fonte.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Fonte
|
4
|
+
module Parsers
|
5
|
+
describe AddressParser do
|
6
|
+
let(:parser) { described_class.new }
|
7
|
+
|
8
|
+
subject { parser.parse(ip) }
|
9
|
+
|
10
|
+
context "with a valid ip number" do
|
11
|
+
let(:ip) { "192.168.10.1" }
|
12
|
+
it { should be }
|
13
|
+
end
|
14
|
+
|
15
|
+
context "with a valid ip number and port" do
|
16
|
+
let(:ip) { "192.168.10.1:3000" }
|
17
|
+
it { should be }
|
18
|
+
its(:"port.value") { should == 3000 }
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with a missing octet" do
|
22
|
+
let(:ip) { "192.10.1" }
|
23
|
+
it { should_not be }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "first octet with more than 8 bits" do
|
27
|
+
let(:ip) { "1929.10.1.1" }
|
28
|
+
it { should_not be }
|
29
|
+
end
|
30
|
+
|
31
|
+
context "second octet with more than 8 bits" do
|
32
|
+
let(:ip) { "192.1110.1.1" }
|
33
|
+
it { should_not be }
|
34
|
+
end
|
35
|
+
|
36
|
+
context "first octet with more than 8 bits" do
|
37
|
+
let(:ip) { "192.10.1212.1" }
|
38
|
+
it { should_not be }
|
39
|
+
end
|
40
|
+
|
41
|
+
context "first octet with more than 8 bits" do
|
42
|
+
let(:ip) { "192.10.1.1123" }
|
43
|
+
it { should_not be }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|