linkedin-drspin 0.3.6
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/.autotest +14 -0
- data/.document +5 -0
- data/.gemtest +0 -0
- data/.gitignore +41 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.markdown +78 -0
- data/Rakefile +19 -0
- data/changelog.markdown +71 -0
- data/examples/authenticate.rb +21 -0
- data/examples/network.rb +12 -0
- data/examples/profile.rb +18 -0
- data/examples/sinatra.rb +77 -0
- data/examples/status.rb +6 -0
- data/lib/linked_in/api.rb +11 -0
- data/lib/linked_in/api/comment_methods.rb +33 -0
- data/lib/linked_in/api/company_search_methods.rb +65 -0
- data/lib/linked_in/api/group_methods.rb +18 -0
- data/lib/linked_in/api/people_search_methods.rb +112 -0
- data/lib/linked_in/api/post_methods.rb +33 -0
- data/lib/linked_in/api/query_methods.rb +81 -0
- data/lib/linked_in/api/update_methods.rb +55 -0
- data/lib/linked_in/client.rb +51 -0
- data/lib/linked_in/errors.rb +19 -0
- data/lib/linked_in/helpers.rb +6 -0
- data/lib/linked_in/helpers/authorization.rb +68 -0
- data/lib/linked_in/helpers/request.rb +80 -0
- data/lib/linked_in/mash.rb +68 -0
- data/lib/linked_in/search.rb +56 -0
- data/lib/linked_in/version.rb +11 -0
- data/lib/linkedin.rb +32 -0
- data/linkedin.gemspec +25 -0
- data/spec/cases/api_spec.rb +92 -0
- data/spec/cases/linkedin_spec.rb +37 -0
- data/spec/cases/mash_spec.rb +85 -0
- data/spec/cases/oauth_spec.rb +166 -0
- data/spec/cases/search_spec.rb +206 -0
- data/spec/fixtures/cassette_library/LinkedIn_Api/Company_API.yml +73 -0
- data/spec/fixtures/cassette_library/LinkedIn_Client/_authorize_from_request.yml +28 -0
- data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token.yml +28 -0
- data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token/with_a_callback_url.yml +28 -0
- data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token/with_default_options.yml +28 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_company_name_option.yml +135 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_first_name_and_last_name_options.yml +122 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_first_name_and_last_name_options_with_fields.yml +72 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_keywords_string_parameter.yml +136 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_single_keywords_option.yml +136 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_single_keywords_option_with_pagination.yml +128 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_keywords_options_with_fields.yml +252 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_keywords_string_parameter.yml +73 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option.yml +71 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option_with_a_facet.yml +111 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option_with_facets_to_return.yml +71 -0
- data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option_with_pagination.yml +66 -0
- data/spec/helper.rb +30 -0
- metadata +237 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module Api
|
3
|
+
module CompanySearchMethods
|
4
|
+
|
5
|
+
# Public: Search the LinkedIn service for companies
|
6
|
+
#
|
7
|
+
# options - The Hash options used to refine the selection:
|
8
|
+
# :sort - Controls the search result order.
|
9
|
+
# :start - The people record to start the return from.
|
10
|
+
# Used for page wrapping when the total number
|
11
|
+
# of records matching the search exceeds count
|
12
|
+
# (default: 0).
|
13
|
+
# :count - The number of companiy records to return
|
14
|
+
# (default: 25).
|
15
|
+
# :companies - The company fields to be returned
|
16
|
+
# (default: id,name,website-url)
|
17
|
+
# :keywords - The keywords to search for (optional).
|
18
|
+
#
|
19
|
+
# Examples
|
20
|
+
#
|
21
|
+
# > y linkedin.company_search(:keywords => "social,local",
|
22
|
+
# :count => 2)
|
23
|
+
# ---
|
24
|
+
# companies:
|
25
|
+
# _count: 2
|
26
|
+
# _start: 0
|
27
|
+
# total: 8620
|
28
|
+
# all:
|
29
|
+
# - id: 1355
|
30
|
+
# name: DHL
|
31
|
+
# website_url: http://www.dhl.com
|
32
|
+
# - id: 15656
|
33
|
+
# name: AECOM
|
34
|
+
# website_url: www.aecom.com
|
35
|
+
# => nil
|
36
|
+
#
|
37
|
+
# Returns a Hashie::Mash of the matched companies.
|
38
|
+
def company_search(options={})
|
39
|
+
options.reverse_merge!({
|
40
|
+
:sort => 'relevance',
|
41
|
+
:start => 0,
|
42
|
+
:count => 25,
|
43
|
+
:companies => 'id,name,website-url'
|
44
|
+
})
|
45
|
+
|
46
|
+
path = "/company-search:(companies:(#{options[:companies]}))" +
|
47
|
+
"?sort=#{options[:sort]}" +
|
48
|
+
"&start=#{options[:start]}" +
|
49
|
+
"&count=#{options[:count]}"
|
50
|
+
|
51
|
+
path += "&keywords=#{CGI.escape(options[:keywords])}" if
|
52
|
+
options[:keywords]
|
53
|
+
|
54
|
+
Mash.from_json(get(path))
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def location(options={})
|
59
|
+
path = "/people-search:(facets:(code,buckets:(code,name)))?company-name=microsoft&facets=location"
|
60
|
+
simple_query(path)
|
61
|
+
end
|
62
|
+
|
63
|
+
end # CompanySearchMethods
|
64
|
+
end # Api
|
65
|
+
end # LinkedIn
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module Api
|
3
|
+
module GroupMethods
|
4
|
+
|
5
|
+
def groups(options={})
|
6
|
+
path = "#{person_path(options)}/group-memberships?membership-state=member"
|
7
|
+
group_mash = simple_query(path, options)
|
8
|
+
group_mash.all
|
9
|
+
end
|
10
|
+
|
11
|
+
def suggested_groups(options={})
|
12
|
+
path = "#{person_path(options)}/suggestions/groups"
|
13
|
+
simple_query(path, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
end # GroupMethods
|
17
|
+
end # Api
|
18
|
+
end # LinkedIn
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module Api
|
3
|
+
module PeopleSearchMethods
|
4
|
+
|
5
|
+
# Public: Search the LinkedIn service for people
|
6
|
+
#
|
7
|
+
# options - The Hash options used to refine the selection:
|
8
|
+
# :people - The people fields to be returned
|
9
|
+
# (default: public-profile-url,id,first-name,
|
10
|
+
# last-name,headline,location).
|
11
|
+
# :count - The number of people records to return
|
12
|
+
# (default: 25).
|
13
|
+
# :start - The people record to start the return from.
|
14
|
+
# Used for page wrapping when the total number
|
15
|
+
# of records matching the search exceeds count
|
16
|
+
# (default: 0).
|
17
|
+
# :companies - A comma separated list of company codes to
|
18
|
+
# to match against the current-company field
|
19
|
+
# of people (optional).
|
20
|
+
# :locations - A comma separated list of location codes to
|
21
|
+
# match agains the location field of people
|
22
|
+
# (optional).
|
23
|
+
# :get_all - Boolean variable telling us to get all of
|
24
|
+
# the people that match our search. LinkedIn
|
25
|
+
# will only return the first 25 by default
|
26
|
+
# (optional).
|
27
|
+
#
|
28
|
+
# Examples
|
29
|
+
#
|
30
|
+
# > y client.people_search(:locations => 'us:91',
|
31
|
+
# :company => 1035,
|
32
|
+
# :count => 1)
|
33
|
+
# ---
|
34
|
+
# people:
|
35
|
+
# _count: 1
|
36
|
+
# _start: 0
|
37
|
+
# total: 110
|
38
|
+
# all:
|
39
|
+
# - first_name: Ben
|
40
|
+
# headline: Designer at SkB Architects
|
41
|
+
# id: EHp0p-X67f
|
42
|
+
# last_name: Humphrey
|
43
|
+
# location:
|
44
|
+
# country:
|
45
|
+
# code: us
|
46
|
+
# name: Greater Seattle Area
|
47
|
+
# public_profile_url: http://www.linkedin.com/in/benhumphrey
|
48
|
+
# => nil
|
49
|
+
#
|
50
|
+
# Returns a Hashie::Mash of the matched people.
|
51
|
+
def people_search(options={})
|
52
|
+
|
53
|
+
# revere merge defaults into options has so that we don't
|
54
|
+
# overwrite what the user has given us.
|
55
|
+
options.reverse_merge!({
|
56
|
+
:people => "public-profile-url,id,first-name,last-name" +
|
57
|
+
",headline,location",
|
58
|
+
:count => 25,
|
59
|
+
:start => 0
|
60
|
+
})
|
61
|
+
|
62
|
+
# initialize path string for a people-search
|
63
|
+
path = "/people-search:(people:(#{options[:people]}))" +
|
64
|
+
"?count=#{options[:count]}" +
|
65
|
+
"&start=#{options[:start]}"
|
66
|
+
|
67
|
+
# add a facet so that we find everyone in the given companys
|
68
|
+
# if the :company option is defined.
|
69
|
+
path += "&facet=current-company,#{options[:companies]}" if
|
70
|
+
options[:companies]
|
71
|
+
|
72
|
+
# add a facet so that we find everyone in the given locations
|
73
|
+
# if the :location option is defined.
|
74
|
+
path += "&facet=location,#{CGI.escape(options[:locations])}" if
|
75
|
+
options[:locations]
|
76
|
+
|
77
|
+
# hit the linkedin service with the search. we store the payload
|
78
|
+
# as an array so that we can manipulate it below.
|
79
|
+
payload = MultiJson.decode(get(path))
|
80
|
+
Rails.logger.debug path
|
81
|
+
|
82
|
+
# if the :get_all option is set, we check to make sure that
|
83
|
+
# the count of items returned from the linkedin service is
|
84
|
+
# not less than the total number of items that match our
|
85
|
+
# search in the linkedin service.
|
86
|
+
while ((payload['people']['_start'] +
|
87
|
+
payload['people']['_count']) <
|
88
|
+
payload['people']['_total'])
|
89
|
+
|
90
|
+
# update the path string so that we start from where we
|
91
|
+
# left off from the last search.
|
92
|
+
path.sub!(/start=\d+/,
|
93
|
+
"start=#{payload['people']['_start'] +
|
94
|
+
payload['people']['_count']}")
|
95
|
+
Rails.logger.debug path
|
96
|
+
|
97
|
+
# update the count in our payload
|
98
|
+
payload['people']['_count'] += options[:count]
|
99
|
+
|
100
|
+
# hit the linkedin service again and merge teh new
|
101
|
+
# results into our payload.
|
102
|
+
payload['people']['values'] +=
|
103
|
+
MultiJson.decode(get(path))['people']['values']
|
104
|
+
|
105
|
+
end if options[:get_all]
|
106
|
+
|
107
|
+
Mash.from_json(MultiJson.encode(payload))
|
108
|
+
|
109
|
+
end # people_search()
|
110
|
+
end # PeopleSearchMethods
|
111
|
+
end # Api
|
112
|
+
end # LinkedIn
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module Api
|
3
|
+
module PostMethods
|
4
|
+
#
|
5
|
+
# group_posts(options={})
|
6
|
+
#
|
7
|
+
# inputs:
|
8
|
+
# REQUIRED
|
9
|
+
# :group_id - the numeric id of the group to pull posts from.
|
10
|
+
# OPTIONAL
|
11
|
+
# :count - number of posts to retun (max 50)
|
12
|
+
# :start - post number to start pulling from
|
13
|
+
#
|
14
|
+
# outputs:
|
15
|
+
# Mash of json results from LinkedIn service
|
16
|
+
#
|
17
|
+
# description: Retreive posts from a given group and filter
|
18
|
+
# based on optional inputs.
|
19
|
+
#
|
20
|
+
def group_posts(options={})
|
21
|
+
options.reverse_merge!({:order => "recency"})
|
22
|
+
if options[:group_id]
|
23
|
+
group_id = options.delete(:group_id)
|
24
|
+
else
|
25
|
+
raise ":group_id option required for group_posts method"
|
26
|
+
end
|
27
|
+
path = "/groups/#{group_id}/posts:(title,summary,creator,id)"
|
28
|
+
simple_query(path, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
end # PostMethods
|
32
|
+
end # Api
|
33
|
+
end # LinkedIn
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module Api
|
3
|
+
|
4
|
+
module QueryMethods
|
5
|
+
|
6
|
+
def profile(options={})
|
7
|
+
path = person_path(options)
|
8
|
+
simple_query(path, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def connections(options={})
|
12
|
+
path = "#{person_path(options)}/connections"
|
13
|
+
simple_query(path, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def network_updates(options={})
|
17
|
+
path = "#{person_path(options)}/network/updates"
|
18
|
+
simple_query(path, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def company(options = {})
|
22
|
+
path = company_path(options)
|
23
|
+
simple_query(path, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def simple_query(path, options={})
|
30
|
+
fields = options.delete(:fields) || LinkedIn.default_profile_fields
|
31
|
+
|
32
|
+
if options.delete(:public)
|
33
|
+
path +=":public"
|
34
|
+
elsif fields
|
35
|
+
path +=":(#{fields.map{ |f| f.to_s.gsub("_","-") }.join(',')})"
|
36
|
+
end
|
37
|
+
|
38
|
+
headers = options.delete(:headers) || {}
|
39
|
+
params = options.map { |k,v| "#{k}=#{v}" }.join("&")
|
40
|
+
path += "?#{params}" if not params.empty?
|
41
|
+
|
42
|
+
Mash.from_json(get(path, headers))
|
43
|
+
end
|
44
|
+
|
45
|
+
def person_path(options)
|
46
|
+
path = "/people/"
|
47
|
+
if id = options.delete(:id)
|
48
|
+
path += "id=#{id}"
|
49
|
+
elsif url = options.delete(:url)
|
50
|
+
path += "url=#{CGI.escape(url)}"
|
51
|
+
else
|
52
|
+
path += "~"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def company_path(options)
|
57
|
+
# We leave out the trailing slash because it is used by keys but not
|
58
|
+
# by filters
|
59
|
+
path = "/companies"
|
60
|
+
|
61
|
+
# Add a trailing slash for keys (i.e. input values that return one
|
62
|
+
# company)
|
63
|
+
if id = options.delete(:id)
|
64
|
+
path += "/id=#{id}"
|
65
|
+
elsif url = options.delete(:url)
|
66
|
+
path += "/url=#{CGI.escape(url)}"
|
67
|
+
elsif name = options.delete(:name)
|
68
|
+
path += "/universal-name=#{CGI.escape(name)}"
|
69
|
+
# Add a question mark for filters (i.e. input values that return
|
70
|
+
# an array of companies.
|
71
|
+
elsif domain = options.delete(:domain)
|
72
|
+
path += "?email-domain=#{CGI.escape(domain)}"
|
73
|
+
else
|
74
|
+
path += "~"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module Api
|
3
|
+
|
4
|
+
module UpdateMethods
|
5
|
+
|
6
|
+
def add_share(share)
|
7
|
+
path = "/people/~/shares"
|
8
|
+
defaults = {:visibility => {:code => "anyone"}}
|
9
|
+
post(path, defaults.merge(share).to_json, "Content-Type" => "application/json")
|
10
|
+
end
|
11
|
+
|
12
|
+
# def share(options={})
|
13
|
+
# path = "/people/~/shares"
|
14
|
+
# defaults = { :visability => 'anyone' }
|
15
|
+
# post(path, share_to_xml(defaults.merge(options)))
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# def update_comment(network_key, comment)
|
19
|
+
# path = "/people/~/network/updates/key=#{network_key}/update-comments"
|
20
|
+
# post(path, comment_to_xml(comment))
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def update_network(message)
|
24
|
+
# path = "/people/~/person-activities"
|
25
|
+
# post(path, network_update_to_xml(message))
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# def send_message(subject, body, recipient_paths)
|
29
|
+
# path = "/people/~/mailbox"
|
30
|
+
#
|
31
|
+
# message = LinkedIn::Message.new
|
32
|
+
# message.subject = subject
|
33
|
+
# message.body = body
|
34
|
+
# recipients = LinkedIn::Recipients.new
|
35
|
+
#
|
36
|
+
# recipients.recipients = recipient_paths.map do |profile_path|
|
37
|
+
# recipient = LinkedIn::Recipient.new
|
38
|
+
# recipient.person = LinkedIn::Person.new
|
39
|
+
# recipient.person.path = "/people/#{profile_path}"
|
40
|
+
# recipient
|
41
|
+
# end
|
42
|
+
# message.recipients = recipients
|
43
|
+
# post(path, message_to_xml(message)).code
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# def clear_status
|
47
|
+
# path = "/people/~/current-status"
|
48
|
+
# delete(path).code
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module LinkedIn
|
4
|
+
|
5
|
+
class Client
|
6
|
+
include Helpers::Request
|
7
|
+
include Helpers::Authorization
|
8
|
+
include Api::QueryMethods
|
9
|
+
include Api::UpdateMethods
|
10
|
+
include Api::GroupMethods
|
11
|
+
include Api::PostMethods
|
12
|
+
include Api::CompanySearchMethods
|
13
|
+
include Api::PeopleSearchMethods
|
14
|
+
include Api::CommentMethods
|
15
|
+
include Search
|
16
|
+
|
17
|
+
attr_reader :consumer_token, :consumer_secret, :consumer_options
|
18
|
+
|
19
|
+
def initialize(ctoken=LinkedIn.token, csecret=LinkedIn.secret, options={})
|
20
|
+
@consumer_token = ctoken
|
21
|
+
@consumer_secret = csecret
|
22
|
+
@consumer_options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# def current_status
|
27
|
+
# path = "/people/~/current-status"
|
28
|
+
# Crack::XML.parse(get(path))['current_status']
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def network_statuses(options={})
|
32
|
+
# options[:type] = 'STAT'
|
33
|
+
# network_updates(options)
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# def network_updates(options={})
|
37
|
+
# path = "/people/~/network"
|
38
|
+
# Network.from_xml(get(to_uri(path, options)))
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# # helpful in making authenticated calls and writing the
|
42
|
+
# # raw xml to a fixture file
|
43
|
+
# def write_fixture(path, filename)
|
44
|
+
# file = File.new("test/fixtures/#{filename}", "w")
|
45
|
+
# file.puts(access_token.get(path).body)
|
46
|
+
# file.close
|
47
|
+
# end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module Errors
|
3
|
+
class LinkedInError < StandardError
|
4
|
+
attr_reader :data
|
5
|
+
def initialize(data)
|
6
|
+
@data = data
|
7
|
+
super
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class UnauthorizedError < LinkedInError; end
|
12
|
+
class GeneralError < LinkedInError; end
|
13
|
+
class AccessDeniedError < LinkedInError; end
|
14
|
+
|
15
|
+
class UnavailableError < StandardError; end
|
16
|
+
class InformLinkedInError < StandardError; end
|
17
|
+
class NotFoundError < StandardError; end
|
18
|
+
end
|
19
|
+
end
|