orcid 0.1.1 → 0.8.0

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +2 -1
  3. data/CONTRIBUTING.md +213 -0
  4. data/README.md +2 -5
  5. data/app/controllers/orcid/profile_requests_controller.rb +11 -1
  6. data/app/models/orcid/profile_connection.rb +12 -2
  7. data/app/models/orcid/profile_status.rb +67 -0
  8. data/app/services/orcid/remote/profile_creation_service.rb +15 -0
  9. data/app/services/orcid/remote/profile_query_service/response_parser.rb +53 -0
  10. data/app/services/orcid/remote/profile_query_service.rb +4 -23
  11. data/app/views/orcid/profile_connections/_authenticated_connection.html.erb +5 -0
  12. data/app/views/orcid/profile_connections/_options_to_connect_orcid_profile.html.erb +20 -0
  13. data/app/views/orcid/profile_connections/_orcid_connector.html.erb +15 -18
  14. data/app/views/orcid/profile_connections/_pending_connection.html.erb +11 -0
  15. data/app/views/orcid/profile_connections/_profile_request_pending.html.erb +5 -0
  16. data/config/locales/orcid.en.yml +1 -1
  17. data/lib/orcid/configuration/provider.rb +8 -0
  18. data/lib/orcid/version.rb +1 -1
  19. data/lib/orcid.rb +4 -0
  20. data/spec/controllers/orcid/profile_requests_controller_spec.rb +21 -9
  21. data/spec/features/public_api_query_spec.rb +6 -0
  22. data/spec/fixtures/orcid-remote-profile_query_service-response_parser/multiple-responses-without-valid-response.json +258 -0
  23. data/spec/fixtures/orcid-remote-profile_query_service-response_parser/single-response-with-orcid-valid-profile.json +71 -0
  24. data/spec/lib/orcid/configuration/provider_spec.rb +3 -0
  25. data/spec/lib/orcid_spec.rb +8 -0
  26. data/spec/models/orcid/profile_connection_spec.rb +39 -17
  27. data/spec/models/orcid/profile_status_spec.rb +73 -0
  28. data/spec/routing/orcid/profile_request_routing_spec.rb +15 -0
  29. data/spec/services/orcid/remote/profile_creation_service_spec.rb +23 -3
  30. data/spec/services/orcid/remote/profile_query_service/response_parser_spec.rb +43 -0
  31. data/spec/services/orcid/remote/profile_query_service_spec.rb +7 -89
  32. data/spec/views/orcid/profile_connections/_authenticated_connection.html.erb_spec.rb +20 -0
  33. data/spec/views/orcid/profile_connections/_options_to_connect_orcid_profile.html.erb_spec.rb +26 -0
  34. data/spec/views/orcid/profile_connections/_orcid_connector.html.erb_spec.rb +65 -0
  35. data/spec/views/orcid/profile_connections/_pending_connection.html.erb_spec.rb +22 -0
  36. data/spec/views/orcid/profile_connections/_profile_request_pending.html.erb_spec.rb +24 -0
  37. metadata +29 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 72fe44a31b834e45dc65efebc06f416ec6452908
4
- data.tar.gz: 93a589d4b5992b70e1ca685f1b50b37b9695efe6
3
+ metadata.gz: cdbf8eaf74801c8b3c9d2325d362d14e7f7e3c8b
4
+ data.tar.gz: dc2b890ba3958416489f364983885bf6a08a0f85
5
5
  SHA512:
6
- metadata.gz: c8ce12e869d4935a7e7c563a2b760630a42b8139d3f1fbe8c0d73a66b05d00b4b8be6a7ad1ee97b6baf8744be2606bd39cadf00b7d33c63155c0be9ef77935be
7
- data.tar.gz: 04a0d325084d3023aefc595abefe659ce126e57a81aae12d62949c308eec60a00c02bedbee72ccc9e7077795c1084f21ea9a0360adaf0767007ea5892efb6fdb
6
+ metadata.gz: 18825277ac23c713d06fad1ead3df6565bf1273fcb77cb53e01839cd7a5e443c7fad3f17cc4eefa1c70a73d4a69d7a03e5c1b5999d187a421a40c2ed0f8fd7ed
7
+ data.tar.gz: 3737282f7a406c4ca10cae3f2d51b315d512278c834680c20c57821f140864d2e0188a888a6a89eb0ff1a6f3f5df9ec0cf93cb9354d493121f19683062a6aa3e
data/.hound.yml CHANGED
@@ -187,6 +187,7 @@ Documentation:
187
187
 
