From c27bb51921752c9ef0446cc20c4ba0fe7c2b8d33 Mon Sep 17 00:00:00 2001 From: Simone Carletti Date: Tue, 18 Sep 2012 16:46:21 +0200 Subject: [PATCH 1/5] Update whois.nic.co responses --- .../whois.nic.co/status_available.txt | 56 +++--- .../whois.nic.co/status_registered.expected | 12 +- .../whois.nic.co/status_registered.txt | 166 ++++++++++-------- .../whois.nic.co/status_registered_spec.rb | 12 +- 4 files changed, 139 insertions(+), 107 deletions(-) diff --git a/spec/fixtures/responses/whois.nic.co/status_available.txt b/spec/fixtures/responses/whois.nic.co/status_available.txt index 49afa5c1e..bd459568c 100644 --- a/spec/fixtures/responses/whois.nic.co/status_available.txt +++ b/spec/fixtures/responses/whois.nic.co/status_available.txt @@ -1,28 +1,38 @@ Not found: u34jedzcq.co ->>>> Whois database was last updated on: Tue Jul 20 09:39:19 GMT 2010 <<<< +>>>> Whois database was last updated on: Tue Sep 18 14:32:43 GMT 2012 <<<< .CO Internet, S.A.S., the Administrator for .CO, has collected this -information for the WHOIS database through Accredited Registrars. -This information is provided to you for informational purposes only and is -designed to assist persons in determining contents of a domain name -registration record in the .CO Internet registry database. .CO Internet makes this -information available to you "as is" and does not guarantee its accuracy. -By submitting a WHOIS query, you agree that you will use this data only for -lawful purposes and that, under no circumstances will you use this data: -(1) to allow, enable, or otherwise support the transmission of mass -unsolicited, commercial advertising or solicitations via direct mail, -electronic mail, or by telephone; (2) in contravention of any applicable -data and privacy protection laws; or (3) to enable high volume, automated, -electronic processes that apply to the registry (or its systems). Compilation, -repackaging, dissemination, or other use of the WHOIS database in its -entirety, or of a substantial portion thereof, is not allowed without -.CO Internet’s prior written permission. .CO Internet reserves the right to modify or -change these conditions at any time without prior or subsequent notification -of any kind. By executing this query, in any manner whatsoever, you agree to -abide by these terms. +information for the WHOIS database through Accredited Registrars. +This information is provided to you for informational purposes only +and is designed to assist persons in determining contents of a domain +name registration record in the .CO Internet registry database. .CO +Internet makes this information available to you "as is" and does not +guarantee its accuracy. -NOTE: FAILURE TO LOCATE A RECORD IN THE WHOIS DATABASE IS NOT INDICATIVE -OF THE AVAILABILITY OF A DOMAIN NAME. +By submitting a WHOIS query, you agree that you will use this data +only for lawful purposes and that, under no circumstances will you +use this data: (1) to allow, enable, or otherwise support the transmission +of mass unsolicited, commercial advertising or solicitations via direct +mail, electronic mail, or by telephone; (2) in contravention of any +applicable data and privacy protection laws; or (3) to enable high volume, +automated, electronic processes that apply to the registry (or its systems). +Compilation, repackaging, dissemination, or other use of the WHOIS +database in its entirety, or of a substantial portion thereof, is not allowed +without .CO Internet's prior written permission. .CO Internet reserves the +right to modify or change these conditions at any time without prior or +subsequent notification of any kind. By executing this query, in any manner +whatsoever, you agree to abide by these terms. In some limited cases, +domains that might appear as available in whois might not actually be +available as they could be already registered and the whois not yet updated +and/or they could be part of the Restricted list. In this cases, performing a +check through your Registrar's (EPP check) will give you the actual status +of the domain. Additionally, domains currently or previously used as +extensions in 3rd level domains will not be available for registration in the +2nd level. For example, org.co,mil.co,edu.co,com.co,net.co,nom.co,arts.co, +firm.co,info.co,int.co,web.co,rec.co,co.co. -All domain names are subject to certain additional domain name registration -rules. For details, please visit our site at www.whois.co . +NOTE: FAILURE TO LOCATE A RECORD IN THE WHOIS DATABASE IS NOT +INDICATIVE OF THE AVAILABILITY OF A DOMAIN NAME. + +All domain names are subject to certain additional domain name registration +rules. For details, please visit our site at www.cointernet.co . diff --git a/spec/fixtures/responses/whois.nic.co/status_registered.expected b/spec/fixtures/responses/whois.nic.co/status_registered.expected index d40209eb7..0d316d16a 100644 --- a/spec/fixtures/responses/whois.nic.co/status_registered.expected +++ b/spec/fixtures/responses/whois.nic.co/status_registered.expected @@ -1,5 +1,5 @@ #status - should: %s == ["serverTransferProhibited"] + should: %s == ["serverDeleteProhibited", "serverTransferProhibited"] #available? should: %s == false @@ -14,7 +14,7 @@ #updated_on should: %s CLASS(time) - should: %s == Time.parse("2010-07-06 18:54:16 UTC") + should: %s == Time.parse("2012-08-15 20:57:42") #expires_on should: %s CLASS(time) @@ -24,10 +24,10 @@ should: %s CLASS(array) should: %s SIZE(4) should: %s[0] CLASS(nameserver) - should: %s[0].name == "ns1.p26.dynect.net" + should: %s[0].name == "ns1.p34.dynect.net" should: %s[1] CLASS(nameserver) - should: %s[1].name == "ns2.p26.dynect.net" + should: %s[1].name == "ns2.p34.dynect.net" should: %s[2] CLASS(nameserver) - should: %s[2].name == "ns3.p26.dynect.net" + should: %s[2].name == "ns3.p34.dynect.net" should: %s[3] CLASS(nameserver) - should: %s[3].name == "ns4.p26.dynect.net" + should: %s[3].name == "ns4.p34.dynect.net" diff --git a/spec/fixtures/responses/whois.nic.co/status_registered.txt b/spec/fixtures/responses/whois.nic.co/status_registered.txt index d0261f1b7..b6860ca83 100644 --- a/spec/fixtures/responses/whois.nic.co/status_registered.txt +++ b/spec/fixtures/responses/whois.nic.co/status_registered.txt @@ -1,82 +1,104 @@ Domain Name: T.CO Domain ID: D740225-CO -Sponsoring Registrar: RESTRICTED AND RESERVED NAMES .COINTERNET -Sponsoring Registrar IANA ID: 672943168 -Registrar URL (registration services): www.cointernet.com.co +Sponsoring Registrar: MELBOURNE IT LTD +Sponsoring Registrar IANA ID: 13 +Registrar URL (registration services): www.melbourneit.com.au +Domain Status: serverDeleteProhibited Domain Status: serverTransferProhibited -Registrant ID: COFOUNDERS -Registrant Name: .CO Founders program -Registrant Organization: .CO Internet S.A.S. -Registrant Address1: Calle 100# 8a-49 Torre B Oficina 507 -Registrant City: Bogota -Registrant Postal Code: 00000 -Registrant Country: Colombia -Registrant Country Code: CO -Registrant Phone Number: +571.6169916 -Registrant Email: cofounders@cointernet.co -Administrative Contact ID: COFOUNDERS -Administrative Contact Name: .CO Founders program -Administrative Contact Organization: .CO Internet S.A.S. -Administrative Contact Address1: Calle 100# 8a-49 Torre B Oficina 507 -Administrative Contact City: Bogota -Administrative Contact Postal Code: 00000 -Administrative Contact Country: Colombia -Administrative Contact Country Code: CO -Administrative Contact Phone Number: +571.6169916 -Administrative Contact Email: cofounders@cointernet.co -Billing Contact ID: COFOUNDERS -Billing Contact Name: .CO Founders program -Billing Contact Organization: .CO Internet S.A.S. -Billing Contact Address1: Calle 100# 8a-49 Torre B Oficina 507 -Billing Contact City: Bogota -Billing Contact Postal Code: 00000 -Billing Contact Country: Colombia -Billing Contact Country Code: CO -Billing Contact Phone Number: +571.6169916 -Billing Contact Email: cofounders@cointernet.co -Technical Contact ID: COFOUNDERS -Technical Contact Name: .CO Founders program -Technical Contact Organization: .CO Internet S.A.S. -Technical Contact Address1: Calle 100# 8a-49 Torre B Oficina 507 -Technical Contact City: Bogota -Technical Contact Postal Code: 00000 -Technical Contact Country: Colombia -Technical Contact Country Code: CO -Technical Contact Phone Number: +571.6169916 -Technical Contact Email: cofounders@cointernet.co -Name Server: NS1.P26.DYNECT.NET -Name Server: NS2.P26.DYNECT.NET -Name Server: NS3.P26.DYNECT.NET -Name Server: NS4.P26.DYNECT.NET +Registrant ID: TWITTERREG2012 +Registrant Name: Twitter, Inc. +Registrant Organization: Twitter, Inc. +Registrant Address1: 1355 Market Street +Registrant Address2: Suite 900 +Registrant City: San Francisco +Registrant State/Province: CA +Registrant Postal Code: 94103 +Registrant Country: United States +Registrant Country Code: US +Registrant Phone Number: +1.4152229670 +Registrant Facsimile Number: +1.4152220922 +Registrant Email: domains@twitter.com +Administrative Contact ID: TWITTERADMIN2012 +Administrative Contact Name: Domain Admin +Administrative Contact Organization: Twitter, Inc. +Administrative Contact Address1: 1355 Market Street +Administrative Contact Address2: Suite 900 +Administrative Contact City: San Francisco +Administrative Contact State/Province: CA +Administrative Contact Postal Code: 94103 +Administrative Contact Country: United States +Administrative Contact Country Code: US +Administrative Contact Phone Number: +1.4152229670 +Administrative Contact Facsimile Number: +1.4152220922 +Administrative Contact Email: domains@twitter.com +Billing Contact ID: MIT_BILLING +Billing Contact Name: DBS Billing +Billing Contact Address1: 636 Ellis Street +Billing Contact City: Mountain View +Billing Contact State/Province: CA +Billing Contact Postal Code: 94043 +Billing Contact Country: United States +Billing Contact Country Code: US +Billing Contact Phone Number: +1.8669073267 +Billing Contact Facsimile Number: +1.6506182574 +Billing Contact Email: billing@melbourneitdbs.com +Technical Contact ID: TWITTERTECH2012 +Technical Contact Name: Tech Admin +Technical Contact Organization: Twitter, Inc. +Technical Contact Address1: 1355 Market Street +Technical Contact Address2: Suite 900 +Technical Contact City: San Francisco +Technical Contact State/Province: CA +Technical Contact Postal Code: 94103 +Technical Contact Country: United States +Technical Contact Country Code: US +Technical Contact Phone Number: +1.4152229670 +Technical Contact Facsimile Number: +1.4152220922 +Technical Contact Email: domains-tech@twitter.com +Name Server: NS1.P34.DYNECT.NET +Name Server: NS2.P34.DYNECT.NET +Name Server: NS3.P34.DYNECT.NET +Name Server: NS4.P34.DYNECT.NET Created by Registrar: INJECTCSR -Last Updated by Registrar: NEULEVELCSR +Last Updated by Registrar: MELBOURNE IT LTD +Last Transferred Date: Mon Mar 14 22:21:58 GMT 2011 Domain Registration Date: Mon Apr 26 07:50:40 GMT 2010 Domain Expiration Date: Thu Apr 25 23:59:59 GMT 2013 -Domain Last Updated Date: Tue Jul 06 18:54:16 GMT 2010 +Domain Last Updated Date: Wed Aug 15 18:57:42 GMT 2012 ->>>> Whois database was last updated on: Tue Jul 20 09:39:19 GMT 2010 <<<< +>>>> Whois database was last updated on: Tue Sep 18 14:32:43 GMT 2012 <<<< .CO Internet, S.A.S., the Administrator for .CO, has collected this -information for the WHOIS database through Accredited Registrars. -This information is provided to you for informational purposes only and is -designed to assist persons in determining contents of a domain name -registration record in the .CO Internet registry database. .CO Internet makes this -information available to you "as is" and does not guarantee its accuracy. -By submitting a WHOIS query, you agree that you will use this data only for -lawful purposes and that, under no circumstances will you use this data: -(1) to allow, enable, or otherwise support the transmission of mass -unsolicited, commercial advertising or solicitations via direct mail, -electronic mail, or by telephone; (2) in contravention of any applicable -data and privacy protection laws; or (3) to enable high volume, automated, -electronic processes that apply to the registry (or its systems). Compilation, -repackaging, dissemination, or other use of the WHOIS database in its -entirety, or of a substantial portion thereof, is not allowed without -.CO Internet’s prior written permission. .CO Internet reserves the right to modify or -change these conditions at any time without prior or subsequent notification -of any kind. By executing this query, in any manner whatsoever, you agree to -abide by these terms. +information for the WHOIS database through Accredited Registrars. +This information is provided to you for informational purposes only +and is designed to assist persons in determining contents of a domain +name registration record in the .CO Internet registry database. .CO +Internet makes this information available to you "as is" and does not +guarantee its accuracy. -NOTE: FAILURE TO LOCATE A RECORD IN THE WHOIS DATABASE IS NOT INDICATIVE -OF THE AVAILABILITY OF A DOMAIN NAME. +By submitting a WHOIS query, you agree that you will use this data +only for lawful purposes and that, under no circumstances will you +use this data: (1) to allow, enable, or otherwise support the transmission +of mass unsolicited, commercial advertising or solicitations via direct +mail, electronic mail, or by telephone; (2) in contravention of any +applicable data and privacy protection laws; or (3) to enable high volume, +automated, electronic processes that apply to the registry (or its systems). +Compilation, repackaging, dissemination, or other use of the WHOIS +database in its entirety, or of a substantial portion thereof, is not allowed +without .CO Internet's prior written permission. .CO Internet reserves the +right to modify or change these conditions at any time without prior or +subsequent notification of any kind. By executing this query, in any manner +whatsoever, you agree to abide by these terms. In some limited cases, +domains that might appear as available in whois might not actually be +available as they could be already registered and the whois not yet updated +and/or they could be part of the Restricted list. In this cases, performing a +check through your Registrar's (EPP check) will give you the actual status +of the domain. Additionally, domains currently or previously used as +extensions in 3rd level domains will not be available for registration in the +2nd level. For example, org.co,mil.co,edu.co,com.co,net.co,nom.co,arts.co, +firm.co,info.co,int.co,web.co,rec.co,co.co. -All domain names are subject to certain additional domain name registration -rules. For details, please visit our site at www.whois.co . +NOTE: FAILURE TO LOCATE A RECORD IN THE WHOIS DATABASE IS NOT +INDICATIVE OF THE AVAILABILITY OF A DOMAIN NAME. + +All domain names are subject to certain additional domain name registration +rules. For details, please visit our site at www.cointernet.co . diff --git a/spec/whois/record/parser/responses/whois.nic.co/status_registered_spec.rb b/spec/whois/record/parser/responses/whois.nic.co/status_registered_spec.rb index af5b963f8..7344d8f63 100644 --- a/spec/whois/record/parser/responses/whois.nic.co/status_registered_spec.rb +++ b/spec/whois/record/parser/responses/whois.nic.co/status_registered_spec.rb @@ -23,7 +23,7 @@ describe "#status" do it do - subject.status.should == ["serverTransferProhibited"] + subject.status.should == ["serverDeleteProhibited", "serverTransferProhibited"] end end describe "#available?" do @@ -45,7 +45,7 @@ describe "#updated_on" do it do subject.updated_on.should be_a(Time) - subject.updated_on.should == Time.parse("2010-07-06 18:54:16 UTC") + subject.updated_on.should == Time.parse("2012-08-15 20:57:42") end end describe "#expires_on" do @@ -59,13 +59,13 @@ subject.nameservers.should be_a(Array) subject.nameservers.should have(4).items subject.nameservers[0].should be_a(Whois::Record::Nameserver) - subject.nameservers[0].name.should == "ns1.p26.dynect.net" + subject.nameservers[0].name.should == "ns1.p34.dynect.net" subject.nameservers[1].should be_a(Whois::Record::Nameserver) - subject.nameservers[1].name.should == "ns2.p26.dynect.net" + subject.nameservers[1].name.should == "ns2.p34.dynect.net" subject.nameservers[2].should be_a(Whois::Record::Nameserver) - subject.nameservers[2].name.should == "ns3.p26.dynect.net" + subject.nameservers[2].name.should == "ns3.p34.dynect.net" subject.nameservers[3].should be_a(Whois::Record::Nameserver) - subject.nameservers[3].name.should == "ns4.p26.dynect.net" + subject.nameservers[3].name.should == "ns4.p34.dynect.net" end end end From a0f2812011f8feb7f062256b4a8ca144431df011 Mon Sep 17 00:00:00 2001 From: Simone Carletti Date: Tue, 18 Sep 2012 18:16:41 +0200 Subject: [PATCH 2/5] Extract BaseShared2 scanner/parser from WhoisBiz. --- lib/whois/record/parser/base_shared1.rb | 2 +- lib/whois/record/parser/base_shared2.rb | 138 ++++++++++++++++++ lib/whois/record/parser/whois.biz.rb | 119 +-------------- lib/whois/record/parser/whois.registry.qa.rb | 3 + lib/whois/record/scanners/base_shared1.rb | 2 +- .../{whois.biz.rb => base_shared2.rb} | 3 +- 6 files changed, 151 insertions(+), 116 deletions(-) create mode 100644 lib/whois/record/parser/base_shared2.rb rename lib/whois/record/scanners/{whois.biz.rb => base_shared2.rb} (91%) diff --git a/lib/whois/record/parser/base_shared1.rb b/lib/whois/record/parser/base_shared1.rb index 594667ed2..bc9d2303b 100644 --- a/lib/whois/record/parser/base_shared1.rb +++ b/lib/whois/record/parser/base_shared1.rb @@ -90,7 +90,7 @@ class BaseShared1 < Base end - # Initializes a new {Scanners::WhoisRegistryOm} instance + # Initializes a new {Scanners::BaseShared1} instance # passing the {#content_for_scanner} # and calls +parse+ on it. # diff --git a/lib/whois/record/parser/base_shared2.rb b/lib/whois/record/parser/base_shared2.rb new file mode 100644 index 000000000..56288dc37 --- /dev/null +++ b/lib/whois/record/parser/base_shared2.rb @@ -0,0 +1,138 @@ +#-- +# Ruby Whois +# +# An intelligent pure Ruby WHOIS client and parser. +# +# Copyright (c) 2009-2012 Simone Carletti +#++ + + +require 'whois/record/parser/base' +require 'whois/record/scanners/base_shared2' + + +module Whois + class Record + class Parser + + # Shared parser 2. + # + # @abstract + # + # @since RELEASE + class BaseShared2 < Base + include Scanners::Ast + + # Actually the :disclaimer is supported, + # but extracting it with the current scanner + # would require too much effort. + # property_supported :disclaimer + + + property_supported :domain do + node("Domain Name", &:downcase) + end + + property_supported :domain_id do + node("Domain ID") + end + + + property_not_supported :referral_whois + + property_not_supported :referral_url + + + property_supported :status do + node("Domain Status") + end + + property_supported :available? do + !!node("status:available") + end + + property_supported :registered? do + !available? + end + + + property_supported :created_on do + node("Domain Registration Date") { |value| Time.parse(value) } + end + + property_supported :updated_on do + node("Domain Last Updated Date") { |value| Time.parse(value) } + end + + property_supported :expires_on do + node("Domain Expiration Date") { |value| Time.parse(value) } + end + + + property_supported :registrar do + node("Sponsoring Registrar") do |str| + Record::Registrar.new( + :id => node("Sponsoring Registrar IANA ID"), + :name => node("Sponsoring Registrar") + ) + end + end + + + property_supported :registrant_contacts do + build_contact("Registrant", Whois::Record::Contact::TYPE_REGISTRANT) + end + + property_supported :admin_contacts do + build_contact("Administrative Contact", Whois::Record::Contact::TYPE_ADMIN) + end + + property_supported :technical_contacts do + build_contact("Technical Contact", Whois::Record::Contact::TYPE_TECHNICAL) + end + + + property_supported :nameservers do + Array.wrap(node("Name Server")).map do |name| + Nameserver.new(:name => name.downcase) + end + end + + + # Initializes a new {Scanners::BaseShared2} instance + # passing the {#content_for_scanner} + # and calls +parse+ on it. + # + # @return [Hash] + def parse + Scanners::BaseShared2.new(content_for_scanner).parse + end + + + private + + def build_contact(element, type) + node("#{element} ID") do |str| + Record::Contact.new( + :type => type, + :id => node("#{element} ID"), + :name => node("#{element} Name"), + :organization => node("#{element} Organization"), + :address => node("#{element} Address1"), + :city => node("#{element} City"), + :zip => node("#{element} Postal Code"), + :state => node("#{element} State/Province"), + :country => node("#{element} Country"), + :country_code => node("#{element} Country Code"), + :phone => node("#{element} Phone Number"), + :fax => node("#{element} Facsimile Number"), + :email => node("#{element} Email") + ) + end + end + + end + + end + end +end diff --git a/lib/whois/record/parser/whois.biz.rb b/lib/whois/record/parser/whois.biz.rb index 8d2275993..3124fa5ad 100644 --- a/lib/whois/record/parser/whois.biz.rb +++ b/lib/whois/record/parser/whois.biz.rb @@ -7,8 +7,7 @@ #++ -require 'whois/record/parser/base' -require 'whois/record/scanners/whois.biz.rb' +require 'whois/record/parser/base_shared2' module Whois @@ -16,117 +15,11 @@ class Record class Parser # Parser for the whois.biz server. - class WhoisBiz < Base - include Scanners::Ast - - # Actually the :disclaimer is supported, - # but extracting it with the current scanner - # would require too much effort. - # property_supported :disclaimer - - - property_supported :domain do - node("Domain Name", &:downcase) - end - - property_supported :domain_id do - node("Domain ID") - end - - - property_not_supported :referral_whois - - property_not_supported :referral_url - - - property_supported :status do - node("Domain Status") - end - - property_supported :available? do - !!node("status:available") - end - - property_supported :registered? do - !available? - end - - - property_supported :created_on do - node("Domain Registration Date") { |value| Time.parse(value) } - end - - property_supported :updated_on do - node("Domain Last Updated Date") { |value| Time.parse(value) } - end - - property_supported :expires_on do - node("Domain Expiration Date") { |value| Time.parse(value) } - end - - - property_supported :registrar do - node("Sponsoring Registrar") do |str| - Record::Registrar.new( - :id => node("Sponsoring Registrar IANA ID"), - :name => node("Sponsoring Registrar") - ) - end - end - - - property_supported :registrant_contacts do - build_contact("Registrant", Whois::Record::Contact::TYPE_REGISTRANT) - end - - property_supported :admin_contacts do - build_contact("Administrative Contact", Whois::Record::Contact::TYPE_ADMIN) - end - - property_supported :technical_contacts do - build_contact("Technical Contact", Whois::Record::Contact::TYPE_TECHNICAL) - end - - - property_supported :nameservers do - Array.wrap(node("Name Server")).map do |name| - Nameserver.new(:name => name.downcase) - end - end - - - # Initializes a new {Scanners::WhoisBiz} instance - # passing the {#content_for_scanner} - # and calls +parse+ on it. - # - # @return [Hash] - def parse - Scanners::WhoisBiz.new(content_for_scanner).parse - end - - - private - - def build_contact(element, type) - node("#{element} ID") do |str| - Record::Contact.new( - :type => type, - :id => node("#{element} ID"), - :name => node("#{element} Name"), - :organization => node("#{element} Organization"), - :address => node("#{element} Address1"), - :city => node("#{element} City"), - :zip => node("#{element} Postal Code"), - :state => node("#{element} State/Province"), - :country => node("#{element} Country"), - :country_code => node("#{element} Country Code"), - :phone => node("#{element} Phone Number"), - :fax => node("#{element} Facsimile Number"), - :email => node("#{element} Email") - ) - end - end - + # + # @see Whois::Record::Parser::Example + # The Example parser for the list of all available methods. + # + class WhoisBiz < BaseShared2 end end diff --git a/lib/whois/record/parser/whois.registry.qa.rb b/lib/whois/record/parser/whois.registry.qa.rb index 90d258ea4..50193b84c 100644 --- a/lib/whois/record/parser/whois.registry.qa.rb +++ b/lib/whois/record/parser/whois.registry.qa.rb @@ -16,6 +16,9 @@ class Parser # Parser for the whois.registry.qa server. # + # @see Whois::Record::Parser::Example + # The Example parser for the list of all available methods. + # # @since 2.1.0 class WhoisRegistryQa < BaseShared1 end diff --git a/lib/whois/record/scanners/base_shared1.rb b/lib/whois/record/scanners/base_shared1.rb index 4d157e3da..0c0537ee9 100644 --- a/lib/whois/record/scanners/base_shared1.rb +++ b/lib/whois/record/scanners/base_shared1.rb @@ -25,7 +25,7 @@ class BaseShared1 < Base tokenizer :scan_available do if @input.skip(/^No Data Found\n/) - @ast['status:available'] = true + @ast["status:available"] = true end end diff --git a/lib/whois/record/scanners/whois.biz.rb b/lib/whois/record/scanners/base_shared2.rb similarity index 91% rename from lib/whois/record/scanners/whois.biz.rb rename to lib/whois/record/scanners/base_shared2.rb index 723c2b559..acca7a4dd 100644 --- a/lib/whois/record/scanners/whois.biz.rb +++ b/lib/whois/record/scanners/base_shared2.rb @@ -8,13 +8,14 @@ require 'whois/record/scanners/base' +require 'whois/record/scanners/base_shared2' module Whois class Record module Scanners - class WhoisBiz < Base + class BaseShared2 < Base self.tokenizers += [ :skip_empty_line, From 13fdc5db462d9e83178f8f22b1ed759a5df195cd Mon Sep 17 00:00:00 2001 From: Simone Carletti Date: Tue, 18 Sep 2012 18:38:19 +0200 Subject: [PATCH 3/5] Replace WhoisNicCo custom parser with BaseShared2 --- lib/whois/record/parser/whois.nic.co.rb | 53 ++----------------- lib/whois/record/scanners/base.rb | 4 ++ lib/whois/record/scanners/base_shared2.rb | 4 +- .../whois.nic.co/status_available.expected | 2 +- .../whois.nic.co/status_available_spec.rb | 2 +- 5 files changed, 12 insertions(+), 53 deletions(-) diff --git a/lib/whois/record/parser/whois.nic.co.rb b/lib/whois/record/parser/whois.nic.co.rb index 906371974..c49576bc7 100644 --- a/lib/whois/record/parser/whois.nic.co.rb +++ b/lib/whois/record/parser/whois.nic.co.rb @@ -7,64 +7,19 @@ #++ -require 'whois/record/parser/base' +require 'whois/record/parser/base_shared2' module Whois class Record class Parser - # - # = whois.nic.co parser - # # Parser for the whois.nic.co server. # - # NOTE: This parser is just a stub and provides only a few basic methods - # to check for domain availability and get domain status. - # Please consider to contribute implementing missing methods. - # See WhoisNicIt parser for an explanation of all available methods - # and examples. + # @see Whois::Record::Parser::Example + # The Example parser for the list of all available methods. # - class WhoisNicCo < Base - - property_supported :status do - content_for_scanner.scan(/Domain Status:\s+(.*?)\n/).flatten - end - - property_supported :available? do - !!(content_for_scanner =~ /Not found:/) - end - - property_supported :registered? do - !available? - end - - - property_supported :created_on do - if content_for_scanner =~ /Domain Registration Date:\s+(.*)\n/ - Time.parse($1) - end - end - - property_supported :updated_on do - if content_for_scanner =~ /Domain Last Updated Date:\s+(.*)\n/ - Time.parse($1) - end - end - - property_supported :expires_on do - if content_for_scanner =~ /Domain Expiration Date:\s+(.*)\n/ - Time.parse($1) - end - end - - - property_supported :nameservers do - content_for_scanner.scan(/Name Server:\s+(.+)\n/).flatten.map do |name| - Record::Nameserver.new(:name => name.downcase) - end - end - + class WhoisNicCo < BaseShared2 end end diff --git a/lib/whois/record/scanners/base.rb b/lib/whois/record/scanners/base.rb index 1deabfd7f..8c81b93db 100644 --- a/lib/whois/record/scanners/base.rb +++ b/lib/whois/record/scanners/base.rb @@ -43,6 +43,10 @@ def parse @input.skip(/^\n/) end + tokenizer :skip_blank_line do + @input.skip(/^[\s]*\n/) + end + tokenizer :skip_newline do @input.skip(/\n/) end diff --git a/lib/whois/record/scanners/base_shared2.rb b/lib/whois/record/scanners/base_shared2.rb index acca7a4dd..825271f60 100644 --- a/lib/whois/record/scanners/base_shared2.rb +++ b/lib/whois/record/scanners/base_shared2.rb @@ -18,7 +18,7 @@ module Scanners class BaseShared2 < Base self.tokenizers += [ - :skip_empty_line, + :skip_blank_line, :scan_available, :scan_keyvalue, :skip_lastupdate, @@ -38,7 +38,7 @@ class BaseShared2 < Base end tokenizer :skip_fuffa do - @input.scan(/^\S(.+)\n/) + @input.skip(/^\S(.+)\n/) end end diff --git a/spec/fixtures/responses/whois.nic.co/status_available.expected b/spec/fixtures/responses/whois.nic.co/status_available.expected index bdb73eecc..f560e9c69 100644 --- a/spec/fixtures/responses/whois.nic.co/status_available.expected +++ b/spec/fixtures/responses/whois.nic.co/status_available.expected @@ -1,5 +1,5 @@ #status - should: %s == [] + should: %s == nil #available? should: %s == true diff --git a/spec/whois/record/parser/responses/whois.nic.co/status_available_spec.rb b/spec/whois/record/parser/responses/whois.nic.co/status_available_spec.rb index c37b6516c..1bd126d17 100644 --- a/spec/whois/record/parser/responses/whois.nic.co/status_available_spec.rb +++ b/spec/whois/record/parser/responses/whois.nic.co/status_available_spec.rb @@ -23,7 +23,7 @@ describe "#status" do it do - subject.status.should == [] + subject.status.should == nil end end describe "#available?" do From cb7f380fd65bf6a8686e9ec2ae5da5521855e457 Mon Sep 17 00:00:00 2001 From: Simone Carletti Date: Wed, 19 Sep 2012 12:52:08 +0200 Subject: [PATCH 4/5] whois.biz missing tests for contacts --- .../whois.biz/status_registered.expected | 41 ++++++++++++++++++- .../whois.biz/status_registered_spec.rb | 41 ++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/spec/fixtures/responses/whois.biz/status_registered.expected b/spec/fixtures/responses/whois.biz/status_registered.expected index 1b493423b..58bd4d055 100644 --- a/spec/fixtures/responses/whois.biz/status_registered.expected +++ b/spec/fixtures/responses/whois.biz/status_registered.expected @@ -13,7 +13,7 @@ #status - should: %s == %w( clientDeleteProhibited clientTransferProhibited clientUpdateProhibited ) + should: %s == ["clientDeleteProhibited", "clientTransferProhibited", "clientUpdateProhibited"] #available? should: %s == false @@ -44,16 +44,55 @@ should: %s CLASS(array) should: %s SIZE(1) should: %s[0] CLASS(contact) + should: %s[0].type == Whois::Record::Contact::TYPE_REGISTRANT + should: %s[0].id == "MMR-32103" + should: %s[0].name == "DNS Admin" + should: %s[0].organization == "Google Inc." + should: %s[0].address == "2400 E. Bayshore Pkwy" + should: %s[0].city == "Mountain View" + should: %s[0].zip == "94043" + should: %s[0].state == "CA" + should: %s[0].country == "United States" + should: %s[0].country_code == "US" + should: %s[0].phone == "+1.6503300100" + should: %s[0].fax == "+1.6506181499" + should: %s[0].email == "dns-admin@GOOGLE.COM" #admin_contacts should: %s CLASS(array) should: %s SIZE(1) should: %s[0] CLASS(contact) + should: %s[0].type == Whois::Record::Contact::TYPE_ADMIN + should: %s[0].id == "MMR-32104" + should: %s[0].name == "DNS Admin" + should: %s[0].organization == "Google Inc." + should: %s[0].address == "2400 E. Bayshore Pkwy" + should: %s[0].city == "Mountain View" + should: %s[0].zip == "94043" + should: %s[0].state == "CA" + should: %s[0].country == "United States" + should: %s[0].country_code == "US" + should: %s[0].phone == "+1.6503300100" + should: %s[0].fax == "+1.6506181499" + should: %s[0].email == "dns-admin@GOOGLE.COM" #technical_contacts should: %s CLASS(array) should: %s SIZE(1) should: %s[0] CLASS(contact) + should: %s[0].type == Whois::Record::Contact::TYPE_TECHNICAL + should: %s[0].id == "MMR-32105" + should: %s[0].name == "DNS Admin" + should: %s[0].organization == "Google Inc." + should: %s[0].address == "2400 E. Bayshore Pkwy" + should: %s[0].city == "Mountain View" + should: %s[0].zip == "94043" + should: %s[0].state == "CA" + should: %s[0].country == "United States" + should: %s[0].country_code == "US" + should: %s[0].phone == "+1.6503300100" + should: %s[0].fax == "+1.6506181499" + should: %s[0].email == "dns-admin@google.com" #nameservers diff --git a/spec/whois/record/parser/responses/whois.biz/status_registered_spec.rb b/spec/whois/record/parser/responses/whois.biz/status_registered_spec.rb index 50b217f16..f405df97a 100644 --- a/spec/whois/record/parser/responses/whois.biz/status_registered_spec.rb +++ b/spec/whois/record/parser/responses/whois.biz/status_registered_spec.rb @@ -43,7 +43,7 @@ end describe "#status" do it do - subject.status.should == %w( clientDeleteProhibited clientTransferProhibited clientUpdateProhibited ) + subject.status.should == ["clientDeleteProhibited", "clientTransferProhibited", "clientUpdateProhibited"] end end describe "#available?" do @@ -86,6 +86,19 @@ subject.registrant_contacts.should be_a(Array) subject.registrant_contacts.should have(1).items subject.registrant_contacts[0].should be_a(Whois::Record::Contact) + subject.registrant_contacts[0].type.should == Whois::Record::Contact::TYPE_REGISTRANT + subject.registrant_contacts[0].id.should == "MMR-32103" + subject.registrant_contacts[0].name.should == "DNS Admin" + subject.registrant_contacts[0].organization.should == "Google Inc." + subject.registrant_contacts[0].address.should == "2400 E. Bayshore Pkwy" + subject.registrant_contacts[0].city.should == "Mountain View" + subject.registrant_contacts[0].zip.should == "94043" + subject.registrant_contacts[0].state.should == "CA" + subject.registrant_contacts[0].country.should == "United States" + subject.registrant_contacts[0].country_code.should == "US" + subject.registrant_contacts[0].phone.should == "+1.6503300100" + subject.registrant_contacts[0].fax.should == "+1.6506181499" + subject.registrant_contacts[0].email.should == "dns-admin@GOOGLE.COM" end end describe "#admin_contacts" do @@ -93,6 +106,19 @@ subject.admin_contacts.should be_a(Array) subject.admin_contacts.should have(1).items subject.admin_contacts[0].should be_a(Whois::Record::Contact) + subject.admin_contacts[0].type.should == Whois::Record::Contact::TYPE_ADMIN + subject.admin_contacts[0].id.should == "MMR-32104" + subject.admin_contacts[0].name.should == "DNS Admin" + subject.admin_contacts[0].organization.should == "Google Inc." + subject.admin_contacts[0].address.should == "2400 E. Bayshore Pkwy" + subject.admin_contacts[0].city.should == "Mountain View" + subject.admin_contacts[0].zip.should == "94043" + subject.admin_contacts[0].state.should == "CA" + subject.admin_contacts[0].country.should == "United States" + subject.admin_contacts[0].country_code.should == "US" + subject.admin_contacts[0].phone.should == "+1.6503300100" + subject.admin_contacts[0].fax.should == "+1.6506181499" + subject.admin_contacts[0].email.should == "dns-admin@GOOGLE.COM" end end describe "#technical_contacts" do @@ -100,6 +126,19 @@ subject.technical_contacts.should be_a(Array) subject.technical_contacts.should have(1).items subject.technical_contacts[0].should be_a(Whois::Record::Contact) + subject.technical_contacts[0].type.should == Whois::Record::Contact::TYPE_TECHNICAL + subject.technical_contacts[0].id.should == "MMR-32105" + subject.technical_contacts[0].name.should == "DNS Admin" + subject.technical_contacts[0].organization.should == "Google Inc." + subject.technical_contacts[0].address.should == "2400 E. Bayshore Pkwy" + subject.technical_contacts[0].city.should == "Mountain View" + subject.technical_contacts[0].zip.should == "94043" + subject.technical_contacts[0].state.should == "CA" + subject.technical_contacts[0].country.should == "United States" + subject.technical_contacts[0].country_code.should == "US" + subject.technical_contacts[0].phone.should == "+1.6503300100" + subject.technical_contacts[0].fax.should == "+1.6506181499" + subject.technical_contacts[0].email.should == "dns-admin@google.com" end end describe "#nameservers" do From c442604c29fdc4b590c562687b6db273e4af6951 Mon Sep 17 00:00:00 2001 From: Simone Carletti Date: Wed, 19 Sep 2012 13:00:35 +0200 Subject: [PATCH 5/5] Added full whois.nic.co parser. --- CHANGELOG.md | 2 + lib/whois/record/parser/base_shared2.rb | 7 +- .../whois.nic.co/status_available.expected | 30 +++++++ .../whois.nic.co/status_registered.expected | 75 ++++++++++++++++ .../whois.nic.co/status_available_spec.rb | 43 +++++++++ .../whois.nic.co/status_registered_spec.rb | 87 +++++++++++++++++++ 6 files changed, 243 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 612721ffc..9f8ca26d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ * NEW: Added full whois.nic.dm parser (GH-43). +* NEW: Added full whois.nic.co parser. + * FIXED: Fixed whois.register.com parser for enom formats (GH-181). [Thanks @JustinCampbell] * FIXED: whois.jprs.js parser should support status `Suspended`. diff --git a/lib/whois/record/parser/base_shared2.rb b/lib/whois/record/parser/base_shared2.rb index 56288dc37..969ef547a 100644 --- a/lib/whois/record/parser/base_shared2.rb +++ b/lib/whois/record/parser/base_shared2.rb @@ -113,12 +113,17 @@ def parse def build_contact(element, type) node("#{element} ID") do |str| + address = (1..3). + map { |i| node("#{element} Address#{i}") }. + delete_if(&:nil?). + join("\n") + Record::Contact.new( :type => type, :id => node("#{element} ID"), :name => node("#{element} Name"), :organization => node("#{element} Organization"), - :address => node("#{element} Address1"), + :address => address, :city => node("#{element} City"), :zip => node("#{element} Postal Code"), :state => node("#{element} State/Province"), diff --git a/spec/fixtures/responses/whois.nic.co/status_available.expected b/spec/fixtures/responses/whois.nic.co/status_available.expected index f560e9c69..2e080df28 100644 --- a/spec/fixtures/responses/whois.nic.co/status_available.expected +++ b/spec/fixtures/responses/whois.nic.co/status_available.expected @@ -1,3 +1,17 @@ +#domain + should: %s == "u34jedzcq.co" + +#domain_id + should: %s == nil + + +#referral_whois + should: %s raise_error(Whois::PropertyNotSupported) + +#referral_url + should: %s raise_error(Whois::PropertyNotSupported) + + #status should: %s == nil @@ -18,6 +32,22 @@ should: %s == nil +#registrar + should: %s == nil + +#registrant_contacts + should: %s CLASS(array) + should: %s == [] + +#admin_contacts + should: %s CLASS(array) + should: %s == [] + +#technical_contacts + should: %s CLASS(array) + should: %s == [] + + #nameservers should: %s CLASS(array) should: %s == [] diff --git a/spec/fixtures/responses/whois.nic.co/status_registered.expected b/spec/fixtures/responses/whois.nic.co/status_registered.expected index 0d316d16a..37884b01d 100644 --- a/spec/fixtures/responses/whois.nic.co/status_registered.expected +++ b/spec/fixtures/responses/whois.nic.co/status_registered.expected @@ -1,3 +1,17 @@ +#domain + should: %s == "t.co" + +#domain_id + should: %s == "D740225-CO" + + +#referral_whois + should: %s raise_error(Whois::PropertyNotSupported) + +#referral_url + should: %s raise_error(Whois::PropertyNotSupported) + + #status should: %s == ["serverDeleteProhibited", "serverTransferProhibited"] @@ -20,6 +34,67 @@ should: %s CLASS(time) should: %s == Time.parse("2013-04-25 23:59:59 UTC") + +#registrar + should: %s CLASS(registrar) + should: %s.id == "13" + should: %s.name == "MELBOURNE IT LTD" + +#registrant_contacts + should: %s CLASS(array) + should: %s SIZE(1) + should: %s[0] CLASS(contact) + should: %s[0].type == Whois::Record::Contact::TYPE_REGISTRANT + should: %s[0].id == "TWITTERREG2012" + should: %s[0].name == "Twitter, Inc." + should: %s[0].organization == "Twitter, Inc." + should: %s[0].address == "1355 Market Street\nSuite 900" + should: %s[0].city == "San Francisco" + should: %s[0].zip == "94103" + should: %s[0].state == "CA" + should: %s[0].country == "United States" + should: %s[0].country_code == "US" + should: %s[0].phone == "+1.4152229670" + should: %s[0].fax == "+1.4152220922" + should: %s[0].email == "domains@twitter.com" + +#admin_contacts + should: %s CLASS(array) + should: %s SIZE(1) + should: %s[0] CLASS(contact) + should: %s[0].type == Whois::Record::Contact::TYPE_ADMIN + should: %s[0].id == "TWITTERADMIN2012" + should: %s[0].name == "Domain Admin" + should: %s[0].organization == "Twitter, Inc." + should: %s[0].address == "1355 Market Street\nSuite 900" + should: %s[0].city == "San Francisco" + should: %s[0].zip == "94103" + should: %s[0].state == "CA" + should: %s[0].country == "United States" + should: %s[0].country_code == "US" + should: %s[0].phone == "+1.4152229670" + should: %s[0].fax == "+1.4152220922" + should: %s[0].email == "domains@twitter.com" + +#technical_contacts + should: %s CLASS(array) + should: %s SIZE(1) + should: %s[0] CLASS(contact) + should: %s[0].type == Whois::Record::Contact::TYPE_TECHNICAL + should: %s[0].id == "TWITTERTECH2012" + should: %s[0].name == "Tech Admin" + should: %s[0].organization == "Twitter, Inc." + should: %s[0].address == "1355 Market Street\nSuite 900" + should: %s[0].city == "San Francisco" + should: %s[0].zip == "94103" + should: %s[0].state == "CA" + should: %s[0].country == "United States" + should: %s[0].country_code == "US" + should: %s[0].phone == "+1.4152229670" + should: %s[0].fax == "+1.4152220922" + should: %s[0].email == "domains-tech@twitter.com" + + #nameservers should: %s CLASS(array) should: %s SIZE(4) diff --git a/spec/whois/record/parser/responses/whois.nic.co/status_available_spec.rb b/spec/whois/record/parser/responses/whois.nic.co/status_available_spec.rb index 1bd126d17..45e5f192b 100644 --- a/spec/whois/record/parser/responses/whois.nic.co/status_available_spec.rb +++ b/spec/whois/record/parser/responses/whois.nic.co/status_available_spec.rb @@ -21,6 +21,26 @@ described_class.new(part) end + describe "#domain" do + it do + subject.domain.should == "u34jedzcq.co" + end + end + describe "#domain_id" do + it do + subject.domain_id.should == nil + end + end + describe "#referral_whois" do + it do + lambda { subject.referral_whois }.should raise_error(Whois::PropertyNotSupported) + end + end + describe "#referral_url" do + it do + lambda { subject.referral_url }.should raise_error(Whois::PropertyNotSupported) + end + end describe "#status" do it do subject.status.should == nil @@ -51,6 +71,29 @@ subject.expires_on.should == nil end end + describe "#registrar" do + it do + subject.registrar.should == nil + end + end + describe "#registrant_contacts" do + it do + subject.registrant_contacts.should be_a(Array) + subject.registrant_contacts.should == [] + end + end + describe "#admin_contacts" do + it do + subject.admin_contacts.should be_a(Array) + subject.admin_contacts.should == [] + end + end + describe "#technical_contacts" do + it do + subject.technical_contacts.should be_a(Array) + subject.technical_contacts.should == [] + end + end describe "#nameservers" do it do subject.nameservers.should be_a(Array) diff --git a/spec/whois/record/parser/responses/whois.nic.co/status_registered_spec.rb b/spec/whois/record/parser/responses/whois.nic.co/status_registered_spec.rb index 7344d8f63..e0f9afc20 100644 --- a/spec/whois/record/parser/responses/whois.nic.co/status_registered_spec.rb +++ b/spec/whois/record/parser/responses/whois.nic.co/status_registered_spec.rb @@ -21,6 +21,26 @@ described_class.new(part) end + describe "#domain" do + it do + subject.domain.should == "t.co" + end + end + describe "#domain_id" do + it do + subject.domain_id.should == "D740225-CO" + end + end + describe "#referral_whois" do + it do + lambda { subject.referral_whois }.should raise_error(Whois::PropertyNotSupported) + end + end + describe "#referral_url" do + it do + lambda { subject.referral_url }.should raise_error(Whois::PropertyNotSupported) + end + end describe "#status" do it do subject.status.should == ["serverDeleteProhibited", "serverTransferProhibited"] @@ -54,6 +74,73 @@ subject.expires_on.should == Time.parse("2013-04-25 23:59:59 UTC") end end + describe "#registrar" do + it do + subject.registrar.should be_a(Whois::Record::Registrar) + subject.registrar.id.should == "13" + subject.registrar.name.should == "MELBOURNE IT LTD" + end + end + describe "#registrant_contacts" do + it do + subject.registrant_contacts.should be_a(Array) + subject.registrant_contacts.should have(1).items + subject.registrant_contacts[0].should be_a(Whois::Record::Contact) + subject.registrant_contacts[0].type.should == Whois::Record::Contact::TYPE_REGISTRANT + subject.registrant_contacts[0].id.should == "TWITTERREG2012" + subject.registrant_contacts[0].name.should == "Twitter, Inc." + subject.registrant_contacts[0].organization.should == "Twitter, Inc." + subject.registrant_contacts[0].address.should == "1355 Market Street\nSuite 900" + subject.registrant_contacts[0].city.should == "San Francisco" + subject.registrant_contacts[0].zip.should == "94103" + subject.registrant_contacts[0].state.should == "CA" + subject.registrant_contacts[0].country.should == "United States" + subject.registrant_contacts[0].country_code.should == "US" + subject.registrant_contacts[0].phone.should == "+1.4152229670" + subject.registrant_contacts[0].fax.should == "+1.4152220922" + subject.registrant_contacts[0].email.should == "domains@twitter.com" + end + end + describe "#admin_contacts" do + it do + subject.admin_contacts.should be_a(Array) + subject.admin_contacts.should have(1).items + subject.admin_contacts[0].should be_a(Whois::Record::Contact) + subject.admin_contacts[0].type.should == Whois::Record::Contact::TYPE_ADMIN + subject.admin_contacts[0].id.should == "TWITTERADMIN2012" + subject.admin_contacts[0].name.should == "Domain Admin" + subject.admin_contacts[0].organization.should == "Twitter, Inc." + subject.admin_contacts[0].address.should == "1355 Market Street\nSuite 900" + subject.admin_contacts[0].city.should == "San Francisco" + subject.admin_contacts[0].zip.should == "94103" + subject.admin_contacts[0].state.should == "CA" + subject.admin_contacts[0].country.should == "United States" + subject.admin_contacts[0].country_code.should == "US" + subject.admin_contacts[0].phone.should == "+1.4152229670" + subject.admin_contacts[0].fax.should == "+1.4152220922" + subject.admin_contacts[0].email.should == "domains@twitter.com" + end + end + describe "#technical_contacts" do + it do + subject.technical_contacts.should be_a(Array) + subject.technical_contacts.should have(1).items + subject.technical_contacts[0].should be_a(Whois::Record::Contact) + subject.technical_contacts[0].type.should == Whois::Record::Contact::TYPE_TECHNICAL + subject.technical_contacts[0].id.should == "TWITTERTECH2012" + subject.technical_contacts[0].name.should == "Tech Admin" + subject.technical_contacts[0].organization.should == "Twitter, Inc." + subject.technical_contacts[0].address.should == "1355 Market Street\nSuite 900" + subject.technical_contacts[0].city.should == "San Francisco" + subject.technical_contacts[0].zip.should == "94103" + subject.technical_contacts[0].state.should == "CA" + subject.technical_contacts[0].country.should == "United States" + subject.technical_contacts[0].country_code.should == "US" + subject.technical_contacts[0].phone.should == "+1.4152229670" + subject.technical_contacts[0].fax.should == "+1.4152220922" + subject.technical_contacts[0].email.should == "domains-tech@twitter.com" + end + end describe "#nameservers" do it do subject.nameservers.should be_a(Array)