possible_email 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +17 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +2 -0
  7. data/Guardfile +11 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +161 -0
  10. data/Rakefile +13 -0
  11. data/bin/possible_email +6 -0
  12. data/lib/possible_email.rb +56 -0
  13. data/lib/possible_email/cli.rb +33 -0
  14. data/lib/possible_email/error.rb +3 -0
  15. data/lib/possible_email/patterns.rb +38 -0
  16. data/lib/possible_email/permutator.rb +64 -0
  17. data/lib/possible_email/profile/image.rb +11 -0
  18. data/lib/possible_email/profile/membership.rb +12 -0
  19. data/lib/possible_email/profile/occupation.rb +10 -0
  20. data/lib/possible_email/profile/phone.rb +5 -0
  21. data/lib/possible_email/profile/profile.rb +58 -0
  22. data/lib/possible_email/rapportive_requester.rb +49 -0
  23. data/lib/possible_email/response.rb +26 -0
  24. data/lib/possible_email/response_getter.rb +37 -0
  25. data/lib/possible_email/version.rb +3 -0
  26. data/possible_email.gemspec +35 -0
  27. data/spec/PossibleEmail/possible_email/permutator_spec.rb +83 -0
  28. data/spec/PossibleEmail/possible_email/profile/image_spec.rb +20 -0
  29. data/spec/PossibleEmail/possible_email/profile/membership_spec.rb +26 -0
  30. data/spec/PossibleEmail/possible_email/profile/occupation_spec.rb +15 -0
  31. data/spec/PossibleEmail/possible_email/profile/phone_spec.rb +0 -0
  32. data/spec/PossibleEmail/possible_email/profile/profile_spec.rb +100 -0
  33. data/spec/PossibleEmail/possible_email/rapportive_requester_spec.rb +67 -0
  34. data/spec/PossibleEmail/possible_email/response_getter_spec.rb +50 -0
  35. data/spec/PossibleEmail/possible_email/response_spec.rb +22 -0
  36. data/spec/PossibleEmail/possible_email_spec.rb +92 -0
  37. data/spec/fixtures/rapportive_example_data.json +509 -0
  38. data/spec/spec_helper.rb +12 -0
  39. metadata +274 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e7c59b28497e97a707ccef97030b2ba5c71f5902