188
188
  DotPosition:
189
189
  Description: 'Checks the position of the dot in multi-line method calls.'
190
+ EnforcedStyle: trailing
190
191
  Enabled: true
191
192
 
192
193
  DoubleNegation:
@@ -235,7 +236,7 @@ EvenOdd:
235
236
 
236
237
  FileName:
237
238
  Description: 'Use snake_case for source file names.'
238
- Enabled: true
239
+ Enabled: false
239
240
 
240
241
  FlipFlop:
241
242
  Description: 'Checks for flip flops'
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,213 @@
1
+ We want your help to make our project great.
2
+ There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.
3
+
4
+ * [The Hydra Way](#the-hydra-way)
5
+ * [Reporting Issues](#reporting-issues)
6
+ * [What is a well written issue?](#what-is-a-well-written-issue)
7
+ * [Making Changes](#making-changes)
8
+ * [Where to Engage for Help](#where-to-engage-for-help)
9
+ * [Submitting Changes](#submitting-changes)
10
+ * [Contributor License Agreement](#contributor-license-agreement)
11
+ * [Coding Guidelines](#coding-guidelines)
12
+ * [Stating Your Intent](#stating-your-intent)
13
+ * [Writing Your Specs](#writing-your-specs)
14
+ * [Writing Your Code](#writing-your-code)
15
+ * [Ruby File Structure](#ruby-file-structure)
16
+ * [Well Written Commit Messages](#well-written-commit-messages)
17
+ * [Hooks into other Subsystems](#hooks-into-other-subsystems)
18
+ * [Reviewing Changes](#reviewing-changes)
19
+ * [Responsibilities of a Reviewer](#responsibilities-of-a-reviewer)
20
+ * [Responsibilities of the Submitter](#responsibilities-of-the-submitter)
21
+ * [Merging Changes](#merging-changes)
22
+
23
+ # The Hydra Way
24
+
25
+ We strive to…
26
+
27
+ * Provide a [harassment-free community experience](https://wiki.duraspace.org/display/hydra/Anti-Harassment+Policy)
28
+ * Adhere to our [Hierarchy of Promises](https://wiki.duraspace.org/display/hydra/Hydra+Stack+-+The+Hierarchy+of+Promises)
29
+ * Operate under [Lazy Consensus](http://rave.apache.org/docs/governance/lazyConsensus.html)
30
+ * Encourage community participation as guided by [our community principles](https://wiki.duraspace.org/display/hydra/Hydra+Community+Principles)
31
+
32
+ # Reporting Issues
33
+
34
+ Submit a [well written issue](#what-is-a-well-written-issue) to [Github's issue tracker](./issues).
35
+ This will require a [GitHub account](https://github.com/signup/free) *(its free)*.
36
+
37
+ ## What is a well written issue?
38
+
39
+ * Provide a descriptive summary
40
+ * Reference the version in which you encountered the problem
41
+ * Explain the expected behavior
42
+ * Explain the actual behavior
43
+ * Provide steps to reproduce the actual behavior
44
+
45
+ # Making Changes
46
+
47
+ Hydra is an open source project, released under the [APACHE 2 license](LICENSE).
48
+ You are free to clone or [fork the repository](https://help.github.com/articles/fork-a-repo) and modify the code as you see fit.
49
+
50
+ ## Where to Engage for Help
51
+
52
+ This gem is part of ProjectHydra, so you can [connect via the usual ProjectHydra channels](https://wiki.duraspace.org/pages/viewpage.action?pageId=43910187).
53
+
54
+ # Submitting Changes
55
+
56
+ ## Contributor License Agreement
57
+
58
+ **Note: You can submit a pull request before you've signed the Contributor License Agreement, but we won't merge your changes until we have your CLA on file.**
59
+
60
+ Before any [ProjectHydra project](https://github.com/projecthydra) can accept your contributions we must have a [Contributor License Agreement on file](https://wiki.duraspace.org/display/hydra/Hydra+Project+Intellectual+Property+Licensing+and+Ownership).
61
+
62
+ All code contributors must have an Individual Contributor License Agreement (iCLA) on file with the Hydra Project Steering Group.
63
+ If the contributor works for an institution, the institution must have a Corporate Contributor License Agreement (cCLA) on file.
64
+
65
+ [More on the Contributor License Agreements](https://wiki.duraspace.org/display/hydra/Hydra+Project+Intellectual+Property+Licensing+and+Ownership)
66
+
67
+ ## Coding Guidelines
68
+
69
+ This project is using [HoundCI](https://houndci.com) to help support our agreed upon style guide.
70
+ The style guide is a work in progress, and is declared in the project's `./hound.yml` file.
71
+
72
+ Hound is a Github integration tool that essentially runs [rubocop](http://rubygems.org/gems/rubocop).
73
+
74
+ > Automatic Ruby code style checking tool. Aims to enforce the community-driven Ruby Style Guide.
75
+
76
+ If you want to run `rubocop` with our style guide, first `gem install rubocop` then inside the project:
77
+
78
+ ```bash
79
+ $ rubocop ./path/to/file ./or/path/to/directory -c ./.hound.yml
80
+ ```
81
+ **Can I break these guidelines?** Yes.
82
+ But you may need to convince the person merging your changes.
83
+
84
+ ### Stating Your Intent
85
+
86
+ Think of your written test as a statement of intent.
87
+ The statement of intent can then be used when asking for help or clarity;
88
+ Either from another developer or a stakeholder.
89
+ Someone helping you can then see both what you are trying to do and how you are doing it;
90
+ And that helper may know of a "better" way to do it.
91
+
92
+ ### Writing Your Specs
93
+
94
+ Your code changes should include supporting tests.
95
+
96
+ Before you begin writing code, think about the test that will verify the code you plan to write.
97
+ A [well written story with Gherkin syntax](http://pivotallabs.com/well-formed-stories/) can help formulate the pre-conditions (Given), invocation (When), and post-conditions (Then).
98
+
99
+ *This is the first step of Test Driven Development, and something that we encourage.*
100
+
101
+ Now write that test; It should be your guidepost for any changes you plan on making.
102
+ The test you just wrote should be executable code;
103
+ If you are uncomfortable with where to put the executable code, a well formed Gherkin-syntax story is a suitable proxy.
104
+
105
+ ### Writing Your Code
106
+
107
+ We are going to do our best to follow [Sandi Metz' Rules for Developers](http://robots.thoughtbot.com/post/50655960596/sandi-metz-rules-for-developers)
108
+
109
+ > Here are the rules:
110
+ >
111
+ > * Classes can be no longer than one hundred lines of code.
112
+ > * Methods can be no longer than five lines of code.
113
+ > * Pass no more than four parameters into a method. Hash options are parameters.
114
+ > * Controllers can instantiate only one object. Therefore, views can only know about one instance variable and views should only send messages to that object (`@object.collaborator.value` is not allowed).
115
+
116
+ ## Well Written Commit Messages
117
+
118
+ **TL;DR**
119
+
120
+ * First line is 50 characters or less
121
+ * The message body explains what the code changes are about
122
+ * [Reference any Github issues](https://help.github.com/articles/writing-on-github#references) on new lines.
123
+ * It is helpful if you [close a related issue via the commit message](https://help.github.com/articles/closing-issues-via-commit-messages)
124
+
125
+ ### Terse Example
126
+
127
+ ```
128
+ Removing Document title validation
129
+
130
+ We thought we wanted title validation but that was too complicated.
131
+
132
+ Closes #12
133
+ ```
134
+
135
+ ### Verbose Example
136
+
137
+ ```
138
+ Present tense short summary (50 characters or less)
139
+
140
+ More detailed description, if necessary. It should be wrapped to 72
141
+ characters. Try to be as descriptive as you can, even if you think that
142
+ the commit content is obvious, it may not be obvious to others. You
143
+ should add such description also if it's already present in bug tracker,
144
+ it should not be necessary to visit a webpage to check the history.
145
+
146
+ Description can have multiple paragraphs and you can use code examples
147
+ inside, just indent it with 4 spaces:
148
+
149
+ class PostsController
150
+ def index
151
+ respond_with Post.limit(10)
152
+ end
153
+ end
154
+
155
+ You can also add bullet points:
156
+
157
+ - you can use dashes or asterisks
158
+
159
+ - also, try to indent next line of a point for readability, if it's too
160
+ long to fit in 72 characters
161
+ ```
162
+
163
+ > When appropriate, please squash your commits into logical units of work.
164
+ > This simplifies future cherry picks, and also keeps the git log clean.
165
+
166
+ ### Hooks into other Subsystems
167
+
168
+ **[ci skip]**: If your commit does not need to go through the Continuous Integration server, add `[ci skip]` to your commit message.
169
+ This is used for updates to the documentation and stylesheet changes.
170
+
171
+ # Reviewing Changes
172
+
173
+ The review process is a conversation between the submitter and the team as a whole.
174
+ Please feel free to bring other people into the conversation as necessary.
175
+
176
+ As either the submitter or reviewer, feel free to assign the Pull Request to a repository contributor.
177
+ This is a way of indicating that you want that repository contributor to review the change.
178
+
179
+ When you do assign someone to the Pull Request, please make sure to add a comment stating why you assigned it to them.
180
+
181
+ ## Responsibilities of a Reviewer
182
+
183
+ As a reviewer, it is important that the pull request:
184
+
185
+ * Has a [well written commit message](#well-written-commit-messages)
186
+ * Has [well written code](#coding-guidelines)
187
+ * All tests pass in the test suite
188
+ * Any questions regarding the pull request are answered
189
+ * Adjudicate if the Pull Request should be squashed into a smaller number of commits
190
+
191
+ ## Responsibilities of the Submitter
192
+
193
+ **As the submitter**, you should be responsive to the review process:
194
+
195
+ * answering questions
196
+ * making refinements
197
+ * providing clarification
198
+ * rebasing your commits.
199
+
200
+ *If your changes are gridlocked please notify [@jeremyf](https://github.com/jeremyf) via a comment on the pull request.*
201
+
202
+ # Merging Changes
203
+
204
+ *If a pull request has received at least one Thumbs Up, but has still not been merged, please notify [@jeremyf](https://github.com/jeremyf) via a comment on the pull request.*
205
+
206
+ **As the submitter,** you should not merge your own pull request.
207
+ That is bad form.
208
+
209
+ **As the reviewer,** if you are comfortable merge the pull request.
210
+ Otherwise feel free to assign the pull request to another contributor for final merging.
211
+
212
+ **As the merger,** once you have merged the pull request, go ahead and delete the pull request's topic branch.
213
+ You are now on the hook for any breaking of the build.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Orcid [![Version](https://badge.fury.io/rb/orcid.png)](http://badge.fury.io/rb/orcid) [![Build Status](https://travis-ci.org/jeremyf/orcid.png?branch=master)](https://travis-ci.org/jeremyf/orcid)
1
+ # Orcid [![Version](https://badge.fury.io/rb/orcid.png)](http://badge.fury.io/rb/orcid) [![Build Status](https://travis-ci.org/projecthydra-labs/orcid.png?branch=master)](https://travis-ci.org/projecthydra-labs/orcid)
2
2
 
3
3
  A [Rails Engine](https://guides.rubyonrails.org/engines.html) for integrating with [Orcid](https://orcid.org).
4
4
 
@@ -9,6 +9,7 @@ To fully interact with the Orcid remote services, you will need to [register you
9
9
  * [Registering for an ORCID application profile](#registering-for-an-orcid-application-profile)
10
10
  * [Setting up your own ORCIDs in the ORCID Development Sandbox](#setting-up-your-own-orcids-in-the-orcid-development-sandbox)
11
11
  * [Running the tests](#running-the-tests)
12
+ * [Contributing to this gem](./CONTRIBUTING.md)
12
13
 
13
14
  ## Installation
14
15
 
@@ -120,7 +121,3 @@ Run the online tests with
120
121
  ```console
121
122
  $ rake spec:online
122
123
  ```
123
-
124
- ## TODO Items
125
-
126
- * When searching for your profile, expose Name and associated DOI as query parameters.
@@ -25,7 +25,17 @@ module Orcid
25
25
  return false if redirecting_because_user_has_existing_profile_request
26
26
  assign_attributes(new_profile_request)
27
27
  create_profile_request(new_profile_request)
28
- respond_with(orcid, new_profile_request)
28
+ # As a named singular resource, url_for(profile_request) treates the
29
+ # input profile_request as the :format paramter. When you run
30
+ # `$ rake app:routes` in this gem, there is not a named route for:
31
+ # GET /profile_request(.:format) orcid/profile_requests#show
32
+ #
33
+ # Thus we need to pass the location.
34
+ if new_profile_request.valid?
35
+ respond_with(orcid, location: orcid.profile_request_path)
36
+ else
37
+ respond_with(orcid, new_profile_request)
38
+ end
29
39
  end
30
40
 
31
41
  protected
@@ -7,8 +7,9 @@ module Orcid
7
7
  include ActiveModel::Conversion
8
8
  extend ActiveModel::Naming
9
9
 
10
+ # See: http://support.orcid.org/knowledgebase/articles/132354-tutorial-searching-with-the-api
10
11
  class_attribute :available_query_attribute_names
11
- self.available_query_attribute_names = [:email, :text]
12
+ self.available_query_attribute_names = [:email, :text, :digital_object_ids]
12
13
 
13
14
  available_query_attribute_names.each do |attribute_name|
14
15
  attribute attribute_name
@@ -68,7 +69,16 @@ module Orcid
68
69
  private :query_requested?
69
70
 
70
71
  def query_attributes
71
- attributes.slice(*available_query_attribute_names)
72
+ available_query_attribute_names.each_with_object({}) do |name, mem|
73
+ orcid_formatted_name = convert_attribute_name_to_orcid_format(name)
74
+ mem[orcid_formatted_name] = attributes.fetch(name)
75
+ mem
76
+ end
77
+ end
78
+
79
+ def convert_attribute_name_to_orcid_format(name)
80
+ name.to_s.gsub(/_+/, '-')
72
81
  end
82
+ private :convert_attribute_name_to_orcid_format
73
83
  end
74
84
  end
@@ -0,0 +1,67 @@
1
+ module Orcid
2
+ # ProfileStatus.status
3
+ # **:authenticated_connection** - User has authenticated against the Orcid
4
+ # remote system
5
+ # **:pending_connection** - User has indicated there is a connection, but has
6
+ # not authenticated against the Orcid remote system
7
+ # **:profile_request_pending** - User has requested a profile be created on
8
+ # their behalf
9
+ # **:unknown** - None of the above
10
+ class ProfileStatus
11
+ def self.for(user, collaborators = {}, &block)
12
+ new(user, collaborators, &block).status
13
+ end
14
+
15
+ attr_reader :user, :profile_finder, :request_finder, :callback_handler
16
+
17
+ def initialize(user, collaborators = {})
18
+ @user = user
19
+ @profile_finder = collaborators.fetch(:profile_finder) { default_profile_finder }
20
+ @request_finder = collaborators.fetch(:request_finder) { default_request_finder }
21
+ @callback_handler = collaborators.fetch(:callback_handler) { default_callback_handler }
22
+ yield(callback_handler) if block_given?
23
+ end
24
+
25
+ def status
26
+ return callback(:unknown) if user.nil?
27
+ profile = profile_finder.call(user)
28
+ if profile
29
+ if profile.verified_authentication?
30
+ return callback(:authenticated_connection, profile)
31
+ else
32
+ return callback(:pending_connection, profile)
33
+ end
34
+ else
35
+ request = request_finder.call(user)
36
+ if request
37
+ return callback(:profile_request_pending, request)
38
+ else
39
+ return callback(:unknown)
40
+ end
41
+ end
42
+ return callback(:unknown)
43
+ end
44
+
45
+ private
46
+
47
+ def callback(name, *args)
48
+ callback_handler.call(name, *args)
49
+ name
50
+ end
51
+
52
+ def default_callback_handler
53
+ require 'orcid/named_callbacks'
54
+ NamedCallbacks.new
55
+ end
56
+
57
+ def default_profile_finder
58
+ require 'orcid'
59
+ Orcid.method(:profile_for)
60
+ end
61
+
62
+ def default_request_finder
63
+ require 'orcid/profile_request'
64
+ ProfileRequest.method(:find_by_user)
65
+ end
66
+ end
67
+ end
@@ -1,4 +1,6 @@
1
1
  require 'orcid/remote/service'
2
+ require 'oauth2/error'
3
+ require 'nokogiri'
2
4
  module Orcid
3
5
  module Remote
4
6
  # Responsible for minting a new ORCID for the given payload.
@@ -20,6 +22,8 @@ module Orcid
20
22
  def call(payload)
21
23
  response = deliver(payload)
22
24
  parse(response)
25
+ rescue ::OAuth2::Error => e
26
+ parse_exception(e)
23
27
  end
24
28
 
25
29
  protected
@@ -40,6 +44,17 @@ module Orcid
40
44
  end
41
45
  end
42
46
 
47
+ def parse_exception(exception)
48
+ doc = Nokogiri::XML.parse(exception.response.body)
49
+ error_text = doc.css('error-desc').text
50
+ if error_text.to_s.size > 0
51
+ callback(:orcid_validation_error, error_text)
52
+ false
53
+ else
54
+ fail exception
55
+ end
56
+ end
57
+
43
58
  def default_headers
44
59
  {
45
60
  'Accept' => 'application/xml',
@@ -0,0 +1,53 @@
1
+ require_dependency 'orcid/remote/profile_query_service'
2
+ module Orcid
3
+ module Remote
4
+ class ProfileQueryService
5
+ class ResponseParser
6
+
7
+ # A convenience method to expose entry into the ResponseParser function
8
+ def self.call(document, collaborators = {})
9
+ new(collaborators).call(document)
10
+ end
11
+
12
+ attr_reader :response_builder, :logger
13
+
14
+ def initialize(collaborators = {})
15
+ @response_builder = collaborators.fetch(:response_builder) do
16
+ SearchResponse
17
+ end
18
+ @logger = collaborators.fetch(:logger) do
19
+ Rails.logger
20
+ end
21
+ end
22
+
23
+ def call(document)
24
+ json = JSON.parse(document)
25
+ json.fetch('orcid-search-results').fetch('orcid-search-result')
26
+ .each_with_object([]) do |result, returning_value|
27
+ profile = result.fetch('orcid-profile')
28
+ begin
29
+ identifier = profile.fetch('orcid-identifier').fetch('path')
30
+ orcid_bio = profile.fetch('orcid-bio')
31
+ given_names = orcid_bio.fetch('personal-details').fetch('given-names').fetch('value')
32
+ family_name = orcid_bio.fetch('personal-details').fetch('family-name').fetch('value')
33
+ emails = []
34
+ contact_details = orcid_bio['contact-details']
35
+ if contact_details
36
+ emails = (contact_details['email'] || []).map {|email| email.fetch('value') }
37
+ end
38
+ label = "#{given_names} #{family_name}"
39
+ label << ' (' << emails.join(', ') << ')' if emails.any?
40
+ label << " [ORCID: #{identifier}]"
41
+ biography = ''
42
+ biography = orcid_bio['biography']['value'] if orcid_bio['biography']
43
+ returning_value << response_builder.new('id' => identifier, 'label' => label, 'biography' => biography)
44
+ rescue KeyError => e
45
+ logger.warn("Unexpected ORCID JSON Response, part of the response has been ignored.\tException Encountered:#{e.class}\t#{e}")
46
+ end
47
+ returning_value
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -9,11 +9,12 @@ module Orcid
9
9
  end
10
10
 
11
11
  attr_reader :token, :path, :headers, :response_builder, :query_builder
12
+ attr_reader :parser
12
13
  def initialize(config = {}, &callbacks)
13
14
  super(&callbacks)
14
15
  @query_builder = config.fetch(:query_parameter_builder) { QueryParameterBuilder }
15
16
  @token = config.fetch(:token) { default_token }
16
- @response_builder = config.fetch(:response_builder) { SearchResponse }
17
+ @parser = config.fetch(:parser) { ResponseParser }
17
18
  @path = config.fetch(:path) { 'v1.1/search/orcid-bio/' }
18
19
  @headers = config.fetch(:headers) { default_headers }
19
20
  end
@@ -41,7 +42,7 @@ module Orcid
41
42
  end
42
43
 
43
44
  def issue_callbacks(search_results)
44
- if search_results.any?
45
+ if Array(search_results).flatten.compact.any?
45
46
  callback(:found, search_results)
46
47
  else
47
48
  callback(:not_found)
@@ -54,27 +55,7 @@ module Orcid
54
55
  end
55
56
 
56
57
  def parse(document)
57
- json = JSON.parse(document)
58
-
59
- json.fetch('orcid-search-results').fetch('orcid-search-result')
60
- .each_with_object([]) do |result, returning_value|
61
- profile = result.fetch('orcid-profile')
62
- identifier = profile.fetch('orcid-identifier').fetch('path')
63
- orcid_bio = profile.fetch('orcid-bio')
64
- given_names = orcid_bio.fetch('personal-details').fetch('given-names').fetch('value')
65
- family_name = orcid_bio.fetch('personal-details').fetch('family-name').fetch('value')
66
- emails = []
67
- contact_details = orcid_bio['contact-details']
68
- if contact_details
69
- emails = (contact_details['email'] || []).map {|email| email.fetch('value') }
70
- end
71
- label = "#{given_names} #{family_name}"
72
- label << ' (' << emails.join(', ') << ')' if emails.any?
73
- label << " [ORCID: #{identifier}]"
74
- biography = ''
75
- biography = orcid_bio['biography']['value'] if orcid_bio['biography']
76
- returning_value << response_builder.new('id' => identifier, 'label' => label, 'biography' => biography)
77
- end
58
+ parser.call(document)
78
59
  end
79
60
 
80
61
  end
@@ -0,0 +1,5 @@
1
+ <div class="authenticated-connection">
2
+ <p>
3
+ Your ORCID (<a class="orcid-profile-id" href="<%= Orcid.url_for_orcid_id(authenticated_connection.orcid_profile_id)%>" ><%= authenticated_connection.orcid_profile_id %></a>) has been authenticated for this application.
4
+ </p>
5
+ </div>
@@ -0,0 +1,20 @@
1
+ <% profile_connection = Orcid::ProfileConnection.new %>
2
+ <% default_search_text = '' unless defined?(default_search_text) %>
3
+ <div class="options-to-connect-orcid-profile">
4
+ <p>
5
+ <%= link_to t('orcid/profile_connection.look_up_your_existing_orcid', scope: 'helpers.label'), orcid.new_profile_connection_path(profile_connection:{text: default_search_text}) %>
6
+ </p>
7
+ <p>
8
+ <%= link_to t('orcid/profile_connection.create_an_orcid', scope: 'helpers.label'), orcid.new_profile_request_path %>
9
+ </p>
10
+ <p>
11
+ <%= form_for(profile_connection, as: :profile_connection, url: orcid.profile_connections_path, method: :post) do |f| %>
12
+ <%# Note the `scope: 'helpers.label'` option is the default for the label, but I want to call those specifically out %>
13
+ <%= f.label :orcid_profile_id, t('orcid/profile_connection.orcid_profile_id', scope: 'helpers.label') %>
14
+ <%= f.text_field :orcid_profile_id, class:"orcid-input" %>
15
+ <button type="submit" class="search-submit btn btn-primary" id="keyword-search-submit" tabindex="2">
16
+ <span class="orcid-connect"><%= t('orcid/profile_connection.connect_button_text', scope: 'helpers.label') %></span>
17
+ </button>
18
+ <% end %>
19
+ </p>
20
+ </div>
@@ -1,22 +1,19 @@
1
- <% profile_connection = Orcid::ProfileConnection.new %>
2
- <% default_search_text = '' unless defined?(default_search_text) %>
1
+ <% defined?(status_processor) || status_processor = Orcid::ProfileStatus.method(:for) %>
3
2
  <div class='orcid-connector'>
4
3
  <h3><i class="icon-user"></i> <%= link_to t('orcid.verbose_name'), 'http://orcid.org/' %></h3>
5
- <p>
6
- <%= link_to t('orcid/profile_connection.look_up_your_existing_orcid', scope: 'helpers.label'), orcid.new_profile_connection_path(profile_connection:{text: default_search_text}) %>
7
- </p>
8
- <p>
9
- <%= link_to t('orcid/profile_connection.create_an_orcid', scope: 'helpers.label'), orcid.new_profile_request_path %>
10
- </p>
11
- <p>
12
- <%= form_for(profile_connection, as: :profile_connection, url: orcid.profile_connections_path, method: :post) do |f| %>
13
- <%# Note the `scope: 'helpers.label'` option is the default for the label, but I want to call those specifically out %>
14
- <%= f.label :orcid_profile_id, value: t('orcid/profile_connection.connect_button_text', scope: 'helpers.label') %>
15
- <%= f.text_field :orcid_profile_id, class:"orcid-input" %>
16
- <button type="submit" class="search-submit btn btn-primary" id="keyword-search-submit" tabindex="2">
17
- <span class="orcid-connect"><%= t('orcid/profile_connection.connect_button_text', scope: 'helpers.label') %></span>
18
- </button>
4
+ <% status_processor.call(current_user) do |on|%>
5
+ <% on.profile_request_pending do |pending_request| %>
6
+ <%= render partial: 'orcid/profile_connections/profile_request_pending', object: pending_request %>
7
+ <% end %>
8
+ <% on.authenticated_connection do |profile| %>
9
+ <%= render partial: 'orcid/profile_connections/authenticated_connection', object: profile %>
10
+ <% end %>
11
+ <% on.pending_connection do |profile| %>
12
+ <%= render partial: 'orcid/profile_connections/pending_connection', object: profile %>
13
+ <% end %>
14
+ <% on.unknown do %>
15
+ <% defined?(default_search_text) || default_search_text = '' %>
16
+ <%= render template: 'orcid/profile_connections/_options_to_connect_orcid_profile', locals: { default_search_text: default_search_text } %>
17
+ <% end %>
19
18
  <% end %>
20
- </p>
21
19
  </div>
22
-
@@ -0,0 +1,11 @@
1
+ <div class="pending-connection">
2
+ <p>
3
+ You have an ORCID (<a class="orcid-profile-id" href="<%= Orcid.url_for_orcid_id(pending_connection.orcid_profile_id)%>" ><%= pending_connection.orcid_profile_id %></a>).
4
+ </p>
5
+ <p>However, your ORCID has not been verified by this system. There are a few possibilities:
6
+ <ul>
7
+ <li>You may not have claimed your ORCID. <a class="find-out-more" href="http://support.orcid.org/knowledgebase/articles/164480-creating-claiming-new-records">Find out more about claiming your ORCID.</a></li>
8
+ <li>You have claimed your ORCID, but have not used it to <%= link_to('sign into this application', user_omniauth_authorize_path(provider: 'orcid'), class: 'signin-via-orcid') %>.</li>
9
+ </ul>
10
+ </p>
11
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="profile-request-pending">
2
+ <p>We are processing your ORCID Profile Request. It was submitted
3
+ <time datetime="<%= profile_request_pending.created_at.in_time_zone %>"><%= time_ago_in_words(profile_request_pending.created_at) %></time> ago.
4
+ </p>
5
+ </div>
@@ -30,7 +30,7 @@ en:
30
30
  messages:
31
31
  profile_connection_not_found: "Unable to find an existing Orcid Profile connection."
32
32
  verified_profile_connection_exists: "You have already connected and verified your Orcid Profile (%{orcid_profile_id})."
33
- verbose_name: Open Researcher and Contributor ID (ORCID)'
33
+ verbose_name: Open Researcher and Contributor ID (ORCID)
34
34
  helpers:
35
35
  label:
36
36
  orcid/profile_connection:
@@ -40,6 +40,14 @@ module Orcid
40
40
  end
41
41
  end
42
42
 
43
+ attr_writer :host_url
44
+ def host_url
45
+ @host_url ||= store.fetch('ORCID_HOST_URL') do
46
+ uri = URI.parse(signin_via_json_url)
47
+ "#{uri.scheme}://#{uri.host}"
48
+ end
49
+ end
50
+
43
51
  attr_writer :authorize_url
44
52
  def authorize_url
45
53
  @authorize_url ||= store.fetch('ORCID_AUTHORIZE_URL') do
data/lib/orcid/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Orcid
2
- VERSION = '0.1.1'
2
+ VERSION = '0.8.0'
3
3
  end
data/lib/orcid.rb CHANGED
@@ -71,6 +71,10 @@ module Orcid
71
71
  object.run
72
72
  end
73
73
 
74
+ def url_for_orcid_id(orcid_profile_id)
75
+ File.join(provider.host_url, orcid_profile_id)
76
+ end
77
+
74
78
  def oauth_client
75
79
  # passing the site: option as Orcid's Sandbox has an invalid certificate
76
80
  # for the api.sandbox.orcid.org