linkedin 0.1.7 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +14 -0
- data/.document +5 -0
- data/.gitignore +25 -0
- data/Gemfile +3 -0
- data/README.markdown +12 -7
- data/Rakefile +10 -33
- data/VERSION +1 -1
- data/changelog.markdown +38 -0
- data/examples/authenticate.rb +1 -1
- data/lib/linked_in/api_standard_profile_request.rb +14 -6
- data/lib/linked_in/authorization_helpers.rb +48 -0
- data/lib/linked_in/base.rb +13 -0
- data/lib/linked_in/birthdate.rb +21 -0
- data/lib/linked_in/client.rb +86 -146
- data/lib/linked_in/company.rb +9 -7
- data/lib/linked_in/connections.rb +14 -5
- data/lib/linked_in/country.rb +7 -5
- data/lib/linked_in/current_share.rb +56 -0
- data/lib/linked_in/education.rb +40 -14
- data/lib/linked_in/error.rb +19 -8
- data/lib/linked_in/group.rb +30 -7
- data/lib/linked_in/languages.rb +28 -0
- data/lib/linked_in/likes.rb +23 -0
- data/lib/linked_in/location.rb +11 -6
- data/lib/linked_in/message.rb +20 -0
- data/lib/linked_in/network.rb +10 -5
- data/lib/linked_in/patents.rb +42 -0
- data/lib/linked_in/people.rb +16 -8
- data/lib/linked_in/person.rb +7 -0
- data/lib/linked_in/phone_number.rb +29 -0
- data/lib/linked_in/position.rb +45 -14
- data/lib/linked_in/profile.rb +81 -28
- data/lib/linked_in/publications.rb +40 -0
- data/lib/linked_in/recipient.rb +7 -0
- data/lib/linked_in/recipients.rb +18 -0
- data/lib/linked_in/recommendations.rb +30 -0
- data/lib/linked_in/request_helpers.rb +76 -0
- data/lib/linked_in/short_profile.rb +13 -0
- data/lib/linked_in/skill.rb +33 -0
- data/lib/linked_in/to_xml_helpers.rb +53 -0
- data/lib/linked_in/update.rb +21 -9
- data/lib/linked_in/url_resource.rb +24 -6
- data/lib/linkedin.rb +58 -59
- data/linkedin.gemspec +52 -0
- data/spec/cases/client_spec.rb +281 -0
- data/spec/cases/linkedin_spec.rb +37 -0
- data/spec/cases/oauth_spec.rb +109 -0
- data/{test → spec}/fixtures/blank.xml +0 -0
- data/{test → spec}/fixtures/connections.xml +0 -0
- data/{test → spec}/fixtures/error.xml +0 -0
- data/spec/fixtures/likes.xml +18 -0
- data/spec/fixtures/mailbox_items.xml +16 -0
- data/{test → spec}/fixtures/network_status_with_group.xml +3 -3
- data/{test → spec}/fixtures/network_statuses.xml +18 -0
- data/{test → spec}/fixtures/picture_updates.xml +0 -0
- data/{test → spec}/fixtures/profile.xml +0 -0
- data/{test → spec}/fixtures/profile_full.xml +57 -0
- data/{test → spec}/fixtures/profile_with_positions.xml +0 -0
- data/{test → spec}/fixtures/search.xml +2 -2
- data/spec/fixtures/shares.xml +12 -0
- data/{test → spec}/fixtures/status.xml +0 -0
- data/spec/spec_helper.rb +49 -0
- metadata +160 -76
- data/test/client_test.rb +0 -124
- data/test/oauth_test.rb +0 -117
- data/test/test_helper.rb +0 -51
data/.autotest
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Autotest.add_hook(:initialize) do |at|
|
2
|
+
at.add_exception(".git")
|
3
|
+
end
|
4
|
+
|
5
|
+
Autotest.add_hook(:initialize) do |at|
|
6
|
+
at.clear_mappings
|
7
|
+
|
8
|
+
at.add_mapping %r%/^lib/(.*)\.rb$% do |_, m|
|
9
|
+
possible = File.basename(m[1])
|
10
|
+
files_matching %r%^test/.*(#{possible}_test|test_#{possible})\.rb$%
|
11
|
+
end
|
12
|
+
|
13
|
+
at.add_mapping(%r%^test/.*\.rb$%) {|filename, _| filename }
|
14
|
+
end
|
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
rdoc
|
19
|
+
pkg
|
20
|
+
|
21
|
+
## PROJECT::SPECIFIC
|
22
|
+
examples/wynn.rb
|
23
|
+
|
24
|
+
## Bundler
|
25
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/README.markdown
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
Ruby wrapper for the [LinkedIn API](http://developer.linkedin.com). Heavily inspired by [John Nunemaker's](http://github.com/jnunemaker) [Twitter gem](http://github.com/jnunemaker/twitter), the LinkedIn gem provides an easy-to-use wrapper for LinkedIn's Oauth/XML APIs.
|
4
4
|
|
5
|
+
Travis CI : [![Build Status](http://travis-ci.org/pengwynn/linkedin.png)](http://travis-ci.org/pengwynn/linkedin)
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
|
-
sudo gem install
|
8
|
-
|
9
|
-
sudo gem install linkedin
|
10
|
-
|
9
|
+
[sudo] gem install linkedin
|
10
|
+
|
11
11
|
## Usage
|
12
12
|
|
13
13
|
### Authenticate
|
@@ -46,19 +46,24 @@ LinkedIn's API uses Oauth for authentication. Luckily, the LinkedIn gem hides mo
|
|
46
46
|
|
47
47
|
# get a profile for someone via their public profile url
|
48
48
|
client.profile(:url => 'http://www.linkedin.com/in/netherland')
|
49
|
-
|
49
|
+
|
50
50
|
|
51
51
|
|
52
52
|
More examples in the [examples folder](http://github.com/pengwynn/linkedin/blob/master/examples).
|
53
53
|
|
54
|
+
For a nice example on using this in a [Rails App](http://pivotallabs.com/users/will/blog/articles/1096-linkedin-gem-for-a-web-app).
|
54
55
|
|
56
|
+
If you want to play with the LinkedIn api without using the gem, have a look at the [apigee LinkedIn console](http://app.apigee.com/console/linkedin).
|
55
57
|
|
56
58
|
## TODO
|
57
59
|
|
60
|
+
* Change to json api
|
61
|
+
* Update and correct test suite
|
62
|
+
* Change to Faraday for authentication
|
58
63
|
* Implement Messaging APIs
|
59
64
|
|
60
65
|
## Note on Patches/Pull Requests
|
61
|
-
|
66
|
+
|
62
67
|
* Fork the project.
|
63
68
|
* Make your feature addition or bug fix.
|
64
69
|
* Add tests for it. This is important so I don't break it in a
|
@@ -70,4 +75,4 @@ More examples in the [examples folder](http://github.com/pengwynn/linkedin/blob/
|
|
70
75
|
|
71
76
|
## Copyright
|
72
77
|
|
73
|
-
Copyright (c) 2009 [Wynn Netherland](http://wynnnetherland.com). See LICENSE for details.
|
78
|
+
Copyright (c) 2009-11 [Wynn Netherland](http://wynnnetherland.com). See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -1,41 +1,21 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'rake'
|
3
2
|
|
4
3
|
begin
|
5
|
-
require '
|
6
|
-
|
7
|
-
gem.name = "linkedin"
|
8
|
-
gem.summary = %Q{Ruby wrapper for the LinkedIn API}
|
9
|
-
gem.description = %Q{Ruby wrapper for the LinkedIn API}
|
10
|
-
gem.email = "wynn.netherland@gmail.com"
|
11
|
-
gem.homepage = "http://github.com/pengwynn/linkedin"
|
12
|
-
gem.authors = ["Wynn Netherland"]
|
13
|
-
gem.files = FileList["[A-Z]*", "{lib,test}/**/*"]
|
14
|
-
|
15
|
-
|
16
|
-
gem.add_dependency('oauth', '~> 0.3.5')
|
17
|
-
gem.add_dependency('roxml', '~> 3.1.3')
|
18
|
-
gem.add_dependency('crack', '~> 0.1.4')
|
19
|
-
|
20
|
-
gem.add_development_dependency('thoughtbot-shoulda', '>= 2.10.1')
|
21
|
-
gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
|
22
|
-
gem.add_development_dependency('mocha', '0.9.4')
|
23
|
-
gem.add_development_dependency('fakeweb', '>= 1.2.5')
|
24
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
25
|
-
end
|
26
|
-
Jeweler::GemcutterTasks.new
|
4
|
+
require 'bundler/setup'
|
5
|
+
Bundler::GemHelper.install_tasks
|
27
6
|
rescue LoadError
|
28
|
-
puts
|
7
|
+
puts 'although not required, bundler is recommened for running the tests'
|
29
8
|
end
|
30
9
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
10
|
+
|
11
|
+
task :default => :spec
|
12
|
+
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
RSpec::Core::RakeTask.new do |t|
|
15
|
+
t.rspec_opts = ["--color", '--format doc']
|
37
16
|
end
|
38
17
|
|
18
|
+
|
39
19
|
begin
|
40
20
|
require 'rcov/rcovtask'
|
41
21
|
Rcov::RcovTask.new do |test|
|
@@ -49,9 +29,6 @@ rescue LoadError
|
|
49
29
|
end
|
50
30
|
end
|
51
31
|
|
52
|
-
task :test => :check_dependencies
|
53
|
-
|
54
|
-
task :default => :test
|
55
32
|
|
56
33
|
require 'rake/rdoctask'
|
57
34
|
Rake::RDocTask.new do |rdoc|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1
|
1
|
+
0.2.1
|
data/changelog.markdown
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 0.1.7 - February 5, 2010
|
4
|
+
|
5
|
+
* New group join status support JGRP from Terry Ray
|
6
|
+
|
7
|
+
## 0.1.6 - January 20, 2010
|
8
|
+
|
9
|
+
* Fixed bug with network status update connection collections - thanks Terry Ray
|
10
|
+
|
11
|
+
## 0.1.5 - January 13, 2010
|
12
|
+
* Added education and profile fields missing from updated LinkedIn docs
|
13
|
+
## 0.1.4 - January 13, 2010
|
14
|
+
|
15
|
+
* Applied patch for position end month/year from @holman
|
16
|
+
|
17
|
+
## 0.1.3 - December 24, 2009
|
18
|
+
|
19
|
+
* Added configure block for easier initialization of consumer token, secret
|
20
|
+
|
21
|
+
## 0.1.1 - December 8, 2009
|
22
|
+
|
23
|
+
* Applied patch from [nfo](http://github.com/nfo) to fix error handling
|
24
|
+
|
25
|
+
### 0.1.0 - November 25, 2009
|
26
|
+
|
27
|
+
* Network updates API support
|
28
|
+
* Search API support
|
29
|
+
* Updates API support
|
30
|
+
|
31
|
+
### 0.0.2 - November 25, 2009
|
32
|
+
|
33
|
+
* Swapped out Crack for ROXML for prettier object access
|
34
|
+
* Added more tests for Profile API
|
35
|
+
|
36
|
+
### 0.0.1 - November 24, 2009
|
37
|
+
|
38
|
+
* Initial release
|
data/examples/authenticate.rb
CHANGED
@@ -18,4 +18,4 @@ client.authorize_from_request(rtoken, rsecret, pin)
|
|
18
18
|
# or authorize from previously fetched access keys
|
19
19
|
c.authorize_from_access("OU812", "8675309")
|
20
20
|
|
21
|
-
# you're now free to move about the cabin, call any API method
|
21
|
+
# you're now free to move about the cabin, call any API method
|
@@ -1,9 +1,17 @@
|
|
1
1
|
module LinkedIn
|
2
|
-
class ApiStandardProfileRequest
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
class ApiStandardProfileRequest < LinkedIn::Base
|
3
|
+
|
4
|
+
def url
|
5
|
+
@doc.xpath("//api-standard-profile-request/url").text
|
6
|
+
end
|
7
|
+
|
8
|
+
# returning a hash should be ok, but suggestions are welcome
|
9
|
+
def headers
|
10
|
+
hash = {}
|
11
|
+
hash[:name] = @doc.xpath("//api-standard-profile-request/headers/http-header/name").text
|
12
|
+
hash[:value] = @doc.xpath("//api-standard-profile-request/headers/http-header/value").text
|
13
|
+
hash
|
14
|
+
end
|
15
|
+
|
8
16
|
end
|
9
17
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
|
3
|
+
module AuthorizationHelpers
|
4
|
+
|
5
|
+
def consumer
|
6
|
+
@consumer ||= begin
|
7
|
+
options = { :site => 'https://api.linkedin.com' }.merge(@consumer_options)
|
8
|
+
::OAuth::Consumer.new(@ctoken, @csecret, options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_callback_url(url)
|
13
|
+
clear_request_token
|
14
|
+
request_token(:oauth_callback => url)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Note: If using oauth with a web app, be sure to provide :oauth_callback.
|
18
|
+
# Options:
|
19
|
+
# :oauth_callback => String, url that LinkedIn should redirect to
|
20
|
+
def request_token(options={})
|
21
|
+
@request_token ||= consumer.get_request_token(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
# For web apps use params[:oauth_verifier], for desktop apps,
|
25
|
+
# use the verifier is the pin that LinkedIn gives users.
|
26
|
+
def authorize_from_request(rtoken, rsecret, verifier_or_pin)
|
27
|
+
request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
|
28
|
+
access_token = request_token.get_access_token(:oauth_verifier => verifier_or_pin)
|
29
|
+
@atoken, @asecret = access_token.token, access_token.secret
|
30
|
+
end
|
31
|
+
|
32
|
+
def access_token
|
33
|
+
@access_token ||= ::OAuth::AccessToken.new(consumer, @atoken, @asecret)
|
34
|
+
end
|
35
|
+
|
36
|
+
def authorize_from_access(atoken, asecret)
|
37
|
+
@atoken, @asecret = atoken, asecret
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def clear_request_token
|
43
|
+
@request_token = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
class Birthdate < LinkedIn::Base
|
3
|
+
|
4
|
+
def year
|
5
|
+
@year ||= @doc.xpath("/person/date-of-birth/year").text.to_i
|
6
|
+
end
|
7
|
+
|
8
|
+
def day
|
9
|
+
@day ||= @doc.xpath("/person/date-of-birth/day").text.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def month
|
13
|
+
@month ||= @doc.xpath("/person/date-of-birth/month").text.to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_date
|
17
|
+
Date.civil(y=year,m=month,d=day)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/linked_in/client.rb
CHANGED
@@ -1,171 +1,135 @@
|
|
1
1
|
module LinkedIn
|
2
2
|
class Client
|
3
|
-
|
3
|
+
include ToXmlHelpers
|
4
|
+
include RequestHelpers
|
5
|
+
include AuthorizationHelpers
|
6
|
+
|
4
7
|
attr_reader :ctoken, :csecret, :consumer_options
|
5
|
-
|
8
|
+
|
6
9
|
def initialize(ctoken=LinkedIn.token, csecret=LinkedIn.secret, options={})
|
7
|
-
opts = {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
opts = {
|
11
|
+
:request_token_path => "/uas/oauth/requestToken",
|
12
|
+
:access_token_path => "/uas/oauth/accessToken",
|
13
|
+
:authorize_path => "/uas/oauth/authorize"
|
14
|
+
}
|
12
15
|
@ctoken, @csecret, @consumer_options = ctoken, csecret, opts.merge(options)
|
13
16
|
end
|
14
|
-
|
15
|
-
|
16
|
-
@consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => 'https://api.linkedin.com'}.merge(consumer_options))
|
17
|
-
end
|
18
|
-
|
19
|
-
def set_callback_url(url)
|
20
|
-
clear_request_token
|
21
|
-
request_token(:oauth_callback => url)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Note: If using oauth with a web app, be sure to provide :oauth_callback.
|
25
|
-
# Options:
|
26
|
-
# :oauth_callback => String, url that LinkedIn should redirect to
|
27
|
-
def request_token(options={})
|
28
|
-
@request_token ||= consumer.get_request_token(options)
|
29
|
-
end
|
30
|
-
|
31
|
-
# For web apps use params[:oauth_verifier], for desktop apps,
|
32
|
-
# use the verifier is the pin that LinkedIn gives users.
|
33
|
-
def authorize_from_request(rtoken, rsecret, verifier_or_pin)
|
34
|
-
request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
|
35
|
-
access_token = request_token.get_access_token(:oauth_verifier => verifier_or_pin)
|
36
|
-
@atoken, @asecret = access_token.token, access_token.secret
|
37
|
-
end
|
38
|
-
|
39
|
-
def access_token
|
40
|
-
@access_token ||= ::OAuth::AccessToken.new(consumer, @atoken, @asecret)
|
41
|
-
end
|
42
|
-
|
43
|
-
def authorize_from_access(atoken, asecret)
|
44
|
-
@atoken, @asecret = atoken, asecret
|
45
|
-
end
|
46
|
-
|
47
|
-
def get(path, options={})
|
48
|
-
path = "/v1#{path}"
|
49
|
-
response = access_token.get(path, options)
|
50
|
-
raise_errors(response)
|
51
|
-
response.body
|
52
|
-
end
|
53
|
-
|
54
|
-
def put(path, options={})
|
55
|
-
path = "/v1#{path}"
|
56
|
-
response = access_token.put(path, options)
|
57
|
-
raise_errors(response)
|
58
|
-
response
|
59
|
-
end
|
60
|
-
|
61
|
-
def delete(path, options={})
|
62
|
-
path = "/v1#{path}"
|
63
|
-
response = access_token.delete(path, options)
|
64
|
-
raise_errors(response)
|
65
|
-
response
|
66
|
-
end
|
67
|
-
|
68
|
-
|
17
|
+
|
18
|
+
|
69
19
|
def profile(options={})
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
path +=":(#{options[:fields].map{|f| f.to_s.gsub("_","-")}.join(',')})"
|
78
|
-
end
|
20
|
+
path = person_path(options)
|
21
|
+
fields = options[:fields] || LinkedIn.default_profile_fields
|
22
|
+
|
23
|
+
if options[:public]
|
24
|
+
path +=":public"
|
25
|
+
elsif fields
|
26
|
+
path +=":(#{fields.map{ |f| f.to_s.gsub("_","-") }.join(',')})"
|
79
27
|
end
|
80
|
-
|
28
|
+
|
81
29
|
Profile.from_xml(get(path))
|
82
30
|
end
|
83
|
-
|
31
|
+
|
84
32
|
def connections(options={})
|
85
33
|
path = "#{person_path(options)}/connections"
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
34
|
+
fields = options[:fields] || LinkedIn.default_profile_fields
|
35
|
+
|
36
|
+
if options[:public]
|
37
|
+
path +=":public"
|
38
|
+
elsif fields
|
39
|
+
path +=":(#{fields.map{ |f| f.to_s.gsub("_","-") }.join(',')})"
|
93
40
|
end
|
94
|
-
|
41
|
+
|
95
42
|
Connections.from_xml(get(path)).profiles
|
96
43
|
end
|
97
|
-
|
44
|
+
|
98
45
|
def search(options={})
|
99
46
|
path = "/people"
|
100
|
-
options = {:keywords => options} if options.is_a?(String)
|
47
|
+
options = { :keywords => options } if options.is_a?(String)
|
101
48
|
options = format_options_for_query(options)
|
102
|
-
|
49
|
+
|
103
50
|
People.from_xml(get(to_uri(path, options)))
|
104
51
|
end
|
105
|
-
|
52
|
+
|
106
53
|
def current_status
|
107
54
|
path = "/people/~/current-status"
|
108
55
|
Crack::XML.parse(get(path))['current_status']
|
109
56
|
end
|
110
|
-
|
57
|
+
|
111
58
|
def update_status(text)
|
112
59
|
path = "/people/~/current-status"
|
113
60
|
put(path, status_to_xml(text))
|
114
61
|
end
|
115
|
-
|
62
|
+
|
63
|
+
def share(options={})
|
64
|
+
path = "/people/~/shares"
|
65
|
+
defaults = { :visability => 'anyone' }
|
66
|
+
post(path, share_to_xml(defaults.merge(options)))
|
67
|
+
end
|
68
|
+
|
69
|
+
def update_comment(network_key, comment)
|
70
|
+
path = "/people/~/network/updates/key=#{network_key}/update-comments"
|
71
|
+
post(path, comment_to_xml(comment))
|
72
|
+
end
|
73
|
+
|
74
|
+
def like(network_key, is_liked=true)
|
75
|
+
path = "/people/~/network/updates/key=#{network_key}/is-liked"
|
76
|
+
put(path, is_liked_to_xml(is_liked))
|
77
|
+
end
|
78
|
+
|
79
|
+
def likes(network_key)
|
80
|
+
path = "/people/~/network/updates/key=#{network_key}/likes"
|
81
|
+
Likes.from_xml(get(path)).likes
|
82
|
+
end
|
83
|
+
|
84
|
+
def update_network(message)
|
85
|
+
path = "/people/~/person-activities"
|
86
|
+
post(path, network_update_to_xml(message))
|
87
|
+
end
|
88
|
+
|
116
89
|
def clear_status
|
117
90
|
path = "/people/~/current-status"
|
118
91
|
delete(path).code
|
119
92
|
end
|
120
|
-
|
93
|
+
|
94
|
+
def send_message(subject, body, recipient_paths)
|
95
|
+
path = "/people/~/mailbox"
|
96
|
+
|
97
|
+
message = LinkedIn::Message.new
|
98
|
+
message.subject = subject
|
99
|
+
message.body = body
|
100
|
+
recipients = LinkedIn::Recipients.new
|
101
|
+
|
102
|
+
recipients.recipients = recipient_paths.map do |profile_path|
|
103
|
+
recipient = LinkedIn::Recipient.new
|
104
|
+
recipient.person = LinkedIn::Person.new
|
105
|
+
recipient.person.path = "/people/#{profile_path}"
|
106
|
+
recipient
|
107
|
+
end
|
108
|
+
|
109
|
+
message.recipients = recipients
|
110
|
+
post(path, message_to_xml(message)).code
|
111
|
+
end
|
112
|
+
|
121
113
|
def network_statuses(options={})
|
122
114
|
options[:type] = 'STAT'
|
123
115
|
network_updates(options)
|
124
116
|
end
|
125
|
-
|
117
|
+
|
126
118
|
def network_updates(options={})
|
127
119
|
path = "/people/~/network"
|
128
120
|
Network.from_xml(get(to_uri(path, options)))
|
129
121
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
# helpful in making authenticated calls and writing the
|
122
|
+
|
123
|
+
# helpful in making authenticated calls and writing the
|
135
124
|
# raw xml to a fixture file
|
136
125
|
def write_fixture(path, filename)
|
137
126
|
file = File.new("test/fixtures/#{filename}", "w")
|
138
127
|
file.puts(access_token.get(path).body)
|
139
128
|
file.close
|
140
129
|
end
|
141
|
-
|
130
|
+
|
142
131
|
private
|
143
|
-
|
144
|
-
@request_token = nil
|
145
|
-
end
|
146
|
-
|
147
|
-
def raise_errors(response)
|
148
|
-
# Even if the XML answer contains the HTTP status code, LinkedIn also sets this code
|
149
|
-
# in the HTTP answer (thankfully).
|
150
|
-
case response.code.to_i
|
151
|
-
when 400
|
152
|
-
data = LinkedIn::Error.from_xml(response.body)
|
153
|
-
raise RateLimitExceeded.new(data), "(#{response.code}): #{response.message} - #{data.code if data}"
|
154
|
-
when 401
|
155
|
-
data = LinkedIn::Error.from_xml(response.body)
|
156
|
-
raise Unauthorized.new(data), "(#{response.code}): #{response.message} - #{data.code if data}"
|
157
|
-
when 403
|
158
|
-
data = LinkedIn::Error.from_xml(response.body)
|
159
|
-
raise General.new(data), "(#{response.code}): #{response.message} - #{data.code if data}"
|
160
|
-
when 404
|
161
|
-
raise NotFound, "(#{response.code}): #{response.message}"
|
162
|
-
when 500
|
163
|
-
raise InformLinkedIn, "LinkedIn had an internal error. Please let them know in the forum. (#{response.code}): #{response.message}"
|
164
|
-
when 502..503
|
165
|
-
raise Unavailable, "(#{response.code}): #{response.message}"
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
132
|
+
|
169
133
|
def format_options_for_query(opts)
|
170
134
|
opts.keys.each do |key|
|
171
135
|
value = opts.delete(key)
|
@@ -175,29 +139,11 @@ module LinkedIn
|
|
175
139
|
end
|
176
140
|
opts
|
177
141
|
end
|
178
|
-
|
179
|
-
def to_query(options)
|
180
|
-
options.inject([]) do |collection, opt|
|
181
|
-
collection << "#{opt[0]}=#{opt[1]}"
|
182
|
-
collection
|
183
|
-
end * '&'
|
184
|
-
end
|
185
|
-
|
186
|
-
def to_uri(path, options)
|
187
|
-
uri = URI.parse(path)
|
188
142
|
|
189
|
-
if options && options != {}
|
190
|
-
uri.query = to_query(options)
|
191
|
-
end
|
192
|
-
uri.to_s
|
193
|
-
end
|
194
|
-
|
195
143
|
def person_path(options)
|
196
144
|
path = "/people/"
|
197
145
|
if options[:id]
|
198
146
|
path += "id=#{options[:id]}"
|
199
|
-
elsif options[:email]
|
200
|
-
path += "email=#{options[:email]}"
|
201
147
|
elsif options[:url]
|
202
148
|
path += "url=#{CGI.escape(options[:url])}"
|
203
149
|
else
|
@@ -205,11 +151,5 @@ module LinkedIn
|
|
205
151
|
end
|
206
152
|
end
|
207
153
|
|
208
|
-
def status_to_xml(status)
|
209
|
-
%Q{<?xml version="1.0" encoding="UTF-8"?>
|
210
|
-
<current-status>#{status}</current-status>}
|
211
|
-
end
|
212
|
-
|
213
|
-
|
214
154
|
end
|
215
|
-
end
|
155
|
+
end
|