linkedin-drspin 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.autotest +14 -0
  2. data/.document +5 -0
  3. data/.gemtest +0 -0
  4. data/.gitignore +41 -0
  5. data/.rspec +1 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +7 -0
  8. data/LICENSE +20 -0
  9. data/README.markdown +78 -0
  10. data/Rakefile +19 -0
  11. data/changelog.markdown +71 -0
  12. data/examples/authenticate.rb +21 -0
  13. data/examples/network.rb +12 -0
  14. data/examples/profile.rb +18 -0
  15. data/examples/sinatra.rb +77 -0
  16. data/examples/status.rb +6 -0
  17. data/lib/linked_in/api.rb +11 -0
  18. data/lib/linked_in/api/comment_methods.rb +33 -0
  19. data/lib/linked_in/api/company_search_methods.rb +65 -0
  20. data/lib/linked_in/api/group_methods.rb +18 -0
  21. data/lib/linked_in/api/people_search_methods.rb +112 -0
  22. data/lib/linked_in/api/post_methods.rb +33 -0
  23. data/lib/linked_in/api/query_methods.rb +81 -0
  24. data/lib/linked_in/api/update_methods.rb +55 -0
  25. data/lib/linked_in/client.rb +51 -0
  26. data/lib/linked_in/errors.rb +19 -0
  27. data/lib/linked_in/helpers.rb +6 -0
  28. data/lib/linked_in/helpers/authorization.rb +68 -0
  29. data/lib/linked_in/helpers/request.rb +80 -0
  30. data/lib/linked_in/mash.rb +68 -0
  31. data/lib/linked_in/search.rb +56 -0
  32. data/lib/linked_in/version.rb +11 -0
  33. data/lib/linkedin.rb +32 -0
  34. data/linkedin.gemspec +25 -0
  35. data/spec/cases/api_spec.rb +92 -0
  36. data/spec/cases/linkedin_spec.rb +37 -0
  37. data/spec/cases/mash_spec.rb +85 -0
  38. data/spec/cases/oauth_spec.rb +166 -0
  39. data/spec/cases/search_spec.rb +206 -0
  40. data/spec/fixtures/cassette_library/LinkedIn_Api/Company_API.yml +73 -0
  41. data/spec/fixtures/cassette_library/LinkedIn_Client/_authorize_from_request.yml +28 -0
  42. data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token.yml +28 -0
  43. data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token/with_a_callback_url.yml +28 -0
  44. data/spec/fixtures/cassette_library/LinkedIn_Client/_request_token/with_default_options.yml +28 -0
  45. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_company_name_option.yml +135 -0
  46. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_first_name_and_last_name_options.yml +122 -0
  47. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_first_name_and_last_name_options_with_fields.yml +72 -0
  48. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_keywords_string_parameter.yml +136 -0
  49. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_single_keywords_option.yml +136 -0
  50. data/spec/fixtures/cassette_library/LinkedIn_Search/_search/by_single_keywords_option_with_pagination.yml +128 -0
  51. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_keywords_options_with_fields.yml +252 -0
  52. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_keywords_string_parameter.yml +73 -0
  53. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option.yml +71 -0
  54. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option_with_a_facet.yml +111 -0
  55. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option_with_facets_to_return.yml +71 -0
  56. data/spec/fixtures/cassette_library/LinkedIn_Search/_search_company/by_single_keywords_option_with_pagination.yml +66 -0
  57. data/spec/helper.rb +30 -0
  58. 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
@@ -0,0 +1,6 @@
1
+ module LinkedIn
2
+ module Helpers
3
+ autoload :Authorization, "linked_in/helpers/authorization"
4
+ autoload :Request, "linked_in/helpers/request"
5
+ end
6
+ end