melissadata 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +31 -0
- data/Gemfile.lock +69 -0
- data/Guardfile +6 -0
- data/README.md +5 -0
- data/Rakefile +47 -0
- data/Vagrantfile +17 -0
- data/bin/md +4 -0
- data/bin/md-rpc +9 -0
- data/bin/md-server +18 -0
- data/bin/melissadata +4 -0
- data/lib/md.rb +4 -0
- data/lib/melissadata.rb +2 -0
- data/lib/melissadata/cli.rb +45 -0
- data/lib/melissadata/command.rb +9 -0
- data/lib/melissadata/command/base.rb +104 -0
- data/lib/melissadata/command/helpers.rb +12 -0
- data/lib/melissadata/command/package.rb +83 -0
- data/lib/melissadata/constants.rb +14 -0
- data/lib/melissadata/env.rb +92 -0
- data/lib/melissadata/errors.rb +83 -0
- data/lib/melissadata/melissadata.rb +164 -0
- data/lib/melissadata/native_object.rb +19 -0
- data/lib/melissadata/native_object/address.rb +124 -0
- data/lib/melissadata/native_object/base.rb +79 -0
- data/lib/melissadata/native_object/client.rb +33 -0
- data/lib/melissadata/native_object/email.rb +61 -0
- data/lib/melissadata/native_object/geo.rb +61 -0
- data/lib/melissadata/native_object/ip_locator.rb +41 -0
- data/lib/melissadata/native_object/name.rb +74 -0
- data/lib/melissadata/native_object/phone.rb +96 -0
- data/lib/melissadata/prev/command_old.rb +80 -0
- data/lib/melissadata/prev/shared_objects.rb +54 -0
- data/lib/melissadata/rpc.rb +39 -0
- data/lib/melissadata/version.rb +3 -0
- data/melissadata.gemspec +40 -0
- data/pkg/melissa-data-0.0.1.gem +0 -0
- data/pkg/melissa-data-0.0.2.gem +0 -0
- data/pkg/melissa-data-0.0.3.gem +0 -0
- data/pkg/melissadata-0.0.1.gem +0 -0
- data/spec/locales/en.yml +8 -0
- data/spec/melissadata/cli_spec.rb +34 -0
- data/spec/melissadata/command/package_spec.rb +22 -0
- data/spec/melissadata/env_spec.rb +55 -0
- data/spec/spec_helper.rb +33 -0
- data/templates/config.rb +20 -0
- data/templates/locales/en.yml +4 -0
- metadata +316 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
module MelissaData::NativeObject
|
2
|
+
class Base
|
3
|
+
attr_reader :obj, :license, :input, :output, :result_codes, :results_string
|
4
|
+
attr_writer :data_dir
|
5
|
+
|
6
|
+
def initialize(opts={})
|
7
|
+
@license = opts[:license] || "DEMO"
|
8
|
+
|
9
|
+
unless obj.SetLicenseString(@license)
|
10
|
+
@license = "DEMO"
|
11
|
+
puts "Invalid License: Running in DEMO MODE."
|
12
|
+
end
|
13
|
+
|
14
|
+
if obj.respond_to?(:InitializeDataFiles) && obj.InitializeDataFiles != 0
|
15
|
+
raise obj.GetInitializeErrorString
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Example: geo.process(:zip => '84108')
|
20
|
+
# - If invalid input (required fields are missing), return {}
|
21
|
+
# - Call subclass's parse_input - this should call methods on the MelissaData object
|
22
|
+
# - Call subclass's assign_values - pull out information from the object into the output hash
|
23
|
+
# - Processes result codes
|
24
|
+
def process(opts={})
|
25
|
+
@input = @defaults.merge(opts)
|
26
|
+
return {} unless valid_input?
|
27
|
+
|
28
|
+
parse_input
|
29
|
+
@results_string = obj.GetResults.to_s
|
30
|
+
assign_values
|
31
|
+
process_result_codes
|
32
|
+
|
33
|
+
output.reduce({}) do |hsh,pair|
|
34
|
+
val = pair.last
|
35
|
+
val.strip! if val.respond_to?(:strip)
|
36
|
+
hsh[pair.first] = val if val.present?
|
37
|
+
hsh
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_input
|
42
|
+
raise "Needs to be overridden"
|
43
|
+
end
|
44
|
+
|
45
|
+
def assign_values
|
46
|
+
@output = {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def valid_input?
|
50
|
+
@required_fields.blank? || input.any?{ |k,v| @required_fields.include?(k) && v.present? }
|
51
|
+
end
|
52
|
+
|
53
|
+
def process_result_codes
|
54
|
+
status_messages = []
|
55
|
+
error_messages = []
|
56
|
+
|
57
|
+
@results_string.split(",").each do |lookup|
|
58
|
+
if (match = @result_codes.detect{ |c,m| c == lookup })
|
59
|
+
code, message = *match
|
60
|
+
|
61
|
+
if code[1] == 'E'
|
62
|
+
error_messages << message
|
63
|
+
else
|
64
|
+
status_messages << message
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
output[:result_codes] = @results_string
|
70
|
+
output[:messages] = {}
|
71
|
+
output[:messages][:status] = status_messages if status_messages.present?
|
72
|
+
output[:messages][:error] = error_messages if error_messages.present?
|
73
|
+
end
|
74
|
+
|
75
|
+
def data_dir
|
76
|
+
@data_dir ||= '/opt/melissa_data/data'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MelissaData::NativeObject
|
2
|
+
|
3
|
+
class Client
|
4
|
+
attr_reader :license
|
5
|
+
|
6
|
+
def initialize(license='INVALID LICENSE')
|
7
|
+
@license = license
|
8
|
+
@email_object = MelissaData::Email.new(:license => license)
|
9
|
+
end
|
10
|
+
|
11
|
+
def process_email(email=nil)
|
12
|
+
email ||= ''
|
13
|
+
output = {}
|
14
|
+
input = {:email => email.to_s}
|
15
|
+
output = @email_object.process(input)
|
16
|
+
output.delete_if{ |k,v| v.to_s.strip == "" }
|
17
|
+
output
|
18
|
+
end
|
19
|
+
|
20
|
+
def email_build_number
|
21
|
+
@email_object.obj.GetBuildNumber
|
22
|
+
end
|
23
|
+
|
24
|
+
def version
|
25
|
+
MelissaData::VERSION
|
26
|
+
end
|
27
|
+
|
28
|
+
def address
|
29
|
+
"SharedObjects"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module MelissaData::NativeObject
|
2
|
+
class Email < Base
|
3
|
+
def initialize(opts={})
|
4
|
+
@obj = MdEmailRubyWrapper::MdEmail.new
|
5
|
+
obj.SetPathToEmailFiles(data_dir)
|
6
|
+
obj.SetCorrectSyntax(1) # Enable/disable syntax correction
|
7
|
+
obj.SetStandardizeCasing(1) # Enable/disable lowercasing of email
|
8
|
+
obj.SetDatabaseLookup(1) # Enable/disable lookups against internal database of known domain names
|
9
|
+
obj.SetMXLookup(0) # Enable/disable DNS lookups for MX records (slower - but can be more accurate)
|
10
|
+
obj.SetUpdateDomain(1)
|
11
|
+
|
12
|
+
@result_codes = [
|
13
|
+
['ES01', "Valid domain name"],
|
14
|
+
['ES02', "Invalid domain name"],
|
15
|
+
['ES03', "Domain name could not be verified"],
|
16
|
+
['ES10', "Syntax was changed/corrected"],
|
17
|
+
['ES11', "Top level domain was changed"],
|
18
|
+
['ES12', "Domain name spelling was corrected"],
|
19
|
+
['ES13', "Domain name was updated"],
|
20
|
+
['EE01', "Syntax error in email address"],
|
21
|
+
['EE02', "Top level domain not found"],
|
22
|
+
['EE03', "Mail server not found"]
|
23
|
+
]
|
24
|
+
|
25
|
+
@defaults = {
|
26
|
+
:email => ''
|
27
|
+
}
|
28
|
+
|
29
|
+
# At least one of these needs to be present
|
30
|
+
@required_fields = [:email]
|
31
|
+
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_input
|
36
|
+
obj.VerifyEmail(input[:email].to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def assign_values
|
40
|
+
@output = {
|
41
|
+
:mailbox_name => obj.GetMailBoxName,
|
42
|
+
:domain_name => obj.GetDomainName,
|
43
|
+
:top_level_domain => obj.GetTopLevelDomain,
|
44
|
+
:top_level_domain_description => obj.GetTopLevelDomainDescription,
|
45
|
+
:email => obj.GetEmailAddress,
|
46
|
+
:lookup_code => obj.GetStatusCode
|
47
|
+
}
|
48
|
+
|
49
|
+
# V - Verified
|
50
|
+
# U - Unverified - not confirmed by lookup, but was not in the invalid domains list
|
51
|
+
# X - Bad email address - either not located by MX lookup, or was on the invalid domains list
|
52
|
+
if output[:lookup_code] == 'U'
|
53
|
+
obj.SetMXLookup(1)
|
54
|
+
obj.VerifyEmail(input[:email])
|
55
|
+
obj.SetMXLookup(0)
|
56
|
+
|
57
|
+
process_result_codes
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module MelissaData::NativeObject
|
2
|
+
class Geo < Base
|
3
|
+
def initialize(opts={})
|
4
|
+
@obj = MdGeoRubyWrapper::MdGeo.new
|
5
|
+
obj.SetPathToGeoCodeDataFiles(data_dir)
|
6
|
+
obj.SetPathToGeoPointDataFiles(data_dir)
|
7
|
+
|
8
|
+
@result_codes = [
|
9
|
+
['GS01', "Geocoded to ZIP+4"],
|
10
|
+
['GS02', "Geocoded to ZIP+2"],
|
11
|
+
['GS03', "Geocoded to ZIP"],
|
12
|
+
['GS05', "Geocoded to rooftop level"],
|
13
|
+
['GS06', "Geocoded to interpolated rooftop level"],
|
14
|
+
['GE01', "Invalid zip code"],
|
15
|
+
['GE02', "Zip code not found"],
|
16
|
+
['GE03', "Demo mode"],
|
17
|
+
['GE04', "Expired database"]
|
18
|
+
]
|
19
|
+
|
20
|
+
@defaults = {
|
21
|
+
:zip => '',
|
22
|
+
:plus4 => ''
|
23
|
+
}
|
24
|
+
|
25
|
+
# At least one of these needs to be present
|
26
|
+
@required_fields = [:zip]
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_input
|
32
|
+
if ((dpc = input[:delivery_point_code]) || (dpc = input[:dpc])).present?
|
33
|
+
obj.GeoPoint(input[:zip].to_s, input[:plus4].to_s, dpc.to_s)
|
34
|
+
else
|
35
|
+
obj.GeoCode(input[:zip].to_s, input[:plus4].to_s)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def assign_values
|
40
|
+
@output = {
|
41
|
+
:lat => obj.GetLatitude,
|
42
|
+
:lng => obj.GetLongitude,
|
43
|
+
:county_name => obj.GetCountyName,
|
44
|
+
:county_fips => obj.GetCountyFips,
|
45
|
+
:census_tract => obj.GetCensusTract,
|
46
|
+
:census_block => obj.GetCensusBlock,
|
47
|
+
:place_name => obj.GetPlaceName,
|
48
|
+
:place_code => obj.GetPlaceCode,
|
49
|
+
:time_zone => obj.GetTimeZone,
|
50
|
+
:time_zone_code => obj.GetTimeZoneCode,
|
51
|
+
|
52
|
+
:cbsa_code => obj.GetCBSACode,
|
53
|
+
:cbsa_level => obj.GetCBSALevel,
|
54
|
+
:cbsa_title => obj.GetCBSATitle,
|
55
|
+
:cbsa_division_code => obj.GetCBSADivisionCode,
|
56
|
+
:cbsa_division_title => obj.GetCBSADivisionTitle,
|
57
|
+
:cbsa_division_level => obj.GetCBSADivisionLevel
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module MelissaData::NativeObject
|
2
|
+
class IpLocator < Base
|
3
|
+
def initialize(opts={})
|
4
|
+
@obj = MdIpLocatorRubyWrapper::MdIpLocator.new
|
5
|
+
obj.SetPathToIpLocatorFiles(data_dir)
|
6
|
+
|
7
|
+
@result_codes = [
|
8
|
+
['IS01', "IP Address found in database"],
|
9
|
+
['IS02', "IP Address unknown"],
|
10
|
+
['IE01', "IP Address was empty or not well formed"]
|
11
|
+
]
|
12
|
+
|
13
|
+
@defaults = {
|
14
|
+
:ip => ''
|
15
|
+
}
|
16
|
+
|
17
|
+
# At least one of these needs to be present
|
18
|
+
@required_fields = [:ip]
|
19
|
+
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_input
|
24
|
+
obj.LocateIpAddress(input[:ip].to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
def assign_values
|
28
|
+
@output = {
|
29
|
+
:domain_name => obj.GetDomainName,
|
30
|
+
:isp => obj.GetISP,
|
31
|
+
:city => obj.GetCity,
|
32
|
+
:region => obj.GetRegion,
|
33
|
+
:zip => obj.GetZip,
|
34
|
+
:country => obj.GetCountry,
|
35
|
+
:country_abbrev => obj.GetCountryAbbreviation,
|
36
|
+
:lat => obj.GetLatitude,
|
37
|
+
:lng => obj.GetLongitude
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module MelissaData::NativeObject
|
2
|
+
class Name < Base
|
3
|
+
def initialize(opts={})
|
4
|
+
@obj = MdNameRubyWrapper::MdName.new
|
5
|
+
obj.SetPathToNameFiles(data_dir)
|
6
|
+
|
7
|
+
@result_codes = [
|
8
|
+
['NS01', "Parsing was successful"],
|
9
|
+
['NS02', "There was a parse error"],
|
10
|
+
['NS03', "The spelling of the FirstName field was corrected"],
|
11
|
+
['NS04', "The spelling of the FirstName2 field was corrected"],
|
12
|
+
['NE01', "Two names were detected but the FullName string was not in a recognized format"],
|
13
|
+
['NE02', "Multiple first names - could not accurately genderize"],
|
14
|
+
['NE03', "A vulgarity was detected in the first name"],
|
15
|
+
['NE04', "The name contained words found on the list of nuisance names [such as Mickey Mouse]"],
|
16
|
+
['NE05', "The name contained words normally found in a company name"],
|
17
|
+
['NE06', "The named contained a non-alphabetic character"]
|
18
|
+
]
|
19
|
+
|
20
|
+
@defaults = {
|
21
|
+
:first_name => '',
|
22
|
+
:last_name => ''
|
23
|
+
}
|
24
|
+
|
25
|
+
# At least one of these needs to be present
|
26
|
+
@required_fields = [:first_name, :last_name, :full_name]
|
27
|
+
|
28
|
+
# 0 - don't correct spelling of first name
|
29
|
+
# 1 - attempt to correct common misspellings of first name
|
30
|
+
obj.SetFirstNameSpellingCorrection(1)
|
31
|
+
|
32
|
+
# Formatting of the FullName input
|
33
|
+
# 1 - Definitely Full
|
34
|
+
# 2 - Very Likely Full
|
35
|
+
# 3 - Probably Full
|
36
|
+
# 4 - Varying (default)
|
37
|
+
# 5 - Probably Inverse
|
38
|
+
# 6 - Very Likely Inverse
|
39
|
+
# 7 - Definitely Inverse
|
40
|
+
# 8 - Mixed First Name
|
41
|
+
# 9 - Mixed Last Name
|
42
|
+
obj.SetPrimaryNameHint(1)
|
43
|
+
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_input
|
48
|
+
input[:full_name] = ("%s %s" % [input[:first_name], input[:last_name]]) if input[:full_name].blank?
|
49
|
+
obj.ClearProperties
|
50
|
+
obj.SetFullName input[:full_name].to_s
|
51
|
+
obj.Parse
|
52
|
+
end
|
53
|
+
|
54
|
+
def assign_values
|
55
|
+
genders = {
|
56
|
+
'M' => 'Male',
|
57
|
+
'F' => 'Female',
|
58
|
+
'U' => 'Unknown first name',
|
59
|
+
'N' => 'Neutral first name'
|
60
|
+
}
|
61
|
+
|
62
|
+
@output = {
|
63
|
+
:prefix => obj.GetPrefix,
|
64
|
+
:first_name => obj.GetFirstName,
|
65
|
+
:middle_name => obj.GetMiddleName,
|
66
|
+
:last_name => obj.GetLastName,
|
67
|
+
:suffix => obj.GetSuffix,
|
68
|
+
:gender => obj.GetGender,
|
69
|
+
:gender_string => genders[obj.GetGender]
|
70
|
+
}
|
71
|
+
# status_messages << "First name spelling was corrected" if name_obj.GetChangeCode == 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module MelissaData::NativeObject
|
2
|
+
class Phone < Base
|
3
|
+
def initialize(opts={})
|
4
|
+
@obj = MdPhoneRubyWrapper::MdPhone.new
|
5
|
+
if obj.Initialize(data_dir) != 0
|
6
|
+
raise obj.GetInitializeErrorString
|
7
|
+
end
|
8
|
+
|
9
|
+
@result_codes = [
|
10
|
+
["PS01", "10 digit match"],
|
11
|
+
["PS02", "7 digit match"],
|
12
|
+
["PS03", "Corrected Area Code"],
|
13
|
+
["PS04", "Outside Demo Range"],
|
14
|
+
["PS05", "Expired Database"],
|
15
|
+
["PS06", "Updated Area Code"],
|
16
|
+
["PS07", "Exchange Type: Cell Phone"],
|
17
|
+
["PS08", "Exchange Type: Land Line"],
|
18
|
+
["PS09", "Exchange Type: VOIP"],
|
19
|
+
["PS10", "Phone Type: Residential"],
|
20
|
+
["PS11", "Phone Type: Business"],
|
21
|
+
["PS12", "Phone Type: Small Business or Home Office"],
|
22
|
+
["PE01", "Bad area code"],
|
23
|
+
["PE02", "Blank phone number"],
|
24
|
+
["PE03", "Bad phone number - too many or too few digits"],
|
25
|
+
["PE04", "Multiple phone matches"],
|
26
|
+
["PE05", "Bad prefix - does not exist in the database"],
|
27
|
+
["PE06", "Bad zip code"]
|
28
|
+
]
|
29
|
+
|
30
|
+
@defaults = {
|
31
|
+
:phone => '',
|
32
|
+
:zip => ''
|
33
|
+
}
|
34
|
+
|
35
|
+
# At least one of these needs to be present
|
36
|
+
@required_fields = [:phone]
|
37
|
+
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_input
|
42
|
+
obj.Lookup(input[:phone].to_s, input[:zip].to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
def assign_values
|
46
|
+
@output = {
|
47
|
+
:lat => obj.GetLatitude,
|
48
|
+
:lng => obj.GetLongitude,
|
49
|
+
:city => obj.GetCity,
|
50
|
+
:state => obj.GetState,
|
51
|
+
:county_name => obj.GetCountyName,
|
52
|
+
:county_fips => obj.GetCountyFips,
|
53
|
+
:country_code => obj.GetCountryCode,
|
54
|
+
:time_zone => obj.GetTimeZone,
|
55
|
+
:time_zone_code => obj.GetTimeZoneCode,
|
56
|
+
:msa => obj.GetMsa,
|
57
|
+
:pmsa => obj.GetPmsa,
|
58
|
+
:time_zone => obj.GetTimeZone,
|
59
|
+
:time_zone_code => obj.GetTimeZoneCode,
|
60
|
+
:area_code => obj.GetAreaCode,
|
61
|
+
:prefix => obj.GetPrefix,
|
62
|
+
:suffix => obj.GetSuffix,
|
63
|
+
:extension => obj.GetExtension
|
64
|
+
}
|
65
|
+
|
66
|
+
codes = result_codes.split(',')
|
67
|
+
output[:exchange_type] = if codes.include?('PS07')
|
68
|
+
'Cell Phone'
|
69
|
+
|
70
|
+
elsif codes.include?('PS08')
|
71
|
+
'Land Line'
|
72
|
+
|
73
|
+
elsif codes.include?('PS09')
|
74
|
+
'VOIP'
|
75
|
+
end
|
76
|
+
|
77
|
+
output[:phone_type] = if codes.include?('PS07')
|
78
|
+
'Residential'
|
79
|
+
|
80
|
+
elsif codes.include?('PS07')
|
81
|
+
'Business'
|
82
|
+
|
83
|
+
elsif codes.include?('PS07')
|
84
|
+
'Small Business or Home Office'
|
85
|
+
end
|
86
|
+
|
87
|
+
# if opts[:zip].present?
|
88
|
+
# phone_obj.CorrectAreaCode(opts[:phone], opts[:zip])
|
89
|
+
# hsh[:new_area_code] = phone_obj.GetNewAreaCode
|
90
|
+
# end
|
91
|
+
|
92
|
+
output[:formatted_phone] = "(#{output[:area_code]}) #{output[:prefix]}-#{output[:suffix]}"
|
93
|
+
output[:formatted_phone] << " x#{output[:extension]}" if output[:extension].present?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|