duns-lookup 0.0.3

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.
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{duns-lookup}
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kali Donovan"]
12
+ s.date = %q{2009-10-14}
13
+ s.description = %q{Provides a small wrapper around the Dun & Bradstreet website to retrieve business information from DUNS numbers.}
14
+ s.email = %q{kali.donovan@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "doc/classes/Duns.html",
27
+ "doc/classes/Duns.src/M000001.html",
28
+ "doc/classes/Duns.src/M000002.html",
29
+ "doc/classes/Duns.src/M000003.html",
30
+ "doc/classes/Duns.src/M000004.html",
31
+ "doc/classes/Duns.src/M000005.html",
32
+ "doc/classes/Duns.src/M000006.html",
33
+ "doc/classes/DunsError.html",
34
+ "doc/created.rid",
35
+ "doc/files/lib/duns-lookup_rb.html",
36
+ "doc/fr_class_index.html",
37
+ "doc/fr_file_index.html",
38
+ "doc/fr_method_index.html",
39
+ "doc/index.html",
40
+ "doc/rdoc-style.css",
41
+ "duns-lookup.gemspec",
42
+ "lib/duns-lookup.rb",
43
+ "spec/duns-lookup_spec.rb",
44
+ "spec/spec_helper.rb"
45
+ ]
46
+ s.homepage = %q{http://github.com/kdonovan/duns-lookup}
47
+ s.rdoc_options = ["--charset=UTF-8"]
48
+ s.require_paths = ["lib"]
49
+ s.rubygems_version = %q{1.3.5}
50
+ s.summary = %q{Wrapper for looking up Dun & Bradstreet numbers (DUNS)}
51
+ s.test_files = [
52
+ "spec/duns-lookup_spec.rb",
53
+ "spec/spec_helper.rb"
54
+ ]
55
+
56
+ if s.respond_to? :specification_version then
57
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
58
+ s.specification_version = 3
59
+
60
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
61
+ s.add_development_dependency(%q<rspec>, [">= 0"])
62
+ s.add_runtime_dependency(%q<mechanize>, [">= 0"])
63
+ else
64
+ s.add_dependency(%q<rspec>, [">= 0"])
65
+ s.add_dependency(%q<mechanize>, [">= 0"])
66
+ end
67
+ else
68
+ s.add_dependency(%q<rspec>, [">= 0"])
69
+ s.add_dependency(%q<mechanize>, [">= 0"])
70
+ end
71
+ end
@@ -0,0 +1,99 @@
1
+ class DunsError < Exception
2
+ end
3
+
4
+ # = Synopsis
5
+ # The Duns library provides a small wrapper around the Dun & Bradstreet
6
+ # website's advanced search functionality. Currently it only implements
7
+ # searching for company by DUNS number.
8
+ #
9
+ # == Example
10
+ # require 'rubygems'
11
+ # require 'kdonovan-duns-lookup'
12
+ #
13
+ # Duns.lookup_duns( *invalid_number* )
14
+ # # => nil
15
+ # Duns.lookup_duns( *some_valid_number* )
16
+ # # => {:name => *a_name*, :address => *an_address*}
17
+
18
+ class Duns
19
+ require 'rubygems'
20
+ require 'open-uri'
21
+ require 'mechanize'
22
+
23
+ # D&B homepage URL
24
+ @@dnb_homepage = 'http://smallbusiness.dnb.com/'
25
+
26
+ # The URI for D&B advanced search
27
+ @@dnb_advanced_search = 'https://smallbusiness.dnb.com/ePlatform/servlet/AdvancedCompanySearch?storeId=10001&catalogId=70001?storeId=10001&catalogId=70001'
28
+
29
+ # Our Mechanize agent
30
+ @@agent = WWW::Mechanize.new
31
+ @@agent.user_agent_alias = 'Windows IE 7'
32
+
33
+ ####
34
+ # Look up a given DUNS number in the D&B database.
35
+ # If the number is found, returns a hash with :name and :address keys.
36
+ # Otherwise, returns nil.
37
+ #
38
+ def self.lookup_duns(number)
39
+ form = @@agent.get( @@dnb_advanced_search ).form('DunsSearchForm')
40
+ form.dunsNumber = enforce_duns_formatting(number)
41
+ page = @@agent.submit(form)
42
+
43
+ extract_search_results(page)
44
+ end
45
+
46
+
47
+ ####
48
+ # Updates the internal URL used as the base for advanced searches.
49
+ #
50
+ # The D&B website uses lots of extraneous (to us) URL params, and I have no idea what they all mean.
51
+ # If the base URL stops working, this method will try to set a new one by visiting the main page and
52
+ # finding & clicking an Advanced Search link.
53
+ #
54
+ # This is mostly a precaution, but if searches stop working a good first bet would be to try running this
55
+ # method. If search is still broken, something actually changed in the D&B HTML and we'll need to retool
56
+ # the gem.
57
+ #
58
+ def self.update_advanced_search_url
59
+ page = @@agent.get( @@dnb_homepage )
60
+ advanced_link = page.links.find{|l| l.text.match(/search/i) && l.uri.to_s.match(/AdvancedCompanySearch/)}
61
+ raise(DunsError, "Unable to find an advanced search link at: #{@@dnb_homepage}") if advanced_link.nil?
62
+ @@dnb_advanced_search = advanced_link.uri.to_s
63
+ end
64
+
65
+
66
+ protected
67
+
68
+ # Given a search page, extract the results
69
+ def self.extract_search_results(page)
70
+ return nil if page.search('div.text-red').size > 0
71
+
72
+ # Given a DUNS number search, we only expect one result. For other search types (when/if implemented), loop over all TRs returned in this table
73
+ company_name = extract_name( page.search("//table[@id='SearchResultsTable']//tr[1]/td[2]") )
74
+ company_address = extract_address( page.search("//table[@id='SearchResultsTable']//tr[1]/td[3]") )
75
+
76
+ {:name => company_name, :address => company_address}
77
+ end
78
+
79
+ # Retrieve an address from the proper D&B HTML for the td element
80
+ def self.extract_address(td)
81
+ td.text.gsub(/\s{2,}/, ' ')
82
+ end
83
+
84
+ # Retrieve a name from the proper D&B HTML for the td element
85
+ def self.extract_name(td)
86
+ raw_text = td.text.gsub(/\s{2,}/, ' ')
87
+ with_js = raw_text.match(/var companyName=escape(.+?)var companyAddr/)[1] # First get the surrounding js, to ensure we don't get confused by e.g. a ) in the company name
88
+
89
+ # Now we have e.g. >> ('NORTH TEXAS CIRCUIT BOARD CO., INC.'); << and we need to strip off the extraneous stuff
90
+ with_js[2..-5]
91
+ end
92
+
93
+ # Strip out non-numeric characters, and raise error if remaining number is still invalid
94
+ def self.enforce_duns_formatting(orig_number)
95
+ number = orig_number.to_s.gsub(/\D/, '')
96
+ raise(DunsError, "Received invalid DUNS number (must be 9 digits): #{orig_number}") unless number.length == 9
97
+ return number
98
+ end
99
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "DunsLookup" do
4
+ context "Searching by DUNS number" do
5
+ it "should raise an exception when invalid DUNS numbers are supplied" do
6
+ lambda { Duns.lookup_duns('123') }.should raise_error(DunsError)
7
+ lambda { Duns.lookup_duns('abcdefghi') }.should raise_error(DunsError)
8
+ lambda { Duns.lookup_duns('123abc---') }.should raise_error(DunsError)
9
+ lambda { Duns.lookup_duns('') }.should raise_error(DunsError)
10
+ end
11
+
12
+ it "should not raise an exception when a valid DUNS number is supplied" do
13
+ lambda { Duns.lookup_duns('123456789') }.should_not raise_error(DunsError)
14
+ lambda { Duns.lookup_duns('123 456 789') }.should_not raise_error(DunsError)
15
+ lambda { Duns.lookup_duns('123-456-789') }.should_not raise_error(DunsError)
16
+ end
17
+
18
+ it "Returns no results where there shouldn't be any" do
19
+ Duns.lookup_duns( fake_duns[:number] ).should be_nil
20
+ end
21
+
22
+ it "Should properly extract company name and address when a valid result is found" do
23
+ unless real_duns[:number] && real_duns[:name] && real_duns[:address]
24
+ raise "Must provide information from an existing company to validates results are correct. Edit #real_duns in spec_helper.rb"
25
+ end
26
+
27
+ results = Duns.lookup_duns( real_duns[:number] )
28
+ results.should_not be_nil
29
+ results.should be_a_kind_of Hash
30
+
31
+ results[:name].should == real_duns[:name]
32
+ results[:address].should == real_duns[:address]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'duns-lookup'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
10
+
11
+ # TODO -- to run the specs, input valid information from a real company here
12
+ # Try looking up your business by hand first to see exactly the format used by the D&B website
13
+ # (for instance punctuation matters, as in the abbreviations of CO and INC below)
14
+ def real_duns
15
+ {
16
+ :number => nil, # Enter the company's DUNS number
17
+ :name => nil, # Enter the official company name, ALL UPPER CASE: FOO WIDGET CO., INC.
18
+ :address => nil # Enter the officially registered address, ALL UPPER CASE: 123 W MAIN ST, SAN FRANCISCO, CA
19
+ }
20
+ end
21
+
22
+ # A valid, but non-existant DUNS number
23
+ def fake_duns
24
+ {:number => '123456789'}
25
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: duns-lookup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Kali Donovan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-14 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: mechanize
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: Provides a small wrapper around the Dun & Bradstreet website to retrieve business information from DUNS numbers.
36
+ email: kali.donovan@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION
51
+ - doc/classes/Duns.html
52
+ - doc/classes/Duns.src/M000001.html
53
+ - doc/classes/Duns.src/M000002.html
54
+ - doc/classes/Duns.src/M000003.html
55
+ - doc/classes/Duns.src/M000004.html
56
+ - doc/classes/Duns.src/M000005.html
57
+ - doc/classes/Duns.src/M000006.html
58
+ - doc/classes/DunsError.html
59
+ - doc/created.rid
60
+ - doc/files/lib/duns-lookup_rb.html
61
+ - doc/fr_class_index.html
62
+ - doc/fr_file_index.html
63
+ - doc/fr_method_index.html
64
+ - doc/index.html
65
+ - doc/rdoc-style.css
66
+ - duns-lookup.gemspec
67
+ - lib/duns-lookup.rb
68
+ - spec/duns-lookup_spec.rb
69
+ - spec/spec_helper.rb
70
+ has_rdoc: true
71
+ homepage: http://github.com/kdonovan/duns-lookup
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options:
76
+ - --charset=UTF-8
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ version:
91
+ requirements: []
92
+
93
+ rubyforge_project:
94
+ rubygems_version: 1.3.5
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: Wrapper for looking up Dun & Bradstreet numbers (DUNS)
98
+ test_files:
99
+ - spec/duns-lookup_spec.rb
100
+ - spec/spec_helper.rb