whois 0.5.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +55 -0
- data/Manifest +43 -8
- data/README.rdoc +53 -20
- data/lib/whois.rb +69 -1
- data/lib/whois/answer.rb +126 -0
- data/lib/whois/answer/contact.rb +55 -0
- data/lib/whois/answer/parser.rb +110 -0
- data/lib/whois/answer/parser/README.rdoc +21 -0
- data/lib/whois/answer/parser/base.rb +144 -0
- data/lib/whois/answer/parser/blank.rb +45 -0
- data/lib/whois/answer/parser/whois.crsnic.net.rb +209 -0
- data/lib/whois/answer/parser/whois.denic.de.rb +202 -0
- data/lib/whois/answer/parser/whois.nic.it.rb +330 -0
- data/lib/whois/answer/parser/whois.publicinterestregistry.net.rb +232 -0
- data/lib/whois/answer/part.rb +35 -0
- data/lib/whois/answer/registrar.rb +42 -0
- data/lib/whois/answer/super_struct.rb +57 -0
- data/lib/whois/client.rb +6 -0
- data/lib/whois/definitions/tlds.rb +2 -2
- data/lib/whois/errors.rb +25 -8
- data/lib/whois/server.rb +0 -1
- data/lib/whois/server/adapters/afilias.rb +5 -5
- data/lib/whois/server/adapters/base.rb +31 -4
- data/lib/whois/server/adapters/formatted.rb +3 -1
- data/lib/whois/server/adapters/none.rb +20 -1
- data/lib/whois/server/adapters/pir.rb +5 -5
- data/lib/whois/server/adapters/standard.rb +13 -2
- data/lib/whois/server/adapters/verisign.rb +5 -5
- data/lib/whois/server/adapters/web.rb +15 -0
- data/lib/whois/version.rb +2 -2
- data/lib/whois/whois.rb +2 -2
- data/test/answer/parser/base_test.rb +27 -0
- data/test/answer/parser/blank_test.rb +19 -0
- data/test/answer/parser/whois_crsnic_net_test.rb +175 -0
- data/test/answer/parser/whois_denic_de_test.rb +209 -0
- data/test/answer/parser/whois_nic_it_test.rb +255 -0
- data/test/answer/parser/whois_publicinterestregistry_net_test.rb +210 -0
- data/test/answer/parser_test.rb +77 -0
- data/test/answer_test.rb +99 -0
- data/test/client_test.rb +3 -3
- data/test/integration_test.rb +31 -0
- data/test/{adapters → server/adapters}/afilias_test.rb +10 -5
- data/test/server/adapters/base_test.rb +15 -0
- data/test/server/adapters/formatted_test.rb +26 -0
- data/test/{adapters → server/adapters}/none_test.rb +0 -0
- data/test/{adapters → server/adapters}/not_implemented_test.rb +0 -0
- data/test/{adapters → server/adapters}/pir_test.rb +10 -5
- data/test/server/adapters/standard_test.rb +29 -0
- data/test/{adapters → server/adapters}/verisign_test.rb +10 -5
- data/test/{adapters → server/adapters}/web_test.rb +0 -0
- data/test/server_test.rb +5 -1
- data/test/testcases/responses/super_struct_test.rb +25 -0
- data/test/testcases/responses/whois.crsnic.net/available.txt +43 -0
- data/test/testcases/responses/whois.crsnic.net/registered.txt +57 -0
- data/test/testcases/responses/whois.denic.de/available.txt +30 -0
- data/test/testcases/responses/whois.denic.de/registered.txt +77 -0
- data/test/testcases/responses/whois.nic.it/available.txt +2 -0
- data/test/testcases/responses/{it.txt → whois.nic.it/registered.txt} +1 -0
- data/test/testcases/responses/whois.nic.it/status_active.txt +53 -0
- data/test/testcases/responses/whois.nic.it/status_available.txt +2 -0
- data/test/testcases/responses/whois.publicinterestregistry.net/available.txt +1 -0
- data/test/testcases/responses/whois.publicinterestregistry.net/registered.txt +85 -0
- data/test/whois_test.rb +23 -1
- data/utils/bm_delegation_vs_inheritance.rb +150 -0
- data/whois.gemspec +6 -6
- metadata +78 -18
- data/test/adapters/standard_test.rb +0 -23
@@ -0,0 +1,55 @@
|
|
1
|
+
#
|
2
|
+
# = Ruby Whois
|
3
|
+
#
|
4
|
+
# An intelligent pure Ruby WHOIS client.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Category:: Net
|
8
|
+
# Package:: Whois
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
+
# License:: MIT License
|
11
|
+
#
|
12
|
+
#--
|
13
|
+
#
|
14
|
+
#++
|
15
|
+
|
16
|
+
|
17
|
+
require 'whois/answer/super_struct'
|
18
|
+
|
19
|
+
|
20
|
+
module Whois
|
21
|
+
class Answer
|
22
|
+
|
23
|
+
#
|
24
|
+
# = Contacts
|
25
|
+
#
|
26
|
+
# Holds the details of a Contact extracted from the WHOIS answer.
|
27
|
+
#
|
28
|
+
# A Contact is composed by the following attributes:
|
29
|
+
#
|
30
|
+
# <tt>:id</tt>::
|
31
|
+
# <tt>:name</tt>::
|
32
|
+
# <tt>:organization</tt>::
|
33
|
+
# <tt>:address</tt>::
|
34
|
+
# <tt>:city</tt>::
|
35
|
+
# <tt>:zip</tt>::
|
36
|
+
# <tt>:state</tt>::
|
37
|
+
# <tt>:country</tt>::
|
38
|
+
# <tt>:country_code</tt>::
|
39
|
+
# <tt>:phone</tt>::
|
40
|
+
# <tt>:fax</tt>::
|
41
|
+
# <tt>:email</tt>::
|
42
|
+
# <tt>:created_on</tt>::
|
43
|
+
# <tt>:updated_on</tt>::
|
44
|
+
#
|
45
|
+
# Be aware that every WHOIS server can return a different number of details
|
46
|
+
# or no details at all.
|
47
|
+
#
|
48
|
+
class Contact < SuperStruct.new(:id, :name, :organization,
|
49
|
+
:address, :city, :zip, :state, :country, :country_code,
|
50
|
+
:phone, :fax, :email,
|
51
|
+
:created_on, :updated_on)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#
|
2
|
+
# = Ruby Whois
|
3
|
+
#
|
4
|
+
# An intelligent pure Ruby WHOIS client.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Category:: Net
|
8
|
+
# Package:: Whois
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
+
# License:: MIT License
|
11
|
+
#
|
12
|
+
#--
|
13
|
+
#
|
14
|
+
#++
|
15
|
+
|
16
|
+
|
17
|
+
module Whois
|
18
|
+
class Answer
|
19
|
+
|
20
|
+
#
|
21
|
+
# = Parser
|
22
|
+
#
|
23
|
+
class Parser
|
24
|
+
|
25
|
+
@@registrable_methods = [
|
26
|
+
:disclaimer,
|
27
|
+
:domain, :domain_id,
|
28
|
+
:referral_whois, :referral_url,
|
29
|
+
:status, :registered?, :available?,
|
30
|
+
:created_on, :updated_on, :expires_on,
|
31
|
+
:registrar, :registrant, :admin, :technical,
|
32
|
+
:nameservers,
|
33
|
+
]
|
34
|
+
|
35
|
+
# Returns an array containing the name of all methods
|
36
|
+
# that can be registered and should be implemented by
|
37
|
+
# server-specific parsers.
|
38
|
+
def self.registrable_methods
|
39
|
+
@@registrable_methods
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :answer
|
43
|
+
|
44
|
+
|
45
|
+
def initialize(answer)
|
46
|
+
@answer = answer
|
47
|
+
end
|
48
|
+
|
49
|
+
def parsers
|
50
|
+
@parsers ||= init_parsers
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def method_missing(method, *args, &block)
|
57
|
+
if Parser.registrable_methods.include?(method)
|
58
|
+
if parsers.empty?
|
59
|
+
raise ParserError, "Unable to select a parser because the answer is empty"
|
60
|
+
elsif parser = select_parser(method)
|
61
|
+
parser.send(method, *args, &block)
|
62
|
+
else
|
63
|
+
raise PropertyNotSupported, "Unable to find a parser for `#{method}'"
|
64
|
+
end
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Loops through all answer parts, for each parts tries to guess
|
71
|
+
# the appropriate Whois::Answer::Parser::<parser> if it exists
|
72
|
+
# and returns the final array of server-specific parsers.
|
73
|
+
def init_parsers
|
74
|
+
answer.parts.map { |part| self.class.parser_for(part) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def select_parser(method)
|
78
|
+
parsers.reverse.each do |parser|
|
79
|
+
return parser if parser.method_registered?(method)
|
80
|
+
end
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def self.parser_for(part)
|
86
|
+
parser_klass(part.host).new(part)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.parser_klass(host)
|
90
|
+
file = "whois/answer/parser/#{host}"
|
91
|
+
require file
|
92
|
+
|
93
|
+
name = host_to_parser(host)
|
94
|
+
Parser.const_get(name)
|
95
|
+
|
96
|
+
rescue LoadError
|
97
|
+
require "whois/answer/parser/blank"
|
98
|
+
Parser::Blank
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.host_to_parser(host)
|
102
|
+
host.to_s.
|
103
|
+
gsub(/\./, '_').
|
104
|
+
gsub(/(?:^|_)(.)/) { $1.upcase }
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
disclaimer String
|
2
|
+
|
3
|
+
domain String
|
4
|
+
domain_id String
|
5
|
+
|
6
|
+
status Enumerator/Array
|
7
|
+
available? Boolean
|
8
|
+
registered? Boolean
|
9
|
+
|
10
|
+
created_on Time
|
11
|
+
updated_on Time
|
12
|
+
expires_on Time
|
13
|
+
|
14
|
+
registrar <Registrar :id, :name, :organization, :url, ...>
|
15
|
+
|
16
|
+
|
17
|
+
registrant <Contact :id, :name, :organization, ...>
|
18
|
+
admin <Contact :id, :name, :organization, ...>
|
19
|
+
tech <Contact :id, :name, :organization, ...>
|
20
|
+
|
21
|
+
nameservers Array
|
@@ -0,0 +1,144 @@
|
|
1
|
+
#
|
2
|
+
# = Ruby Whois
|
3
|
+
#
|
4
|
+
# An intelligent pure Ruby WHOIS client.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Category:: Net
|
8
|
+
# Package:: Whois
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
+
# License:: MIT License
|
11
|
+
#
|
12
|
+
#--
|
13
|
+
#
|
14
|
+
#++
|
15
|
+
|
16
|
+
|
17
|
+
require 'strscan'
|
18
|
+
require 'time'
|
19
|
+
require 'whois/answer/contact'
|
20
|
+
require 'whois/answer/registrar'
|
21
|
+
|
22
|
+
|
23
|
+
module Whois
|
24
|
+
class Answer
|
25
|
+
class Parser
|
26
|
+
|
27
|
+
#
|
28
|
+
# = Base Answer Parser
|
29
|
+
#
|
30
|
+
# This class is intended to be the base abstract class for all
|
31
|
+
# server-specific parser implementations.
|
32
|
+
#
|
33
|
+
# == Available Methods
|
34
|
+
#
|
35
|
+
# The Base class is for the most part auto-generated via meta programming.
|
36
|
+
# This is the reason why RDoc can't detect and document all available methods.
|
37
|
+
#
|
38
|
+
class Base
|
39
|
+
|
40
|
+
attr_reader :part
|
41
|
+
|
42
|
+
|
43
|
+
def initialize(part)
|
44
|
+
@part = part
|
45
|
+
end
|
46
|
+
|
47
|
+
::Whois::Answer::Parser.registrable_methods.each do |method|
|
48
|
+
define_method(method) do
|
49
|
+
raise PropertyNotImplemented, "You should overwrite this method."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# This is an internal method primaly used as a common access point
|
55
|
+
# to get the content to be parsed as a string.
|
56
|
+
#
|
57
|
+
# The main reason behind this method is because I changed the internal
|
58
|
+
# representation of the data to be parsed more than once
|
59
|
+
# and I always had to rewrite all single parsers in order to reflect these changes.
|
60
|
+
# Now, as far as the parser access the data via the content method,
|
61
|
+
# there's no need to change each single implementation in case the content source changes.
|
62
|
+
#
|
63
|
+
# That said, the only constraints about this method is to return the data to be parsed as string.
|
64
|
+
#
|
65
|
+
def content
|
66
|
+
part.response
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
@@method_registry = {}
|
71
|
+
|
72
|
+
#
|
73
|
+
# :call-seq:
|
74
|
+
# method_registry => hash
|
75
|
+
# method_registry(:key) => array
|
76
|
+
#
|
77
|
+
# Returns the <tt>@@method_registry</tt> if <tt>key</tt> is nil,
|
78
|
+
# otherwise returns the value in <tt>@@method_registry</tt> for given <tt>key</tt>.
|
79
|
+
#
|
80
|
+
# <tt>@@method_registry</tt> is always a Hash while <tt>@@method_registry[:key]</tt>
|
81
|
+
# is always an array. If <tt>@@method_registry[:key]</tt> doesn't exist, this method
|
82
|
+
# automatically initializes it to an empty array.
|
83
|
+
#
|
84
|
+
def self.method_registry(key = nil)
|
85
|
+
if key.nil?
|
86
|
+
@@method_registry
|
87
|
+
else
|
88
|
+
@@method_registry[key] ||= []
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns true if <tt>method</tt> is registered for current class.
|
93
|
+
#
|
94
|
+
# method_registered?(:disclaimer)
|
95
|
+
# # => false
|
96
|
+
#
|
97
|
+
# register_method(:discaimer) {}
|
98
|
+
# method_registered?(:disclaimer)
|
99
|
+
# # => true
|
100
|
+
#
|
101
|
+
def self.method_registered?(method)
|
102
|
+
method_registry(self).include?(method)
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# :call-seq:
|
107
|
+
# register_method(:method) { }
|
108
|
+
# register_method(:method) { |parameter| ... }
|
109
|
+
# register_method(:method) { |parameter, ...| ... }
|
110
|
+
#
|
111
|
+
# Creates <tt>method</tt> with the content of <tt>block</tt>
|
112
|
+
# and automatically registers <tt>method</tt> for current class.
|
113
|
+
#
|
114
|
+
# register_method(:discaimer) do
|
115
|
+
# ...
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# register_method(:changed?) do |other|
|
119
|
+
# ...
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# method_registered?(:disclaimer)
|
123
|
+
# # => true
|
124
|
+
#
|
125
|
+
def self.register_method(method, &block)
|
126
|
+
method_registry(self) << method
|
127
|
+
define_method(method, &block)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Instance-level version of <tt>Base.method_registered?</tt>.
|
131
|
+
def method_registered?(method)
|
132
|
+
self.class.method_registered?(method)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Instance-level version of <tt>Base.register_method</tt>.
|
136
|
+
def register_method(method, &block)
|
137
|
+
self.class.register_method(method, &block)
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# = Ruby Whois
|
3
|
+
#
|
4
|
+
# An intelligent pure Ruby WHOIS client.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Category:: Net
|
8
|
+
# Package:: Whois
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
+
# License:: MIT License
|
11
|
+
#
|
12
|
+
#--
|
13
|
+
#
|
14
|
+
#++
|
15
|
+
|
16
|
+
|
17
|
+
require 'whois/answer/parser/base'
|
18
|
+
|
19
|
+
|
20
|
+
module Whois
|
21
|
+
class Answer
|
22
|
+
class Parser
|
23
|
+
|
24
|
+
#
|
25
|
+
# = Blank parser
|
26
|
+
#
|
27
|
+
# The Blank parser isn't a real parser. It's just a fake parser
|
28
|
+
# that acts as a parser but doesn't provide any special capability.
|
29
|
+
# It doesn't register itself in the parser_registry, it doesn't scan any string,
|
30
|
+
# it only exists to be initialized in case an answer needs to create a parser
|
31
|
+
# for a whois server not supported yet.
|
32
|
+
#
|
33
|
+
class Blank < Base
|
34
|
+
|
35
|
+
::Whois::Answer::Parser.registrable_methods.each do |method|
|
36
|
+
define_method(method) do
|
37
|
+
raise ParserNotFound, "Unable to find a parser for the server `#{part.host}'"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
#
|
2
|
+
# = Ruby Whois
|
3
|
+
#
|
4
|
+
# An intelligent pure Ruby WHOIS client.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Category:: Net
|
8
|
+
# Package:: Whois
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
+
# License:: MIT License
|
11
|
+
#
|
12
|
+
#--
|
13
|
+
#
|
14
|
+
#++
|
15
|
+
|
16
|
+
|
17
|
+
require 'whois/answer/parser/base'
|
18
|
+
|
19
|
+
|
20
|
+
module Whois
|
21
|
+
class Answer
|
22
|
+
class Parser
|
23
|
+
|
24
|
+
#
|
25
|
+
# = whois.crsnic.net.rb parser
|
26
|
+
#
|
27
|
+
# Parser for the whois.crsnic.net.rb server.
|
28
|
+
#
|
29
|
+
class WhoisCrsnicNet < Base
|
30
|
+
|
31
|
+
register_method :disclaimer do
|
32
|
+
node("Disclaimer")
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
register_method :domain do
|
37
|
+
node("Domain Name") { |raw| raw.downcase }
|
38
|
+
end
|
39
|
+
|
40
|
+
register_method :domain_id do
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
register_method :referral_whois do
|
46
|
+
node("Whois Server")
|
47
|
+
end
|
48
|
+
|
49
|
+
register_method :referral_url do
|
50
|
+
node("Referral URL")
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
register_method :status do
|
55
|
+
node("Status")
|
56
|
+
end
|
57
|
+
|
58
|
+
register_method :available? do
|
59
|
+
node("Registrar").nil?
|
60
|
+
end
|
61
|
+
|
62
|
+
register_method :registered? do
|
63
|
+
!available?
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
register_method :created_on do
|
68
|
+
node("Creation Date") { |raw| Time.parse(raw) }
|
69
|
+
end
|
70
|
+
|
71
|
+
register_method :updated_on do
|
72
|
+
node("Updated Date") { |raw| Time.parse(raw) }
|
73
|
+
end
|
74
|
+
|
75
|
+
register_method :expires_on do
|
76
|
+
node("Expiration Date") { |raw| Time.parse(raw) }
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
register_method :registrar do
|
81
|
+
# Return nil because when the response contains more than one registrar section
|
82
|
+
# the response can be messy. See, for instance, the Verisign response for google.com.
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def ast
|
90
|
+
@ast ||= parse
|
91
|
+
end
|
92
|
+
|
93
|
+
def node(key, &block)
|
94
|
+
if block_given?
|
95
|
+
value = ast[key]
|
96
|
+
value = yield(value) unless value.nil?
|
97
|
+
value
|
98
|
+
else
|
99
|
+
ast[key]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def node?(key)
|
104
|
+
!ast[key].nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse
|
108
|
+
Scanner.new(content.to_s).parse
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
class Scanner
|
113
|
+
|
114
|
+
def initialize(content)
|
115
|
+
content = content.to_s.gsub("\r", "")
|
116
|
+
@input = StringScanner.new(content.to_s)
|
117
|
+
end
|
118
|
+
|
119
|
+
def parse
|
120
|
+
@ast = {}
|
121
|
+
while !@input.eos?
|
122
|
+
parse_content
|
123
|
+
end
|
124
|
+
@ast
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def parse_content
|
130
|
+
trim_newline ||
|
131
|
+
parse_not_found ||
|
132
|
+
parse_disclaimer ||
|
133
|
+
parse_notice ||
|
134
|
+
parse_pair ||
|
135
|
+
trim_last_update ||
|
136
|
+
trim_fuffa ||
|
137
|
+
error("Unexpected token")
|
138
|
+
end
|
139
|
+
|
140
|
+
def trim_newline
|
141
|
+
@input.scan(/\n/)
|
142
|
+
end
|
143
|
+
|
144
|
+
def trim_last_update
|
145
|
+
@input.scan(/>>>(.*?)<<<\n/)
|
146
|
+
end
|
147
|
+
|
148
|
+
def trim_fuffa
|
149
|
+
@input.scan(/^\w(.*)\n/) ||
|
150
|
+
(@input.scan(/^\w(.*)/) and @input.eos?)
|
151
|
+
end
|
152
|
+
|
153
|
+
def parse_not_found
|
154
|
+
if @input.scan(/No match for "(.*?)"\.\n/)
|
155
|
+
@ast["Domain Name"] = @input[1].strip
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# NOTE: parse_notice and parse_disclaimer are similar!
|
160
|
+
def parse_notice
|
161
|
+
if @input.match?(/NOTICE:/)
|
162
|
+
lines = []
|
163
|
+
while !@input.match?(/\n/) && @input.scan(/(.*)\n/)
|
164
|
+
lines << @input[1].strip
|
165
|
+
end
|
166
|
+
@ast["Notice"] = lines.join(" ")
|
167
|
+
else
|
168
|
+
false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def parse_disclaimer
|
173
|
+
if @input.match?(/TERMS OF USE:/)
|
174
|
+
lines = []
|
175
|
+
while !@input.match?(/\n/) && @input.scan(/(.*)\n/)
|
176
|
+
lines << @input[1].strip
|
177
|
+
end
|
178
|
+
@ast["Disclaimer"] = lines.join(" ")
|
179
|
+
else
|
180
|
+
false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def parse_pair
|
185
|
+
if @input.scan(/\s+(.*?):(.*?)\n/)
|
186
|
+
key, value = @input[1].strip, @input[2].strip
|
187
|
+
if @ast[key].nil?
|
188
|
+
@ast[key] = value
|
189
|
+
else
|
190
|
+
@ast[key].is_a?(Array) || @ast[key] = [@ast[key]]
|
191
|
+
@ast[key] << value
|
192
|
+
end
|
193
|
+
else
|
194
|
+
false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def error(message)
|
199
|
+
raise "#{message}: #{@input.peek(@input.string.length)}"
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|