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,42 @@
1
+ module LinkedIn
2
+ class Patents < LinkedIn::Base
3
+
4
+ def patents
5
+ @patents ||= @doc.children.inject([]) do |list, patent|
6
+ list << Resource.new(patent) unless patent.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ class Resource
12
+
13
+ def initialize(patent)
14
+ @patent = patent
15
+ end
16
+
17
+ %w[id title].each do |f|
18
+ define_method(f.to_sym) do
19
+ @patent.xpath("./#{f.gsub(/_/,'-')}").text
20
+ end
21
+ end
22
+
23
+ def year
24
+ @year ||= @patent.xpath("./date/year").text.to_i
25
+ end
26
+
27
+ def day
28
+ @day ||= @patent.xpath("./date/day").text.to_i
29
+ end
30
+
31
+ def month
32
+ @month ||= @patent.xpath("./date/month").text.to_i
33
+ end
34
+
35
+ def date
36
+ Date.civil(y=year,m=month,d=day)
37
+ end
38
+
39
+ end # resource class
40
+
41
+ end # patent class
42
+ end
@@ -0,0 +1,18 @@
1
+ module LinkedIn
2
+ class People < LinkedIn::Base
3
+
4
+ %w[total start count].each do |f|
5
+ define_method(f.to_sym) do
6
+ @doc.xpath('.//people').first["#{f.gsub(/_/,'-')}"].to_i
7
+ end
8
+ end
9
+
10
+ def profiles
11
+ @profiles ||= @doc.xpath('//people').children.inject([]) do |list, profile|
12
+ list << Profile.new(Nokogiri::XML(profile.to_xml)) unless profile.blank?
13
+ list
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ module LinkedIn
2
+ class Person
3
+
4
+ attr_accessor :path
5
+
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ module LinkedIn
2
+ class PhoneNumber < LinkedIn::Base
3
+
4
+ def phone_numbers
5
+ @array ||= begin
6
+ @array = []
7
+ @doc.children.each do |pn|
8
+ @array << Resource.new(pn) unless pn.blank?
9
+ end
10
+ @array
11
+ end
12
+ end
13
+
14
+ class Resource
15
+
16
+ def initialize(phone_number)
17
+ @phone_number = phone_number
18
+ end
19
+
20
+ %w[phone_type phone_number].each do |f|
21
+ define_method(f.to_sym) do
22
+ @phone_number.xpath("./#{f.gsub(/_/,'-')}").text
23
+ end
24
+ end
25
+
26
+ end # resource class
27
+
28
+ end # phone_number class
29
+ end
@@ -0,0 +1,46 @@
1
+ module LinkedIn
2
+ class Position < LinkedIn::Base
3
+
4
+ def positions
5
+ @positions ||= @doc.children.inject([]) do |list, position|
6
+ list << Resource.new(position) unless position.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ class Resource
12
+
13
+ def initialize(position)
14
+ @position = position
15
+ end
16
+
17
+ %w[id title summary is_current].each do |f|
18
+ define_method(f.to_sym) do
19
+ @position.xpath("./#{f.gsub(/_/,'-')}").text
20
+ end
21
+ end
22
+
23
+ def start_month
24
+ @position.xpath('./start-date/month').text.to_i
25
+ end
26
+
27
+ def start_year
28
+ @position.xpath('./start-date/year').text.to_i
29
+ end
30
+
31
+ def end_month
32
+ @position.xpath('./end-date/month').text.to_i
33
+ end
34
+
35
+ def end_year
36
+ @position.xpath('./end-date/year').text.to_i
37
+ end
38
+
39
+ def company
40
+ @company ||= Company.new(@position.xpath('./company'))
41
+ end
42
+
43
+ end # resource
44
+
45
+ end # class
46
+ end # module
@@ -0,0 +1,85 @@
1
+ module LinkedIn
2
+ class Profile < LinkedIn::Base
3
+
4
+ # xml_reader :three_current_positions, :as => [Position]
5
+
6
+ PROFILE_FIELDS = %w[id first_name last_name headline industry
7
+ current_status current_status_timestamp summary
8
+ specialties proposal_comments associations honors
9
+ interests picture_url distance num_recommenders public_profile_url]
10
+
11
+ PROFILE_FIELDS.each do |f|
12
+ define_method(f.to_sym) do
13
+ @doc.xpath("./person/#{f.gsub(/_/,'-')}").text
14
+ end
15
+ end
16
+
17
+ def birthdate
18
+ @birthday ||= Birthdate.new(@doc.xpath('./person/date-of-birth'))
19
+ end
20
+
21
+ def location
22
+ @location ||= Location.new(@doc)
23
+ end
24
+
25
+ def api_standard_profile_request
26
+ @api_standard ||= ApiStandardProfileRequest.new(@doc.xpath('./person/api-standard-profile-request'))
27
+ end
28
+
29
+ def site_standard_profile_request
30
+ @doc.xpath('/person/site-standard-profile-request/url').text
31
+ end
32
+
33
+ def relation_to_viewer
34
+ @doc.xpath('//relation-to-viewer/distance').text
35
+ end
36
+
37
+ def member_url_resources
38
+ @url_resources ||= UrlResource.new(@doc.xpath('//member-url-resources')).resources
39
+ end
40
+
41
+ def positions
42
+ @positions ||= Position.new(@doc.xpath('//positions')).positions
43
+ end
44
+
45
+ def educations
46
+ @educations ||= Education.new(@doc.xpath('//educations')).educations
47
+ end
48
+
49
+ def connections
50
+ @connections ||= Connections.new(@doc.xpath('//connections')).connections
51
+ end
52
+
53
+ def groups
54
+ @groups ||= Group.new(@doc.xpath('//member-groups')).groups
55
+ end
56
+
57
+ def recommendations
58
+ @recommendations ||= Recommendations.new(@doc.xpath('//recommendations-received')).recommendations
59
+ end
60
+
61
+ def current_share
62
+ @current_share ||= CurrentShare.new(@doc.xpath('//current-share'))
63
+ end
64
+
65
+ def languages
66
+ @languages ||= Languages.new(@doc.xpath('//languages')).languages
67
+ end
68
+
69
+ def skills
70
+ @skills ||= Skill.new(@doc.xpath('//skills')).skills
71
+ end
72
+
73
+ def phone_numbers
74
+ @phone_number ||= PhoneNumber.new(@doc.xpath('//phone-numbers')).phone_numbers
75
+ end
76
+
77
+ def publications
78
+ @publications ||= Publications.new(@doc.xpath('//publications')).publications
79
+ end
80
+
81
+ def patents
82
+ @patents ||= Patents.new(@doc.xpath('//patents')).patents
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,40 @@
1
+ module LinkedIn
2
+ class Publications < LinkedIn::Base
3
+
4
+ def publications
5
+ @publications ||= @doc.children.inject([]) do |list, publication|
6
+ list << Resource.new(publication) unless publication.blank?
7
+ list
8
+ end
9
+ end
10
+
11
+ class Resource
12
+ def initialize(publication)
13
+ @publication = publication
14
+ end
15
+
16
+ %w[id title].each do |f|
17
+ define_method(f.to_sym) do
18
+ @publication.xpath("./#{f.gsub(/_/,'-')}").text
19
+ end
20
+ end
21
+
22
+ def year
23
+ @year ||= @publication.xpath("./date/year").text.to_i
24
+ end
25
+
26
+ def day
27
+ @day ||= @publication.xpath("./date/day").text.to_i
28
+ end
29
+
30
+ def month
31
+ @month ||= @publication.xpath("./date/month").text.to_i
32
+ end
33
+
34
+ def date
35
+ Date.civil(year, month, day)
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ module LinkedIn
2
+ class Recipient
3
+
4
+ attr_accessor :person
5
+
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ module LinkedIn
2
+ class Recipients
3
+
4
+ attr_accessor :recipients
5
+
6
+ def to_xml
7
+ self.to_xml_nodes(Nokogiri.XML('<root/>', nil, 'UTF-8')).to_xml
8
+ end
9
+
10
+ def to_xml_nodes(doc)
11
+ recipients.inject(Nokogiri::XML::NodeSet.new(doc)) do |nodes, recipient|
12
+ node = Nokogiri::XML::DocumentFragment.new(doc, '<recipient><person/></recipient>')
13
+ node.at_css('person')['path'] = recipient.person.path
14
+ nodes << node
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,30 @@
1
+ module LinkedIn
2
+
3
+ class Recommendations < LinkedIn::Base
4
+
5
+ def recommendations
6
+ @recommendations ||= @doc.children.inject([]) do |list, recommendation|
7
+ list << Recommendation.new(recommendation) unless recommendation.blank?
8
+ list
9
+ end
10
+ end
11
+
12
+ class Recommendation
13
+ def initialize(recommendation)
14
+ @recommendation = recommendation
15
+ end
16
+
17
+ %w[id recommendation_type recommendation_text].each do |f|
18
+ define_method(f.to_sym) do
19
+ @recommendation.xpath("./#{f.gsub(/_/,'-')}").text
20
+ end
21
+ end
22
+
23
+ def recommender
24
+ @recommender ||= ShortProfile.new(@recommendation.xpath('./recommender'))
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,78 @@
1
+ module LinkedIn
2
+
3
+ module RequestHelpers
4
+
5
+ def get(path, options={})
6
+ path = "/v1#{path}"
7
+ response = access_token.get(path, options)
8
+ raise_errors(response)
9
+ response.body
10
+ end
11
+
12
+ def post(path, body='', options={})
13
+ path = "/v1#{path}"
14
+ default_options = { 'Content-Type' => 'application/xml' }
15
+ response = access_token.post(path, body, default_options.merge(options))
16
+ raise_errors(response)
17
+ response
18
+ end
19
+
20
+ def put(path, body, options={})
21
+ path = "/v1#{path}"
22
+ response = access_token.put(path, body, options)
23
+ raise_errors(response)
24
+ response
25
+ end
26
+
27
+ def delete(path, options={})
28
+ path = "/v1#{path}"
29
+ response = access_token.delete(path, options)
30
+ raise_errors(response)
31
+ response
32
+ end
33
+
34
+ private
35
+
36
+ def raise_errors(response)
37
+ # Even if the XML answer contains the HTTP status code, LinkedIn also sets this code
38
+ # in the HTTP answer (thankfully).
39
+ if response.code.to_i >= 400
40
+ data = LinkedIn::Error.from_xml(response.body)
41
+ message = "(#{response.code}): #{data.message} - #{data.code}"
42
+
43
+ case response.code.to_i
44
+ when 400
45
+ raise BadRequest.new(data), message
46
+ when 401
47
+ raise Unauthorized.new(data), message
48
+ when 403
49
+ raise Forbidden.new(data), message
50
+ when 404
51
+ raise NotFound.new(data), message
52
+ when 500
53
+ raise InformLinkedIn, "LinkedIn had an internal error. Please let them know in the forum. #{message}"
54
+ when 502..503
55
+ raise Unavailable, message
56
+ end
57
+ end
58
+ end
59
+
60
+ def to_query(options)
61
+ query_string = options.inject([]) do |collection, opt|
62
+ collection << "#{opt[0]}=#{opt[1]}"
63
+ collection
64
+ end * '&'
65
+ URI.escape(query_string)
66
+ end
67
+
68
+ def to_uri(path, options)
69
+ uri = URI.parse(path)
70
+
71
+ if options && options != {}
72
+ uri.query = to_query(options)
73
+ end
74
+ uri.to_s
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,13 @@
1
+ module LinkedIn
2
+
3
+ class ShortProfile < LinkedIn::Base
4
+
5
+ %w[id first_name last_name].each do |f|
6
+ define_method(f.to_sym) do
7
+ @doc.xpath("./#{f.gsub(/_/,'-')}").text
8
+ end
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,33 @@
1
+ module LinkedIn
2
+ class Skill < LinkedIn::Base
3
+
4
+ def skills
5
+ @array ||= begin
6
+ @array = []
7
+ @doc.children.each do |skill|
8
+ @array << Resource.new(skill) unless skill.blank?
9
+ end
10
+ @array
11
+ end
12
+ end
13
+
14
+ class Resource
15
+
16
+ def initialize(skill)
17
+ @skill = skill
18
+ end
19
+
20
+ def name
21
+ @skill.xpath("./skill/name").text
22
+ end
23
+
24
+ %w[id].each do |f| #proficiency level to come? http://developer.linkedin.com/community/apis/blog/2011/01/04/new-profile-fields-are-here
25
+ define_method(f.to_sym) do
26
+ @skill.xpath("./#{f.gsub(/_/,'-')}").text
27
+ end
28
+ end
29
+
30
+ end # resource class
31
+
32
+ end # skill class
33
+ end