linkedin-bdigital 0.2.2

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.
Files changed (69) hide show
  1. data/.autotest +14 -0
  2. data/.document +5 -0
  3. data/.gitignore +25 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +20 -0
  6. data/README.markdown +78 -0
  7. data/Rakefile +41 -0
  8. data/VERSION +1 -0
  9. data/changelog.markdown +71 -0
  10. data/examples/authenticate.rb +21 -0
  11. data/examples/network.rb +12 -0
  12. data/examples/profile.rb +14 -0
  13. data/examples/status.rb +9 -0
  14. data/lib/linked_in/api_standard_profile_request.rb +17 -0
  15. data/lib/linked_in/authorization_helpers.rb +48 -0
  16. data/lib/linked_in/base.rb +13 -0
  17. data/lib/linked_in/birthdate.rb +21 -0
  18. data/lib/linked_in/client.rb +155 -0
  19. data/lib/linked_in/company.rb +11 -0
  20. data/lib/linked_in/connections.rb +16 -0
  21. data/lib/linked_in/country.rb +9 -0
  22. data/lib/linked_in/current_share.rb +56 -0
  23. data/lib/linked_in/education.rb +41 -0
  24. data/lib/linked_in/error.rb +21 -0
  25. data/lib/linked_in/group.rb +32 -0
  26. data/lib/linked_in/languages.rb +28 -0
  27. data/lib/linked_in/likes.rb +23 -0
  28. data/lib/linked_in/location.rb +13 -0
  29. data/lib/linked_in/message.rb +20 -0
  30. data/lib/linked_in/network.rb +12 -0
  31. data/lib/linked_in/patents.rb +42 -0
  32. data/lib/linked_in/people.rb +18 -0
  33. data/lib/linked_in/person.rb +7 -0
  34. data/lib/linked_in/phone_number.rb +29 -0
  35. data/lib/linked_in/position.rb +46 -0
  36. data/lib/linked_in/profile.rb +85 -0
  37. data/lib/linked_in/publications.rb +40 -0
  38. data/lib/linked_in/recipient.rb +7 -0
  39. data/lib/linked_in/recipients.rb +18 -0
  40. data/lib/linked_in/recommendations.rb +30 -0
  41. data/lib/linked_in/request_helpers.rb +78 -0
  42. data/lib/linked_in/short_profile.rb +13 -0
  43. data/lib/linked_in/skill.rb +33 -0
  44. data/lib/linked_in/to_xml_helpers.rb +53 -0
  45. data/lib/linked_in/update.rb +23 -0
  46. data/lib/linked_in/url_resource.rb +26 -0
  47. data/lib/linkedin.rb +81 -0
  48. data/linkedin.gemspec +49 -0
  49. data/spec/cases/client_spec.rb +230 -0
  50. data/spec/cases/linkedin_spec.rb +37 -0
  51. data/spec/cases/oauth_spec.rb +109 -0
  52. data/spec/client_shared_examples.rb +91 -0
  53. data/spec/fixtures/403.xml +7 -0
  54. data/spec/fixtures/404.xml +7 -0
  55. data/spec/fixtures/blank.xml +0 -0
  56. data/spec/fixtures/connections.xml +3733 -0
  57. data/spec/fixtures/likes.xml +18 -0
  58. data/spec/fixtures/mailbox_items.xml +16 -0
  59. data/spec/fixtures/network_status_with_group.xml +44 -0
  60. data/spec/fixtures/network_statuses.xml +317 -0
  61. data/spec/fixtures/picture_updates.xml +117 -0
  62. data/spec/fixtures/profile.xml +9 -0
  63. data/spec/fixtures/profile_full.xml +3909 -0
  64. data/spec/fixtures/profile_with_positions.xml +79 -0
  65. data/spec/fixtures/search.xml +538 -0
  66. data/spec/fixtures/shares.xml +12 -0
  67. data/spec/fixtures/status.xml +2 -0
  68. data/spec/spec_helper.rb +58 -0
  69. metadata +179 -0