4
+ data.tar.gz: 7545a313b3364563cbad881f5bc3620ecaaccb7c
5
+ SHA512:
6
+ metadata.gz: d4a1ecc7e3e67f6ffa3b35a2b424319877a61e9bfde31b3907769a860412f2de3f4a12065be4e952b9fb26a4bbfc5524a1e8301a4b22ae14459adfea05145d1a
7
+ data.tar.gz: 2c4657ce20186cf5d03416e02347257af0b127b2d49aa5015280233c65a4c9dedd8bffc420ddf7638bc21e2b746058810929834f37a6ce7991f48d2231e23d11
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --warnings
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.1.0
5
+ - 2.0.0
6
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,11 @@
1
+ guard :bundler do
2
+ watch('Gemfile')
3
+ watch(/^.+\.gemspec/)
4
+ end
5
+
6
+ guard :rspec, cmd: 'bundle exec rspec' do
7
+ watch(%r{^spec/.+_spec\.rb$})
8
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
9
+ watch('spec/spec_helper.rb') { "spec" }
10
+ end
11
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Patrick Perey
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,161 @@
1
+ PossibleEmail
2
+ =========
3
+ A Ruby Gem to find someone's possible email address using their first name, last name, and domain.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/possible_email.svg)](http://badge.fury.io/rb/possible_email)
6
+ [![Build Status](https://travis-ci.org/the4dpatrick/possible-email.svg?branch=master)](https://travis-ci.org/the4dpatrick/possible-email)
7
+ [![Coverage Status](https://coveralls.io/repos/the4dpatrick/possible-email/badge.png?branch=master)](https://coveralls.io/r/the4dpatrick/possible-email?branch=master)
8
+ [![Code Climate](https://codeclimate.com/github/the4dpatrick/possible-email/badges/gpa.svg)](https://codeclimate.com/github/the4dpatrick/possible-email)
9
+
10
+ More information in my [blog post](http://patrickperey.com/possible-email "blog post") at [PatrickPerey.com](http://patrickperey.com "Patrick Perey Blog")
11
+
12
+ Installation
13
+ ------------
14
+ Install the gem:
15
+
16
+ ```
17
+ $ gem install possible_email
18
+ ```
19
+
20
+ Basic Usage
21
+ -----
22
+
23
+ ### Command Line Interface
24
+
25
+
26
+ Enter target's first name, last name, and possible domain name into the terminal using `search`
27
+
28
+ ```
29
+ $ possible_email search first_name last_name domain
30
+ ```
31
+
32
+ Not sure about the domain name? Just add multiple domains at the end
33
+
34
+ ```
35
+ $ possible_email search first_name last_name gmail.com, yahoo.com, live.com
36
+ ```
37
+
38
+
39
+ Just want to Confirm a single email address? Use `find_profile`
40
+
41
+ ```
42
+ $ possible_email find_profile test@example.com
43
+ ```
44
+ Multiple email address? Same `find_profile`
45
+
46
+ ```
47
+ $ possible_email find_profile test@example.com test1@example.com
48
+ ```
49
+ ### Ruby
50
+
51
+ ```ruby
52
+ require 'possible_email'
53
+
54
+ profiles = PossibleEmail.search('Kevin', 'Rose', 'gmail.com')
55
+ profiles #=> "#<PossibleEmail::Response>"
56
+ first_profile = profiles.first
57
+
58
+ first_profile.name #=> 'Kevin Rose'
59
+ first_profile.email #=> 'kevinrose@gmail.com'
60
+ first_profile.location #=> 'San Francisco Bay Area'
61
+ ```
62
+ Documentation
63
+ -------------
64
+ ### PossibleEmail
65
+
66
+ Available methods:
67
+
68
+ **search(first_name, last_name, *domain)**
69
+
70
+ Accepts three arguments `first_name`, `last_name`, and `domain`. PossibleEmail will use these three arguments to generate possible email addresses based on common email address patterns. PossibleEmail would then attempt to verify and return an email profile for each email address.
71
+
72
+ ```ruby
73
+ # Single domain name
74
+ PossibleEmail.search('bob', 'jones', 'gmail.com')
75
+
76
+ # Multiple domain names as Strings
77
+ PossibleEmail.search('bob', 'jones', 'gmail.com', 'yahoo.com')
78
+
79
+ # Multiple domain names as an Array
80
+ domains = ['gmail.com', 'yahoo.com', 'live.com']
81
+ PossibleEmail.search('bob', 'jones', domains)
82
+ ```
83
+
84
+ **find_profile(*emails)**
85
+
86
+ Accepts a list of email address string arguments or an array. Instead of generating email addresses based on name arguments, PossibleEmail would attempt to verify and return an email profile for each email addresses passed into the method.
87
+
88
+ ```ruby
89
+ # Comma-splitted email arguments
90
+ PossibleEmail.find_profile('test@example.com', 'test1@example.com')
91
+
92
+ # Array of emails
93
+ PossibleEmail.find_profile(['test@example.com', 'test1@example.com'])
94
+ ```
95
+
96
+ Both methods return a `PossibleEmail::Response` object. `PossibleEmail::Response` includes the `Enumerable` module, so all the methods you need to iterate through the profiles are available. The only exception when neither the `search` or `find_profile` is when there is only one profile within the response. In this case, the method returns the single Profile.
97
+
98
+ ### Profile
99
+
100
+ Class for the associated data connnected with a specific email address.
101
+
102
+ `Profile` attribute list:
103
+
104
+ * `email` - Returns the profile's email address
105
+ * `name` - Full name
106
+ * `first_name` - First name
107
+ * `last_name` - Last name
108
+ * `friendly_name` - First name or named used to address this person
109
+ * `location` - Location
110
+ * `headline` - Short blurb about person
111
+ * `success` - Type of response returned back from Rapportive API
112
+ * `occupations` - Array of Occupation objects
113
+ * `memberships` - Array of social network Membership objects
114
+ * `images` - Array of Image objects
115
+
116
+ ### Occupation
117
+
118
+ Class for person's jobs.
119
+
120
+ `Occupation` attribute list:
121
+
122
+ * `job_title` - Job title
123
+ * `company` - Company
124
+
125
+ ### Membership
126
+
127
+ Class for Social Network Accounts
128
+
129
+ `Membership` attribute list:
130
+
131
+ * `profile_url` - URL to person's website membership
132
+ * `profile_id` - Website profile ID
133
+ * `username` - Username
134
+ * `site_name` - Name of the website membership
135
+
136
+ ### Image
137
+
138
+ Class for images associated with email profile.
139
+
140
+ `Image` attribute list:
141
+
142
+ * `url` - Image url
143
+ * `service` - Where the image is located
144
+ * `url_proxied` - Rapportive image proxied URL
145
+
146
+ Notes
147
+ -----
148
+ * With great power, comes great responsibly
149
+ * Wrapper around the undocumented Rapportive API.
150
+ * Valid results may be hidden due to API's limitations
151
+ * Send Bitcoin `18fZ6muNmBrtENMZhnAjUw8eEsytmY8mZJ`
152
+
153
+
154
+ Contributing
155
+ ------------
156
+
157
+ 1. Fork it ( http://github.com/the4dpatrick/possible-email )
158
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
159
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
160
+ 4. Push to the branch (`git push origin my-new-feature`)
161
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc "Run all specs"
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = ['--color', '--format', 'nested']
9
+ t.pattern = 'spec/**/*_spec.rb'
10
+ t.verbose = false
11
+ end
12
+
13
+ task :default => :spec
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'possible_email'
4
+ require 'possible_email/cli'
5
+
6
+ PossibleEmail::CLI.start(ARGV)
@@ -0,0 +1,56 @@
1
+ require 'possible_email/error'
2
+ require 'possible_email/version'
3
+ require 'possible_email/permutator'
4
+ require 'possible_email/rapportive_requester'
5
+
6
+ require 'httpi'
7
+ require 'json'
8
+
9
+ HTTPI.log = false
10
+
11
+ EMAIL_REGEX = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
12
+ DOMAIN_REGEX = /^[^@]([\w\-]+\.)+([\w]{2,})$/
13
+ NAME_REGEX = /^\b[a-zA-Z]+\b$/
14
+
15
+ module PossibleEmail
16
+ class InvalidNameFormat < ArgumentError; end
17
+ class InvalidEmailFormat < ArgumentError; end
18
+
19
+ module_function
20
+
21
+ def search(first_name, last_name, *domain)
22
+ assign_instance_variables first_name, last_name, domain
23
+
24
+ fail InvalidNameFormat, "Name arguments were not formatted correctly #{[@first_name, @last_name, *@domain].inspect}" unless valid_names?
25
+
26
+ permutations = Permutator.call(@first_name, @last_name, @domain)
27
+ RapportiveRequester.request(permutations)
28
+ end
29
+
30
+ def find_profile(*emails)
31
+ @emails = emails.flatten
32
+
33
+ fail InvalidEmailFormat, "Email arguments were not formatted correctly #{@emails.inspect}" if invalid_emails?
34
+
35
+ RapportiveRequester.request(@emails)
36
+ end
37
+
38
+ private_class_method
39
+
40
+ def assign_instance_variables(first_name, last_name, domain)
41
+ @first_name = first_name
42
+ @last_name = last_name
43
+ @domain = domain.flatten
44
+ end
45
+
46
+ def invalid_emails?
47
+ @emails.any? { |email| email !~ EMAIL_REGEX }
48
+ end
49
+
50
+ def valid_names?
51
+ valid_domain = @domain.all? { |d| d =~ DOMAIN_REGEX }
52
+ valid_names = [@first_name, @last_name].all? { |n| n =~ NAME_REGEX }
53
+
54
+ valid_domain && valid_names
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ require 'thor'
2
+
3
+ module PossibleEmail
4
+ class CLI < Thor
5
+ # possible_email search
6
+
7
+ desc 'search', 'Search for valid emails using first name, last name, and domain name'
8
+ long_desc <<-LONGDESC
9
+ Takes in a first name, last name, and domain name.
10
+
11
+ Domain name can be a single domain like 'gmail.com', or multiple domain names like 'gmail.com', 'yahoo.com'
12
+
13
+ Prints out all of the found email profiles for valid email addresses with associated information
14
+ LONGDESC
15
+
16
+ def search(first_name, last_name, *domain)
17
+ profiles = PossibleEmail.search(first_name, last_name, domain)
18
+ puts profiles
19
+ end
20
+
21
+ # possible_email find_profile
22
+
23
+ desc 'find_profile', 'return a profile for one or more email addresses'
24
+ long_desc <<-LONGDESC
25
+ Prints out all of the found email profiles for valid email addresses with associated information
26
+ LONGDESC
27
+
28
+ def find_profile(*emails)
29
+ profiles = PossibleEmail.find_profile(emails)
30
+ puts profiles
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module PossibleEmail
2
+
3
+ end
@@ -0,0 +1,38 @@
1
+ PATTERNS = <<PATTERNS
2
+ {first_name}@{domain}
3
+ {last_name}@{domain}
4
+ {first_initial}@{domain}
5
+ {last_initial}@{domain}
6
+ {first_name}{last_name}@{domain}
7
+ {first_name}.{last_name}@{domain}
8
+ {first_initial}{last_name}@{domain}
9
+ {first_initial}.{last_name}@{domain}
10
+ {first_name}{last_initial}@{domain}
11
+ {first_name}.{last_initial}@{domain}
12
+ {first_initial}{last_initial}@{domain}
13
+ {first_initial}.{last_initial}@{domain}
14
+ {last_name}{first_name}@{domain}
15
+ {last_name}.{first_name}@{domain}
16
+ {last_name}{first_initial}@{domain}
17
+ {last_name}.{first_initial}@{domain}
18
+ {last_initial}{first_name}@{domain}
19
+ {last_initial}.{first_name}@{domain}
20
+ {last_initial}{first_initial}@{domain}
21
+ {last_initial}.{first_initial}@{domain}
22
+ {first_name}-{last_name}@{domain}
23
+ {first_initial}-{last_name}@{domain}
24
+ {first_name}-{last_initial}@{domain}
25
+ {first_initial}-{last_initial}@{domain}
26
+ {last_name}-{first_name}@{domain}
27
+ {last_name}-{first_initial}@{domain}
28
+ {last_initial}-{first_name}@{domain}
29
+ {last_initial}-{first_initial}@{domain}
30
+ {first_name}_{last_name}@{domain}
31
+ {first_initial}_{last_name}@{domain}
32
+ {first_name}_{last_initial}@{domain}
33
+ {first_initial}_{last_initial}@{domain}
34
+ {last_name}_{first_name}@{domain}
35
+ {last_name}_{first_initial}@{domain}
36
+ {last_initial}_{first_name}@{domain}
37
+ {last_initial}_{first_initial}@{domain}
38
+ PATTERNS
@@ -0,0 +1,64 @@
1
+ require 'possible_email/patterns'
2
+
3
+ module PossibleEmail
4
+ class Permutator
5
+ def self.call(first_name, last_name, *domain)
6
+ new(first_name, last_name, domain).call
7
+ end
8
+
9
+ def initialize(first_name, last_name, domain)
10
+ @first_name = first_name
11
+ @last_name = last_name
12
+ @domain = domain.flatten # HACK
13
+ @first_initial = first_name.chars.first
14
+ @last_initial = last_name.chars.first
15
+ end
16
+
17
+ def call
18
+ permutations = create_all_permutations
19
+ permutations
20
+ end
21
+
22
+ private
23
+
24
+ def create_all_permutations
25
+ @domain.reduce([]) do |permutations, domain|
26
+ substitute_pattern_placeholders_with_correct_values(domain)
27
+
28
+ separate_each_permutation(permutations)
29
+ end
30
+ end
31
+
32
+ def substitute_pattern_placeholders_with_correct_values(domain)
33
+ # reset after each domain iteration
34
+ @permutation_patterns = PATTERNS.dup
35
+
36
+ substitute_every_pattern_placeholder_except_domain
37
+
38
+ @permutation_patterns.gsub!('{domain}', domain)
39
+ end
40
+
41
+ def substitute_every_pattern_placeholder_except_domain
42
+ all_instance_variables_except_domain.each do |var|
43
+ @permutation_patterns.gsub!(placeholder(var), value_of(var))
44
+ end
45
+ end
46
+
47
+ def all_instance_variables_except_domain
48
+ instance_variables.dup - [:@domain]
49
+ end
50
+
51
+ def placeholder(var)
52
+ "{#{var.to_s[1..-1]}}"
53
+ end
54
+
55
+ def value_of(var)
56
+ instance_variable_get("@#{var[1..-1]}".to_sym)
57
+ end
58
+
59
+ def separate_each_permutation(permutations)
60
+ permutations << @permutation_patterns.split("\n")
61
+ permutations.flatten
62
+ end
63
+ end
64
+ end