kickboxer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kickboxer.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Solomon White
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,53 @@
1
+ # Kickboxer
2
+
3
+ Kickboxer is a wrapper around the FullContact API
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'kickboxer'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install kickboxer
18
+
19
+ ## Usage
20
+
21
+ The Full Contact API requires an API key. Register for a free key at
22
+ https://www.fullcontact.com/sign-up/. Once you receive your key, you can
23
+ configure Kickboxer to use it via:
24
+
25
+ Kickboxer.api_key = '01234decafbad'
26
+
27
+
28
+ The following API endpoints are implemented:
29
+
30
+ + Person
31
+ + lookup (by email, phone number, twitter username, or facebook username)
32
+ - User (TODO)
33
+ - Contact List (TODO)
34
+ - Contact (TODO)
35
+ - Snapshot (TODO)
36
+ - Subscription (TODO)
37
+ + Name
38
+ + normalize
39
+ + deduce
40
+ + similarity
41
+ + stats
42
+ + parser
43
+ - Icon (TODO)
44
+ - Provisioning (TODO)
45
+ - Batch Process (TODO)
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork it
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run specs"
5
+ task :spec do
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.rspec_opts = %w{--colour --format progress}
8
+ t.pattern = 'spec/**/*_spec.rb'
9
+ end
10
+ end
11
+
12
+ desc "Default: run specs."
13
+ task :default => :spec
data/kickboxer.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/kickboxer/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Solomon White"]
6
+ gem.email = ["rubysolo@gmail.com"]
7
+ gem.description = %q{Kickboxer}
8
+ gem.summary = %q{Kickboxer is a Ruby library for accessing the FullContact API}
9
+ gem.homepage = "https://github.com/rubysolo/kickboxer"
10
+
11
+ gem.add_development_dependency 'pry'
12
+ gem.add_development_dependency 'rake'
13
+ gem.add_development_dependency 'rspec'
14
+
15
+ gem.files = `git ls-files`.split($\)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.name = "kickboxer"
19
+ gem.require_paths = ["lib"]
20
+ gem.version = Kickboxer::VERSION
21
+ end
data/lib/kickboxer.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "kickboxer/version"
2
+
3
+ module Kickboxer
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,19 @@
1
+ module Kickboxer
2
+ def self.api_key=(value)
3
+ @api_key = value
4
+ end
5
+
6
+ def self.api_key
7
+ @api_key
8
+ end
9
+
10
+ def self.debug=(value)
11
+ @debug = value
12
+ end
13
+
14
+ def self.debug
15
+ @debug
16
+ end
17
+
18
+ class NoApiKeyError < RuntimeError; end
19
+ end
@@ -0,0 +1,118 @@
1
+ require 'kickboxer/request'
2
+ require 'kickboxer/response'
3
+
4
+ module Kickboxer
5
+ class Name
6
+ # Public: The Name Normalization method takes quasi structured name data
7
+ # provided as a string and outputs the data in a structured manner. It also
8
+ # returns a likelihood based only on the order of the given name and family
9
+ # name as seen in the US population.
10
+ #
11
+ # full_name - a string containing the full name. Can include standard
12
+ # prefix, first name, nickname, middle name, last name, and
13
+ # suffix.
14
+ #
15
+ # Example
16
+ #
17
+ # response = Kickboxer::Name.normalize('Mr. John (Johnny) Michael Smith')
18
+ # response.nameDetails.givenName
19
+ # # => 'John'
20
+ # response.nameDetails.familyName
21
+ # # => 'Smith'
22
+ def self.normalize(full_name)
23
+ Request.run('name/normalizer', q: full_name)
24
+ end
25
+
26
+ # Public: The Name Deducer method takes a username or email address
27
+ # provided as a string and attempts to deduce a structured name. It also
28
+ # returns a likelihood based on US population data. This method is ideal
29
+ # for business email addresses due to the use of standard convention in
30
+ # corporate email address formats.
31
+ #
32
+ # options - a hash containing the email or username to be interpreted.
33
+ # :email - the email address
34
+ # :username - the username
35
+ # either :email or :username must be populated
36
+ #
37
+ # Example
38
+ #
39
+ # response = Kickboxer::Name.deduce(username: 'johndsmith79')
40
+ # response.nameDetails.givenName
41
+ # # => 'John'
42
+ # response.nameDetails.familyName
43
+ # # => 'Smith'
44
+ def self.deduce(options={})
45
+ require_any_key options, :email, :username
46
+ Request.run('name/deducer', options)
47
+ end
48
+
49
+ # Public: The Name Similarity endpoint compares two names and returns a
50
+ # score indicating how similar they are. As the performance of different
51
+ # similarity algorithms can vary over different data sets, the endpoint
52
+ # provides 3 separate choices.
53
+ #
54
+ # first - a string containing the first name to compare
55
+ # second - a string containing the second name to compare
56
+ #
57
+ # Example
58
+ #
59
+ # response = Kickboxer::Name.similarity('john', 'johnathan')
60
+ # response.result.SimMetrics.jaroWinkler.similarity
61
+ # # => 0.8889
62
+ # response.result.SecondString.jaroWinkler.similarity
63
+ # # => 0.8889
64
+ # response.result.FullContact.BigramAnalysis.dice.similarity
65
+ # # => 0.5454545455
66
+ def self.similarity(first, second)
67
+ Request.run('name/similarity', q1: first, q2: second)
68
+ end
69
+
70
+ # Public: The Name Stats method has a variety of parameters that can be
71
+ # used to determine more about a name. See the Full Contact documentation
72
+ # page (http://www.fullcontact.com/docs/?category=name) for a list of
73
+ # available statistics.
74
+ #
75
+ # options - a hash containing the name or name parts to be examined.
76
+ # :givenName - contains a name believed to be a first name.
77
+ # :familyName - contains a name believed to be a last name.
78
+ # :name - contains a name of unknown type.
79
+ # At least one of the above must be provided. Both given and
80
+ # family name can be provided, if available.
81
+ #
82
+ # Example
83
+ #
84
+ # response = Kickboxer::Name.stats(name: 'john')
85
+ # response.name.given.likelihood # likelihood this is a given name
86
+ # # => 0.992
87
+ # response.name.family.likelihood # likelihood this is a family name
88
+ # # => 0.008
89
+ def self.stats(options={})
90
+ require_any_key options, :name, :givenName, :familyName
91
+ Request.run('name/stats', options)
92
+ end
93
+
94
+ # Public: The Name Parser method can be used when you have two names but
95
+ # don't know which one is the first name and which is the last name.
96
+ #
97
+ # full_name - a string containing the full name in unknown order
98
+ #
99
+ # Example
100
+ #
101
+ # response = Kickboxer::Name.parse('smith john')
102
+ # response.result.givenName
103
+ # # => 'John'
104
+ # response.result.familyName
105
+ # # => 'Smith'
106
+ # response.result.likelihood
107
+ # # => 1
108
+ def self.parse(full_name)
109
+ Request.run('name/parser', q: full_name)
110
+ end
111
+
112
+ private
113
+
114
+ def self.require_any_key(options, *keys)
115
+ raise "#{ keys.map{|k| ":#{ k }" }.join(' or ') } required" if (options.keys && keys).empty?
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,67 @@
1
+ module Kickboxer
2
+ class Person
3
+ # Public: look up a person by email address
4
+ #
5
+ # email_address - a string containing the email address
6
+ #
7
+ # Example
8
+ #
9
+ # response = Kickboxer::Person.find_by_email('bart@fullcontact.com')
10
+ # response.contactInfo.familyName
11
+ # # => 'Lorang'
12
+ # response.contactInfo.givenName
13
+ # # => 'Bart'
14
+ #
15
+ def self.find_by_email(email_address)
16
+ Request.run('person', email: email_address)
17
+ end
18
+
19
+ # Public: look up a person by phone number
20
+ #
21
+ # phone_number - a string containing the phone number
22
+ #
23
+ # Example
24
+ #
25
+ # response = Kickboxer::Person.find_by_phone_number('+13037170414')
26
+ # response.contactInfo.familyName
27
+ # # => 'Lorang'
28
+ # response.contactInfo.givenName
29
+ # # => 'Bart'
30
+ #
31
+ def self.find_by_phone_number(phone_number)
32
+ Request.run('person', phone: phone_number)
33
+ end
34
+
35
+ # Public: look up a person by twitter username
36
+ #
37
+ # username - a string containing the twitter username
38
+ #
39
+ # Example
40
+ #
41
+ # response = Kickboxer::Person.find_by_twitter('lorangb')
42
+ # response.contactInfo.familyName
43
+ # # => 'Lorang'
44
+ # response.contactInfo.givenName
45
+ # # => 'Bart'
46
+ #
47
+ def self.find_by_twitter(username)
48
+ Request.run('person', twitter: username)
49
+ end
50
+
51
+ # Public: look up a person by facebook username
52
+ #
53
+ # username - a string containing the facebook username
54
+ #
55
+ # Example
56
+ #
57
+ # response = Kickboxer::Person.find_by_facebook('bart.lorang')
58
+ # response.contactInfo.familyName
59
+ # # => 'Lorang'
60
+ # response.contactInfo.givenName
61
+ # # => 'Bart'
62
+ #
63
+ def self.find_by_facebook(username)
64
+ Request.run('person', facebookUsername: username)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,44 @@
1
+ require 'kickboxer/config'
2
+ require 'kickboxer/response'
3
+
4
+ require 'open-uri'
5
+ require 'json'
6
+
7
+ module Kickboxer
8
+ class Request
9
+ BASE_URL = "https://api.fullcontact.com/v2"
10
+
11
+ def initialize(action)
12
+ @action = action
13
+ end
14
+
15
+ def build_url(options={})
16
+ options[:apiKey] = Kickboxer.api_key
17
+ raise NoApiKeyError unless options[:apiKey]
18
+
19
+ querystring = options.map{|k,v| "#{ k }=#{ URI.escape v }" } * '&'
20
+ "#{ BASE_URL }/#{ @action }.json?#{ querystring }"
21
+ end
22
+
23
+ def run(options={})
24
+ debug = options.delete(:debug) || Kickboxer.debug
25
+
26
+ response = open build_url(options)
27
+ body = response.read
28
+
29
+ if debug
30
+ filename = debug.is_a?(String) ? debug : @action.gsub('/', '-')
31
+ File.open("#{ filename }.json","w"){|out| out.puts body }
32
+ end
33
+
34
+ data = JSON.parse(body)
35
+
36
+ code, text = response.status
37
+ Response.new data.update(status_code: code.to_i, status_text: text)
38
+ end
39
+
40
+ def self.run(action, options={})
41
+ new(action).run(options)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,49 @@
1
+ module Kickboxer
2
+ class Response
3
+ def initialize(data={}, &block)
4
+ @data = normalize data
5
+ @data.merge!(Collector.new.instance_eval(&block).data) if block_given?
6
+ end
7
+
8
+ def respond_to?(method_id)
9
+ @data.has_key?(method_id.to_sym) || @data.has_key?(method_id.to_s) || super
10
+ end
11
+
12
+ private
13
+
14
+ def normalize(hash)
15
+ hash.inject({}) do |h, (k,v)|
16
+ h.update(normalize_key(k) => normalize_value(v))
17
+ end
18
+ end
19
+
20
+ def normalize_key(key)
21
+ key.to_sym
22
+ end
23
+
24
+ def normalize_value(value)
25
+ value.is_a?(Hash) ? Response.new(normalize(value)) : value
26
+ end
27
+
28
+ def method_missing(key, *args)
29
+ case
30
+ when @data.has_key?(key.to_sym)
31
+ @data[key.to_sym]
32
+ when @data.has_key?(key.to_s)
33
+ @data[key.to_s]
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ class Collector
40
+ attr_reader :data
41
+
42
+ def method_missing(key, value)
43
+ @data ||= {}
44
+ @data[key] = value
45
+ self
46
+ end
47
+ end
48
+ end
49
+ end