@@ -0,0 +1,155 @@
1
+ module LinkedIn
2
+ class Client
3
+ include ToXmlHelpers
4
+ include RequestHelpers
5
+ include AuthorizationHelpers
6
+
7
+ attr_reader :ctoken, :csecret, :consumer_options
8
+
9
+ def initialize(ctoken=LinkedIn.token, csecret=LinkedIn.secret, options={})
10
+ opts = {
11
+ :request_token_path => "/uas/oauth/requestToken",
12
+ :access_token_path => "/uas/oauth/accessToken",
13
+ :authorize_path => "/uas/oauth/authorize"
14
+ }
15
+ @ctoken, @csecret, @consumer_options = ctoken, csecret, opts.merge(options)
16
+ end
17
+
18
+
19
+ def profile(options={})
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(',')})"
27
+ end
28
+
29
+ Profile.from_xml(get(path))
30
+ end
31
+
32
+ def connections(options={})
33
+ path = "#{person_path(options)}/connections"
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(',')})"
40
+ end
41
+
42
+ Connections.from_xml(get(path)).profiles
43
+ end
44
+
45
+ def search(options={})
46
+ path = "/people"
47
+ options = { :keywords => options } if options.is_a?(String)
48
+ options = format_options_for_query(options)
49
+
50
+ People.from_xml(get(to_uri(path, options)))
51
+ end
52
+
53
+ def current_status
54
+ path = "/people/~/current-status"
55
+ Nokogiri::XML(get(path)).xpath('//current-status').text
56
+ end
57
+
58
+ def update_status(text)
59
+ path = "/people/~/current-status"
60
+ put(path, status_to_xml(text))
61
+ end
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
+
89
+ def clear_status
90
+ path = "/people/~/current-status"
91
+ delete(path).code
92
+ end
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
+
113
+ def network_statuses(options={})
114
+ options[:type] = 'STAT'
115
+ network_updates(options)
116
+ end
117
+
118
+ def network_updates(options={})
119
+ path = "/people/~/network"
120
+ Network.from_xml(get(to_uri(path, options)))
121
+ end
122
+
123
+ # helpful in making authenticated calls and writing the
124
+ # raw xml to a fixture file
125
+ def write_fixture(path, filename)
126
+ file = File.new("test/fixtures/#{filename}", "w")
127
+ file.puts(access_token.get(path).body)
128
+ file.close
129
+ end
130
+
131
+ private
132
+
133
+ def format_options_for_query(opts)
134
+ opts.keys.each do |key|
135
+ value = opts.delete(key)
136
+ value = value.join("+") if value.is_a?(Array)
137
+ value = value.gsub(" ", "+") if value.is_a?(String)
138
+ opts[key.to_s.gsub("_","-")] = value
139
+ end
140
+ opts
141
+ end
142
+
143
+ def person_path(options)
144
+ path = "/people/"
145
+ if options[:id]
146
+ path += "id=#{options[:id]}"
147
+ elsif options[:url]
148
+ path += "url=#{CGI.escape(options[:url])}"
149
+ else
150
+ path += "~"
151
+ end
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,11 @@
1
+ module LinkedIn
2
+ class Company < LinkedIn::Base
3
+
4
+ %w[type name industry id].each do |f|
5
+ define_method(f.to_sym) do
6
+ @doc.xpath("./#{f.gsub(/_/,'-')}").text
7
+ end
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module LinkedIn
2
+ class Connections < LinkedIn::Base
3
+
4
+ def connections
5
+ @connections ||= @doc.xpath('//connections').children.inject([]) do |list, profile|
6
+ list << Profile.new(Nokogiri::XML(profile.to_xml)) unless profile.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ def profiles
12
+ connections
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module LinkedIn
2
+ class Country < LinkedIn::Base
3
+
4
+ def code
5
+ @doc.xpath("/person/location/country/code").text
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,56 @@
1
+ module LinkedIn
2
+
3
+ class CurrentShare < LinkedIn::Base
4
+ %w[id comment].each do |f|
5
+ define_method(f.to_sym) do
6
+ @doc.xpath("./#{f.gsub(/_/,'-')}").text
7
+ end
8
+ end
9
+
10
+ def submitted_url
11
+ @doc.xpath('./content/submitted-url').text
12
+ end
13
+
14
+ def resolved_url
15
+ @doc.xpath('./content/resolved-url').text
16
+ end
17
+
18
+ def shortened_url
19
+ @doc.xpath('./content/shortened-url').text
20
+ end
21
+
22
+ def title
23
+ @doc.xpath('./content/title').text
24
+ end
25
+
26
+ def description
27
+ @doc.xpath('./content/description').text
28
+ end
29
+
30
+ def submitted_image_url
31
+ @doc.xpath('./content/submitted-image-url').text
32
+ end
33
+
34
+ def thumbnail_url
35
+ @doc.xpath('./content/thumbnail-url').text
36
+ end
37
+
38
+ def timestamp
39
+ time = @doc.xpath('./timestamp').text.to_i
40
+ Time.at(time / 1000)
41
+ end
42
+
43
+ def visibility
44
+ @doc.xpath('./visibility/code').text
45
+ end
46
+
47
+ def application
48
+ @doc.xpath('./source/application/name').text
49
+ end
50
+
51
+ def author
52
+ @author ||= ShortProfile.new(@doc.xpath('./author'))
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,41 @@
1
+ module LinkedIn
2
+ class Education < LinkedIn::Base
3
+
4
+ def educations
5
+ @educations ||= @doc.children.inject([]) do |list, edu|
6
+ list << Resource.new(edu) unless edu.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ class Resource
12
+
13
+ def initialize(education)
14
+ @education = education
15
+ end
16
+
17
+ %w[id school_name degree field_of_study activities notes].each do |f|
18
+ define_method(f.to_sym) do
19
+ @education.xpath("./#{f.gsub(/_/,'-')}").text
20
+ end
21
+ end
22
+
23
+ def start_month
24
+ @education.xpath('./start-date/month').text.to_i
25
+ end
26
+
27
+ def start_year
28
+ @education.xpath('./start-date/year').text.to_i
29
+ end
30
+
31
+ def end_month
32
+ @education.xpath('./end-date/month').text.to_i
33
+ end
34
+
35
+ def end_year
36
+ @education.xpath('./end-date/year').text.to_i
37
+ end
38
+ end # resource class
39
+
40
+ end # education class
41
+ end
@@ -0,0 +1,21 @@
1
+ module LinkedIn
2
+ class Error < LinkedIn::Base
3
+
4
+ def status
5
+ @doc.xpath('//status').text.to_i
6
+ end
7
+
8
+ def timestamp
9
+ @doc.xpath('//timestamp').text.to_i
10
+ end
11
+
12
+ def code
13
+ @doc.xpath('//error-code').text.to_i
14
+ end
15
+
16
+ def message
17
+ @doc.xpath('//message').text
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ module LinkedIn
2
+ class Group < LinkedIn::Base
3
+
4
+ def groups
5
+ @groups ||= @doc.children.inject([]) do |list, group|
6
+ list << Resource.new(group) unless group.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ class Resource
12
+
13
+ def initialize(group)
14
+ @group = group
15
+ end
16
+
17
+ def id
18
+ @group.xpath('//member-group/id').text.to_i
19
+ end
20
+
21
+ def name
22
+ @group.xpath('//member-group/name').text
23
+ end
24
+
25
+ def url
26
+ @group.xpath('//member-group/site-group-request/url').text
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ module LinkedIn
2
+ class Languages < LinkedIn::Base
3
+
4
+ def languages
5
+ @languages ||= @doc.children.inject([]) do |list, lang|
6
+ list << Resource.new(lang) unless lang.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ class Resource
12
+ # proficiency level not implemented yet
13
+ # http://developer.linkedin.com/community/apis/blog/2011/01/04/new-profile-fields-are-here
14
+ def initialize(language)
15
+ @language = language
16
+ end
17
+
18
+ def id
19
+ @language.xpath("./id").text
20
+ end
21
+
22
+ def name
23
+ @language.xpath("./language/name").text
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ module LinkedIn
2
+ class Likes < LinkedIn::Base
3
+
4
+ def likes
5
+ @likes ||= @doc.xpath('//likes').children.inject([]) do |list, like|
6
+ list << Resource.new(like) unless like.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ class Resource
12
+ def initialize(like)
13
+ @like = like
14
+ end
15
+
16
+ def profile
17
+ Profile.new(Nokogiri::XML(@like.xpath('./person').to_xml))
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module LinkedIn
2
+ class Location < LinkedIn::Base
3
+
4
+ def name
5
+ @doc.xpath("/person/location/name").text
6
+ end
7
+
8
+ def country
9
+ @doc.xpath("/person/location/country/code").text
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ module LinkedIn
2
+ class Message
3
+
4
+ attr_accessor :subject, :body, :recipients
5
+
6
+ def to_xml
7
+ self.to_xml_node(Nokogiri.XML('<root/>', nil, 'UTF-8')).to_xml
8
+ end
9
+
10
+ def to_xml_node(doc)
11
+ node = Nokogiri::XML::DocumentFragment.new(doc, '<mailbox-item><recipients/><subject/><body/></mailbox-item>')
12
+ node.at_css('recipients').add_child(self.recipients.to_xml_nodes(doc))
13
+ node.at_css('subject').content = self.subject
14
+ node.at_css('body').content = self.body
15
+ node
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,12 @@
1
+ module LinkedIn
2
+ class Network < LinkedIn::Base
3
+
4
+ def updates
5
+ @updates ||= @doc.xpath('//updates').children.inject([]) do |list, update|
6
+ list << Update.new(Nokogiri::XML(update.to_xml)) unless update.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ end
12
+ end