linked_in 0.0.21

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/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2009-10-25
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,27 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ docs/sample-oauth-plugin_token.rb
7
+ lib/linked_in.rb
8
+ lib/linked_in/base.rb
9
+ lib/linked_in/oauth.rb
10
+ lib/linked_in/request.rb
11
+ linked_in.gemspec
12
+ script/console
13
+ script/destroy
14
+ script/generate
15
+ spec/fixtures/connections.xml
16
+ spec/fixtures/connections_with_field_selectors.xml
17
+ spec/fixtures/empty.xml
18
+ spec/fixtures/error.xml
19
+ spec/fixtures/network.xml
20
+ spec/fixtures/people.xml
21
+ spec/fixtures/profile.xml
22
+ spec/linked_in/base_spec.rb
23
+ spec/linked_in/oauth_spec.rb
24
+ spec/linked_in/request_spec.rb
25
+ spec/spec.opts
26
+ spec/spec_helper.rb
27
+ tasks/rspec.rake
data/PostInstall.txt ADDED
File without changes
data/README.rdoc ADDED
@@ -0,0 +1,104 @@
1
+ == DESCRIPTION:
2
+
3
+ The linked_in gem wraps the LinkedIn API making it easy to read and write profile information and messages on http://linkedin.com. Full support for OAuth is provided including a wrapper for Pelle's oauth-plugin (http://github.com/pelle/oauth-plugin/). This gem borrowed heavily from junemakers twitter gem (http://github.com/jnunemaker/twitter/).
4
+
5
+ == EXAMPLES:
6
+
7
+ First, authorize via oauth:
8
+
9
+ oauth = LinkedIn::OAuth.new('token', 'secret') # => #<LinkedIn::OAuth:0x1021db048 ...>
10
+
11
+ oauth.request_token.authorize_url # => "https://api.linked_in.com/uas/oauth/authorize?oauth_token=XXXX"
12
+
13
+ Visit authorization URL, authorize and copy PIN
14
+
15
+ oauth.authorize_from_request(oauth.request_token.token, oauth.request_token.secret, PIN)
16
+
17
+ Finally, do stuff!
18
+
19
+ linked_in = LinkedIn::Base.new(oauth)
20
+
21
+ === Profile Information
22
+
23
+ # currently auth'd users basic profile
24
+ linked_in.profile
25
+
26
+ # currently auth'd users full profile
27
+ linked_in.profile(:my, :full)
28
+
29
+ # current auth'd users profile, with field selectors
30
+ linked_in.profile(:my, ['first-name', 'last-name'])
31
+
32
+ # get a profile by public URL
33
+ linked_in.profile(:url => 'http://www.linkedin.com/pub/peter-brown/0/116/112')
34
+
35
+ # get a profile by ID
36
+ linked_in.profile(:id => '12345')
37
+
38
+ # get full profile by ID
39
+ linked_in.profile({:id => '12123'}, :full)
40
+
41
+ # get profile by ID with field selectors
42
+ linked_in.profile({:id => '12123'}, ('first-name', 'last-name'))
43
+
44
+ === Connection Information
45
+
46
+ # currently auth'd users connections
47
+ linked_in.connections
48
+
49
+ === Network Updates
50
+
51
+ # currently auth'd users network updates
52
+ linked_in.network
53
+
54
+ # limit updates
55
+ linked_in.network(:start => 0, :count => 20)
56
+
57
+ == REQUIREMENTS:
58
+
59
+ A few gems are required:
60
+
61
+ * forwardable
62
+ * oauth
63
+ * xml-object
64
+
65
+ You'll also need a LinkedIn API account, which you can get here:
66
+
67
+ * http://developer.linked_in.com/community/apis
68
+
69
+ == INSTALL:
70
+
71
+ sudo gem install gemcutter
72
+ gem tumble
73
+ sudo gem install linked_in
74
+
75
+ == TODO:
76
+
77
+ * add support for messaging between connections (http://developer.linked_in.com/docs/DOC-1044)
78
+ * add support for get network updates (http://developer.linked_in.com/docs/DOC-1006)
79
+ * add support for search API (http://developer.linked_in.com/docs/DOC-1005)
80
+
81
+ == LICENSE:
82
+
83
+ (The MIT License)
84
+
85
+ Copyright (c) 2009 FIXME full name
86
+
87
+ Permission is hereby granted, free of charge, to any person obtaining
88
+ a copy of this software and associated documentation files (the
89
+ 'Software'), to deal in the Software without restriction, including
90
+ without limitation the rights to use, copy, modify, merge, publish,
91
+ distribute, sublicense, and/or sell copies of the Software, and to
92
+ permit persons to whom the Software is furnished to do so, subject to
93
+ the following conditions:
94
+
95
+ The above copyright notice and this permission notice shall be
96
+ included in all copies or substantial portions of the Software.
97
+
98
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
99
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
100
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
101
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
102
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
103
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
104
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'hoe' #, '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/linked_in'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'linked_in' do
14
+ self.developer 'Peter T. Brown', 'peter@pathable.com'
15
+ self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ # self.extra_deps = [['activesupport','>= 2.0.2']]
18
+
19
+ end
20
+
21
+ require 'newgem/tasks'
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
23
+
24
+ # TODO - want other tests/tasks run by default? Add them to the list
25
+ # remove_task :default
26
+ # task :default => [:spec, :features]
@@ -0,0 +1,18 @@
1
+ require 'linked_in'
2
+ require 'oauth/models/consumers/token'
3
+
4
+ class ConsumerToken < ActiveRecord::Base
5
+ include Oauth::Models::Consumers::Token
6
+ end
7
+
8
+ class LinkedInToken < ConsumerToken
9
+ def client
10
+ unless @client
11
+ @linkedin_oauth = LinkedIn::OAuth.new(LinkedInToken.consumer.key, LinkedInToken.consumer.secret)
12
+ @linkedin_oauth.authorize_from_access(token,secret)
13
+ @client = LinkedIn::Base.new(@linkedin_oauth)
14
+ end
15
+
16
+ @client
17
+ end
18
+ end
data/lib/linked_in.rb ADDED
@@ -0,0 +1,27 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'forwardable'
6
+ gem 'oauth', '>= 0.3.5'
7
+ require 'oauth'
8
+ gem 'jordi-xml-object', '>= 0.9.91'
9
+ require 'xml-object'
10
+
11
+
12
+
13
+ module LinkedIn
14
+ VERSION = '0.0.21'
15
+
16
+ class LinkedInError < StandardError; end
17
+ class Unauthorized < LinkedInError; end
18
+ class General < LinkedInError; end
19
+ class Unavailable < StandardError; end
20
+ class NotFound < StandardError; end
21
+ class Forbidden < StandardError; end
22
+
23
+ autoload :Base, File.join(File.dirname(__FILE__), *%w[linked_in base.rb])
24
+ autoload :OAuth, File.join(File.dirname(__FILE__), *%w[linked_in oauth.rb])
25
+ autoload :Request, File.join(File.dirname(__FILE__), *%w[linked_in request.rb])
26
+ end
27
+
@@ -0,0 +1,84 @@
1
+ module LinkedIn
2
+ class Base
3
+ API_VERSION = 'v1'
4
+ extend Forwardable
5
+
6
+ def_delegators :client, :get, :post, :put
7
+
8
+ attr_reader :client
9
+
10
+ def initialize(client)
11
+ @client = client
12
+ end
13
+
14
+ # Lookup Options: url=<public profile URL>, id=<member id>
15
+ # Field Options: :full
16
+ def profile(lookup = nil, fields = nil, query = {})
17
+ path = profile_path(nil, lookup, fields)
18
+ perform_get(path, :query => query)
19
+ end
20
+
21
+ # Query options: start=N, count=N
22
+ def people(query = {})
23
+ path = api_path('people')
24
+ perform_get(path, query)
25
+ end
26
+
27
+ # Query options: start=N, count=N
28
+ def connections(lookup = nil, fields = nil, query = {})
29
+ path = profile_path('connections', lookup, fields)
30
+ perform_get(path, :query => query)
31
+ end
32
+
33
+ def network(query = {})
34
+ path = profile_path('network')
35
+ perform_get(path, :query => query)
36
+ end
37
+
38
+ def current_status(update)
39
+ path = profile_path('current-status')
40
+ update_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><current-status>#{update}</current-status>"
41
+ perform_put(path, :body => update_xml)
42
+ end
43
+
44
+ # update is something like: '&lt;a href=&quot;http://www.linkedin.com/profile?viewProfile=&amp;key=ABCDEFG&quot;&gt;Richard Brautigan&lt;/a&gt; is reading about&lt;a href=&quot;http://www.tigers.com&quot;&gt;Tigers&lt;/a&gt;http://www.tigers.com&gt;Tigers&lt;/a&gt;.'}
45
+ def person_activities(update, timestamp = nil)
46
+ path = profile_path('person-activities')
47
+ body = {'activity' => {
48
+ 'timestamp' => (timestamp || Time.now.to_i),
49
+ 'content-type' => 'linkedin-html',
50
+ 'body' => update}
51
+ }
52
+ perform_post(path, body)
53
+ end
54
+
55
+
56
+ private
57
+
58
+ def api_path(p)
59
+ "/#{API_VERSION}/#{p}"
60
+ end
61
+
62
+ # Generate LinkedIn API compatable paths
63
+ def profile_path(api = nil, lookup = nil, fields = nil)
64
+ path = api_path('people') + '/'
65
+ path += lookup.kind_of?(Hash) ? "#{lookup.first[0]}=#{lookup.first[1]}" : ([nil, :my].include?(lookup) ? '~' : lookup.to_s)
66
+ path += api.nil? ? '' : "/#{api}"
67
+ path += fields.kind_of?(Array) ? ":(#{fields.collect{|f| f.to_s}.join(',')})" : (fields.nil? ? '' : ":#{fields.to_s}")
68
+ path
69
+ end
70
+
71
+ def perform_get(path, options={})
72
+ LinkedIn::Request.get(self, path, options)
73
+ end
74
+
75
+ def perform_post(path, options={})
76
+ LinkedIn::Request.post(self, path, options)
77
+ end
78
+
79
+ def perform_put(path, options={})
80
+ LinkedIn::Request.put(self, path, options)
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,65 @@
1
+ module LinkedIn
2
+ # >> oauth = LinkedIn::OAuth.new('token', 'secret')
3
+ # => #<LinkedIn::OAuth:0x1021db048 ...>
4
+ # >> oauth.request_token.authorize_url
5
+ # => "https://api.linkedin.com/uas/oauth/authorize?oauth_token=XXXX" # goto URL
6
+ # >> oauth.authorize_from_request(oauth.request_token.token, oauth.request_token.secret, PIN)
7
+ # DONE
8
+ class OAuth
9
+ extend Forwardable
10
+
11
+ SITE_URL = 'https://api.linkedin.com'
12
+
13
+ def_delegators :access_token, :get, :post, :put
14
+
15
+ attr_reader :ctoken, :csecret, :consumer_options
16
+
17
+ # Options
18
+ def initialize(ctoken, csecret, options={})
19
+ @ctoken = ctoken
20
+ @csecret = csecret
21
+ @consumer_options = {
22
+ :request_token_path => "/uas/oauth/requestToken?oauth_callback=oob",
23
+ :access_token_path => "/uas/oauth/accessToken",
24
+ :authorize_path => "/uas/oauth/authorize"
25
+ }.merge(options)
26
+ end
27
+
28
+ def consumer
29
+ @consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => SITE_URL}.merge(consumer_options))
30
+ end
31
+
32
+ def set_callback_url(url)
33
+ clear_request_token
34
+ request_token(:oauth_callback => url)
35
+ end
36
+
37
+ # Note: If using oauth with a web app, be sure to provide :oauth_callback.
38
+ # Options:
39
+ # :oauth_callback => String, url that linkedin should redirect to
40
+ def request_token(options={})
41
+ @request_token ||= consumer.get_request_token(options)
42
+ end
43
+
44
+ # For web apps use params[:oauth_verifier], for desktop apps,
45
+ # use the verifier is the pin that twitter gives users.
46
+ def authorize_from_request(rtoken, rsecret, verifier_or_pin)
47
+ request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
48
+ access_token = request_token.get_access_token(:oauth_verifier => verifier_or_pin)
49
+ @atoken, @asecret = access_token.token, access_token.secret
50
+ end
51
+
52
+ def access_token
53
+ @access_token ||= ::OAuth::AccessToken.new(consumer, @atoken, @asecret)
54
+ end
55
+
56
+ def authorize_from_access(atoken, asecret)
57
+ @atoken, @asecret = atoken, asecret
58
+ end
59
+
60
+ private
61
+ def clear_request_token
62
+ @request_token = nil
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,86 @@
1
+ module LinkedIn
2
+ class Request
3
+ extend Forwardable
4
+
5
+ def self.get(client, path, options = {})
6
+ new(client, :get, path, options).perform
7
+ end
8
+
9
+ def self.post(client, path, options = {})
10
+ new(client, :post, path, options).perform
11
+ end
12
+
13
+ def self.put(client, path, options = {})
14
+ new(client, :put, path, options).perform
15
+ end
16
+
17
+ attr_reader :client, :method, :path, :options
18
+
19
+ def_delegators :client, :get, :post, :put
20
+
21
+ def initialize(client, method, path, options={})
22
+ @client, @method, @path, @options = client, method, path, {:mash => true}.merge(options)
23
+ end
24
+
25
+ def uri
26
+ @uri ||= begin
27
+ uri = URI.parse(path)
28
+
29
+ if options[:query] && options[:query] != {}
30
+ uri.query = to_query(options[:query])
31
+ end
32
+
33
+ uri.to_s
34
+ end
35
+ end
36
+
37
+ def perform
38
+ make_friendly(send("perform_#{method}"))
39
+ end
40
+
41
+ private
42
+ def perform_get
43
+ send(:get, uri, options[:headers])
44
+ end
45
+
46
+ def perform_post
47
+ send(:post, uri, options[:body], options[:headers])
48
+ end
49
+
50
+ def perform_put
51
+ send(:put, uri, options[:body], options[:headers])
52
+ end
53
+
54
+ def make_friendly(response)
55
+ raise_errors(response)
56
+ parse(response)
57
+ end
58
+
59
+ def raise_errors(response)
60
+ case response.code.to_i
61
+ when 401 # NotAuthorized
62
+ raise Unauthorized.new, "#{response.code}: #{response.message}"
63
+ when 403 # Forbidden
64
+ raise Forbidden.new, "#{response.code}: #{response.message}"
65
+ when 404 # NotFound
66
+ raise NotFound, "#{response.code}: #{response.message}"
67
+ when 500 # InternalServerError
68
+ raise Unavailable, "LinkedIn had an internal error #{response.code}: #{response.message}"
69
+ when 502..503 # Unavailable, BadGateway
70
+ raise Unavailable, "#{response.code}: #{response.message}"
71
+ end
72
+ end
73
+
74
+ def parse(response)
75
+ return if response.nil? || response.body.blank?
76
+ XMLObject.new(response.body)
77
+ end
78
+
79
+ def to_query(options)
80
+ options.inject([]) do |collection, opt|
81
+ collection << "#{opt[0]}=#{opt[1]}"
82
+ collection
83
+ end * '&'
84
+ end
85
+ end
86
+ end