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
@@ -0,0 +1,11 @@
1
+ module PossibleEmail
2
+ class Image
3
+ attr_reader :url, :service, :url_proxied
4
+
5
+ def initialize(data)
6
+ @url = data['url']
7
+ @service = data['service']
8
+ @url_proxied = data['url_proxied']
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module PossibleEmail
2
+ class Membership
3
+ attr_reader :profile_url, :profile_id, :username, :site_name
4
+
5
+ def initialize(data)
6
+ @profile_url = data['profile_url']
7
+ @profile_id = data['profile_id']
8
+ @username = data['username']
9
+ @site_name = data['site_name']
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module PossibleEmail
2
+ class Occupation
3
+ attr_reader :job_title, :company
4
+
5
+ def initialize(data)
6
+ @job_title = data['job_title']
7
+ @company = data['company']
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ module PossibleEmail
2
+ class Phone
3
+
4
+ end
5
+ end
@@ -0,0 +1,58 @@
1
+ require 'possible_email/profile/membership'
2
+ require 'possible_email/profile/occupation'
3
+ require 'possible_email/profile/image'
4
+ require 'possible_email/profile/phone'
5
+
6
+ module PossibleEmail
7
+ class NoDataError < ArgumentError; end
8
+
9
+ class Profile
10
+ attr_reader :data, :email, :name, :first_name, :last_name, :friendly_name,
11
+ :location, :headline, :success, :occupations, :memberships,
12
+ :images, :phones
13
+
14
+ def initialize(data)
15
+ @data = data
16
+
17
+ fail NoDataError, 'Data given was empty' if @data.empty?
18
+
19
+ retrieve_attribute
20
+ retrieve_attribute_collection
21
+ end
22
+
23
+ def attributes
24
+ instance_variables.select { |i| i != :@data }.map { |i| i.to_s[1..-1] }
25
+ end
26
+
27
+ def to_s
28
+ "#{@name} - #{email}"
29
+ end
30
+
31
+ private
32
+
33
+ def retrieve_attribute
34
+ %w(email name first_name last_name friendly_name location headline).each do |attr|
35
+ instance_variable_set("@#{attr}", @data['contact'][attr])
36
+ end
37
+ @success = @data['success']
38
+ end
39
+
40
+ def retrieve_attribute_collection
41
+ %w(occupations memberships images phones).each do |collection|
42
+ build_collection_instance_variable(collection)
43
+ end
44
+ end
45
+
46
+ def build_collection_instance_variable(collection)
47
+ collection_array = @data['contact'][collection].reduce([]) do |col, c|
48
+ col << find_klass_name(collection).new(c)
49
+ end
50
+
51
+ instance_variable_set("@#{collection}", collection_array)
52
+ end
53
+
54
+ def find_klass_name(collection)
55
+ PossibleEmail.const_get collection[0..-2].capitalize # 'images' => 'Image'
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ require 'possible_email/response'
2
+ require 'possible_email/response_getter'
3
+ require 'possible_email/profile/profile'
4
+
5
+ module PossibleEmail
6
+ class RapportiveRequester
7
+ def self.request(*emails)
8
+ new(emails).request
9
+ end
10
+
11
+ def initialize(emails)
12
+ @emails = emails.flatten
13
+ end
14
+
15
+ def request
16
+ email_profiles = accumulate_email_profiles
17
+
18
+ puts # newline after ...F... HACK
19
+
20
+ response = Response.new(email_profiles)
21
+ response.size == 1 ? response.first : response
22
+ end
23
+
24
+ private
25
+
26
+ # HACK
27
+ def accumulate_email_profiles
28
+ @emails.reduce([]) do |profiles, email|
29
+ profile = create_profile_for_email(email)
30
+ print profile ? '.' : 'F'
31
+ profiles << profile
32
+ end
33
+ end
34
+
35
+ def create_profile_for_email(email)
36
+ session_token = ResponseGetter.create_session_token(email)
37
+ return unless session_token
38
+
39
+ response = ResponseGetter.retrieve_email_profile_using_session_token(email, session_token)
40
+ return unless response
41
+
42
+ Profile.new(response) if useful_response?(response)
43
+ end
44
+
45
+ def useful_response?(response)
46
+ response['success'] != 'nothing_useful'
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ module PossibleEmail
2
+ class Response
3
+ include Enumerable
4
+
5
+ def initialize(profiles)
6
+ # remove nils created by ResponseGetter
7
+ @profiles = profiles.compact
8
+ end
9
+
10
+ def each(&block)
11
+ @profiles.each(&block)
12
+ end
13
+
14
+ def [](*args)
15
+ @profiles[*args]
16
+ end
17
+
18
+ def method_missing(method, *)
19
+ respond_to?(method) ? @profiles.send(method) : super
20
+ end
21
+
22
+ def respond_to?(method, *)
23
+ @profiles.respond_to?(method)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,37 @@
1
+ STATUS_URL = 'https://rapportive.com/login_status?user_email='
2
+ PROFILE_URL = 'https://profiles.rapportive.com/contacts/email/'
3
+
4
+ module PossibleEmail
5
+ class ResponseGetter
6
+ class << self
7
+ def create_session_token(email)
8
+ status_url = STATUS_URL + email
9
+ response = request_url status_url
10
+
11
+ valid_response?(response) ? response['session_token'] : nil
12
+ end
13
+
14
+ def retrieve_email_profile_using_session_token(email, session_token)
15
+ profile_url = PROFILE_URL + email
16
+ header = { 'X-Session-Token' => session_token }
17
+ response = request_url profile_url, header
18
+
19
+ response.nil? ? nil : response
20
+ end
21
+
22
+ private
23
+
24
+ def request_url(url, header = {})
25
+ request = HTTPI::Request.new
26
+ request.url = url
27
+ request.headers = header
28
+
29
+ JSON.parse(HTTPI.get(request).body)
30
+ end
31
+
32
+ def valid_response?(response)
33
+ response['error'].nil? && response['status'] == 200
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module PossibleEmail
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'possible_email/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "possible_email"
8
+ spec.version = PossibleEmail::VERSION
9
+ spec.authors = ["Patrick Perey"]
10
+ spec.email = ["the4dpatrick@yahoo.com"]
11
+ spec.summary = %q{Find anyone's email using their first name, last name, and domain.}
12
+ spec.description = %q{Find anyone's email using their first name, last name, and domain.}
13
+ spec.homepage = "https://github.com/the4dpatrick/possible-email"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'httpi', '~> 2.2', '>= 2.2.5'
22
+ spec.add_dependency 'thor', '~> 0.19', '>= 0.19.1'
23
+ spec.add_dependency 'rubyntlm', '~> 0.3.4'
24
+
25
+ spec.add_development_dependency 'bundler', "~> 1.5"
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rspec', '~> 3.0.0'
28
+ spec.add_development_dependency 'guard'
29
+ spec.add_development_dependency 'guard-rspec'
30
+ spec.add_development_dependency 'guard-bundler'
31
+ spec.add_development_dependency 'rb-fsevent'
32
+ spec.add_development_dependency 'growl'
33
+ spec.add_development_dependency 'coveralls'
34
+
35
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe PossibleEmail::Permutator do
4
+
5
+ describe '.call' do
6
+
7
+ context 'when one domain is given' do
8
+ let(:permutations) { PossibleEmail::Permutator.call('kevin', 'rose', 'gmail.com') }
9
+
10
+ it 'returns an array' do
11
+ expect(permutations).to be_instance_of(Array)
12
+ end
13
+
14
+ it 'returns an array equal in size to every pattern in PATTERNS' do
15
+ expect(permutations.size).to eq(PATTERNS.split.size)
16
+ end
17
+
18
+ it 'replaces all placeholder patterns' do
19
+ expect(permutations.to_s).not_to match(/[\{\}]+/)
20
+ end
21
+ end
22
+
23
+ context 'when multiple domains are given' do
24
+
25
+ context 'as splat String arguments' do
26
+ let(:permutations) { PossibleEmail::Permutator.call('kevin', 'rose', 'gmail.com', 'yahoo.com') }
27
+
28
+ it 'returns an array' do
29
+ expect(permutations).to be_instance_of(Array)
30
+ end
31
+
32
+ it 'returns an array equal in size to all permutations created with PATTERNS' do
33
+ expect(permutations.size).to eq(PATTERNS.split.size * 2) # two domains
34
+ end
35
+
36
+ it 'replaces all placeholder patterns' do
37
+ expect(permutations.to_s).not_to match(/[\{\}]+/)
38
+ end
39
+
40
+ it 'has the correct number of `yahoo.com` emails' do
41
+ yahoo_email_count = permutations.to_s.scan(/yahoo\.com/).size
42
+
43
+ expect(yahoo_email_count).to eq(PATTERNS.split.size)
44
+ end
45
+
46
+ it 'has the correct number of `gmail.com` emails' do
47
+ gmail_email_count = permutations.to_s.scan(/gmail\.com/).size
48
+
49
+ expect(gmail_email_count).to eq(PATTERNS.split.size)
50
+ end
51
+ end
52
+
53
+ context 'as an Array' do
54
+ let(:domains) { ['gmail.com', 'yahoo.com'] }
55
+ let(:permutations) { PossibleEmail::Permutator.call('kevin', 'rose', domains) }
56
+
57
+ it 'returns an array' do
58
+ expect(permutations).to be_instance_of(Array)
59
+ end
60
+
61
+ it 'returns an array equal in size to every pattern in PATTERNS' do
62
+ expect(permutations.size).to eq(PATTERNS.split.size * domains.size) # two domains
63
+ end
64
+
65
+ it 'replaces all placeholder patterns' do
66
+ expect(permutations.to_s).not_to match(/[\{\}]+/)
67
+ end
68
+
69
+ it 'has the correct number of `yahoo.com` emails' do
70
+ yahoo_email_count = permutations.to_s.scan(/yahoo\.com/).size
71
+
72
+ expect(yahoo_email_count).to eq(PATTERNS.split.size)
73
+ end
74
+
75
+ it 'has the correct number of `gmail.com` emails' do
76
+ gmail_email_count = permutations.to_s.scan(/gmail\.com/).size
77
+
78
+ expect(gmail_email_count).to eq(PATTERNS.split.size)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe PossibleEmail::Image do
4
+ let(:image) do PossibleEmail::Image.new('url' => 'image_url',
5
+ 'service' => 'Facebook',
6
+ 'url_proxied' => 'url_proxied')
7
+ end
8
+
9
+ it 'returns the associated url' do
10
+ expect(image.url).to eq('image_url')
11
+ end
12
+
13
+ it 'returns the associated service' do
14
+ expect(image.service).to eq('Facebook')
15
+ end
16
+
17
+ it 'returns the associated url_proxied' do
18
+ expect(image.url_proxied).to eq('url_proxied')
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe PossibleEmail::Membership do
4
+ let(:membership) do
5
+ PossibleEmail::Membership.new('profile_url' => 'http://www.facebook.com/profile',
6
+ 'profile_id' => '1234',
7
+ 'username' => 'profile',
8
+ 'site_name' => 'Facebook')
9
+ end
10
+
11
+ it 'returns the associated profile_url' do
12
+ expect(membership.profile_url).to eq('http://www.facebook.com/profile')
13
+ end
14
+
15
+ it 'returns the associated profile_id' do
16
+ expect(membership.profile_id).to eq('1234')
17
+ end
18
+
19
+ it 'returns the associated username' do
20
+ expect(membership.username).to eq('profile')
21
+ end
22
+
23
+ it 'returns the associated site_name' do
24
+ expect(membership.site_name).to eq('Facebook')
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe PossibleEmail::Occupation do
4
+ let(:occupation) do
5
+ PossibleEmail::Occupation.new('job_title' => 'entrepreneur', 'company' => 'startup')
6
+ end
7
+
8
+ it 'returns the associated job_title' do
9
+ expect(occupation.job_title).to eq('entrepreneur')
10
+ end
11
+
12
+ it 'returns the associated company' do
13
+ expect(occupation.company).to eq('startup')
14
+ end
15
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ describe PossibleEmail::Profile do
4
+ let(:data) { JSON.parse(File.read(File.expand_path('spec/fixtures/rapportive_example_data.json'))) }
5
+ let(:profile) { PossibleEmail::Profile.new(data) }
6
+
7
+ it 'returns an error if no data is given' do
8
+ expect { PossibleEmail::Profile.new({}) }.to raise_error(PossibleEmail::NoDataError)
9
+ end
10
+
11
+ describe '#attributes' do
12
+ it 'returns a list of available' do
13
+ expect(profile.attributes).to eq(["email", "name", "first_name", "last_name", "friendly_name", "location", "headline", "success", "occupations", "memberships", "images", "phones"])
14
+ end
15
+ end
16
+
17
+ describe '#to_s' do
18
+ it 'returns formatted name and email' do
19
+ expect(profile.to_s).to eq('Kevin Rose - kevinrose@gmail.com')
20
+ end
21
+ end
22
+
23
+ describe 'Profile Attributes' do
24
+ it 'returns the associated email address' do
25
+ expect(profile.data).to be_instance_of(Hash)
26
+ end
27
+
28
+ it 'returns the associated email address' do
29
+ expect(profile.email).to eq('kevinrose@gmail.com')
30
+ end
31
+
32
+ it 'returns the associated name' do
33
+ expect(profile.name).to eq('Kevin Rose')
34
+ end
35
+
36
+ it 'returns the associated first name' do
37
+ expect(profile.first_name).to eq('Kevin')
38
+ end
39
+
40
+ it 'returns the associated last name' do
41
+ expect(profile.last_name).to eq('Rose')
42
+ end
43
+
44
+ it 'returns the associated friendly_name' do
45
+ expect(profile.friendly_name).to eq('Kevin')
46
+ end
47
+
48
+ it 'returns the associated locaiton' do
49
+ expect(profile.location).to eq('San Francisco Bay Area')
50
+ end
51
+
52
+ it 'returns the associated headline' do
53
+ expect(profile.headline).to eq('General Partner at Google Ventures')
54
+ end
55
+
56
+ it 'returns the type of success' do
57
+ expect(profile.success).to eq('image_and_occupation_and_useful_membership')
58
+ end
59
+
60
+ describe 'occupations' do
61
+ it 'returns an array with the correct number of occupations' do
62
+ expect(profile.occupations.size).to eq(2)
63
+ end
64
+
65
+ it 'returns an array of Occupation instances' do
66
+ expect(profile.occupations).to all be_instance_of(PossibleEmail::Occupation)
67
+ end
68
+ end
69
+
70
+ describe 'images' do
71
+ it 'returns an array with the correct number of images' do
72
+ expect(profile.images.size).to eq(12)
73
+ end
74
+
75
+ it 'returns an array of Image instances' do
76
+ expect(profile.images).to all be_instance_of(PossibleEmail::Image)
77
+ end
78
+ end
79
+
80
+ describe 'memberships' do
81
+ it 'returns an array with the correct number of memberships' do
82
+ expect(profile.memberships.size).to eq(18)
83
+ end
84
+
85
+ it 'returns an array of Memberships instances' do
86
+ expect(profile.memberships).to all be_instance_of(PossibleEmail::Membership)
87
+ end
88
+ end
89
+
90
+ # describe 'phones' do
91
+ # it 'returns an array with the correct number of memberships' do
92
+ # expect(profile.phones.size).to eq(0)
93
+ # end
94
+
95
+ # it 'returns an array of Memberships instances' do
96
+ # expect(profile.phones).to all be_instance_of(PossibleEmail::Phone)
97
+ # end
98
+ # end
99
+ end
100
+ end