melissadata 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|