orcid 0.0.1.pre → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 318aa12d99b90f05892d8ad841b07b82b5884e95
4
- data.tar.gz: e1c66b565ddb73077a1d242aef6243118e558ab8
3
+ metadata.gz: 148909130b9d10ade4c7e2e6b32d07321dcac06f
4
+ data.tar.gz: 0fb6c0784ef0a0000bda7e737fe7604519c7afe3
5
5
  SHA512:
6
- metadata.gz: c8502f9b04da7c9514f775f69919e41a36b190f710a8ebc1c507a1f05879e84c97b9a1c66eea9642dc4ab1a741441c9e1da813e414e98c6b8deec0210eaddf03
7
- data.tar.gz: 5d2feb28a15ff95b3479ad30abe1c545ab1fa3854548f46229d737f8bcfde73f9f4f9fc1ec44a5db501fb71f35c9c2944f42b85f39c1da3dad8409a9217f906e
6
+ metadata.gz: 7d8b914e980f40cb79f6d19942c56ba5f3a5d27330b05d3ef3cb3b3f2c809b829e5c566803b1b5ac1b5025a52da4163e1ee495c232142e2ee1a63fa69575d34c
7
+ data.tar.gz: 9bc94eba6a209de41c0fefa4719d511eb9528ff9c2fc02882bb3be065a0e80a1af06cef5d4afc7f2a23b77a731e76ce4460652536dd4eae7c9c2ebde71db0cd6
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
- = Orcid
1
+ # Orcid
2
2
 
3
- This project rocks and uses MIT-LICENSE.
3
+ A Rails Engine for integrating with Orcid
4
+
5
+ ## TODO
6
+
7
+ * Create a generator for installing configuration options into your Rails application.
data/Rakefile CHANGED
@@ -4,18 +4,33 @@ rescue LoadError
4
4
  puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
5
  end
6
6
 
7
- require 'rdoc/task'
7
+ Bundler::GemHelper.install_tasks
8
8
 
9
- RDoc::Task.new(:rdoc) do |rdoc|
10
- rdoc.rdoc_dir = 'rdoc'
11
- rdoc.title = 'Orcid'
12
- rdoc.options << '--line-numbers'
13
- rdoc.rdoc_files.include('README.rdoc')
14
- rdoc.rdoc_files.include('lib/**/*.rb')
15
- end
9
+ require 'engine_cart/rake_task'
16
10
 
11
+ require 'rspec/core/rake_task'
17
12
 
13
+ namespace :spec do
14
+ desc 'Only run specs that do not require net connect'
15
+ RSpec::Core::RakeTask.new(:offline) do |t|
16
+ t.rspec_opts = "--tag ~requires_net_connect"
17
+ end
18
18
 
19
+ desc 'Only run specs that require net connect'
20
+ RSpec::Core::RakeTask.new(:online) do |t|
21
+ t.rspec_opts = "--tag requires_net_connect"
22
+ end
19
23
 
20
- Bundler::GemHelper.install_tasks
24
+ desc 'Run the Travis CI specs'
25
+ task :travis do
26
+ ENV['RAILS_ENV'] = 'test'
27
+ ENV['SPEC_OPTS'] = "--profile 20"
28
+ Rake::Task['engine_cart:clean'].invoke
29
+ Rake::Task['engine_cart:generate'].invoke
30
+ Rake::Task['spec:offline'].invoke
31
+ end
32
+ end
33
+ Rake::Task["default"].clear rescue nil
34
+ Rake::Task["spec"].clear
21
35
 
36
+ task :spec => 'spec:offline'
@@ -1,4 +1,14 @@
1
1
  module Orcid
2
2
  class ApplicationController < ActionController::Base
3
+ private
4
+ def redirecting_because_user_already_has_a_connected_orcid_profile
5
+ if orcid_profile = Orcid.profile_for(current_user)
6
+ flash[:notice] = I18n.t("orcid.requests.messages.previously_connected_profile", orcid_profile_id: orcid_profile.orcid_profile_id)
7
+ redirect_to main_app.root_path
8
+ return true
9
+ else
10
+ return false
11
+ end
12
+ end
3
13
  end
4
14
  end
@@ -0,0 +1,46 @@
1
+ module Orcid
2
+ class ProfileConnectionsController < Orcid::ApplicationController
3
+ respond_to :html
4
+ before_filter :authenticate_user!
5
+
6
+ def index
7
+ render text: 'Not yet implemented!'
8
+ end
9
+
10
+ def new
11
+ return false if redirecting_because_user_already_has_a_connected_orcid_profile
12
+ assign_attributes(new_profile_connection)
13
+ respond_with(new_profile_connection)
14
+ end
15
+
16
+ def create
17
+ return false if redirecting_because_user_already_has_a_connected_orcid_profile
18
+ assign_attributes(new_profile_connection)
19
+ create_profile_connection(new_profile_connection)
20
+ respond_with(new_profile_connection)
21
+ end
22
+
23
+ protected
24
+
25
+ attr_reader :profile_connection
26
+ helper_method :profile_connection
27
+
28
+ def assign_attributes(profile_connection)
29
+ profile_connection.attributes = profile_connection_params
30
+ profile_connection.user = current_user
31
+ end
32
+
33
+ def profile_connection_params
34
+ params[:profile_connection] || {}
35
+ end
36
+
37
+ def create_profile_connection(profile_connection)
38
+ profile_connection.save
39
+ end
40
+
41
+ def new_profile_connection
42
+ @profile_connection ||= Orcid::ProfileConnection.new(params[:profile_connection])
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,67 @@
1
+ module Orcid
2
+ class ProfileRequestsController < Orcid::ApplicationController
3
+ respond_to :html
4
+ before_filter :authenticate_user!
5
+
6
+ attr_reader :profile_request
7
+ helper_method :profile_request
8
+
9
+ def show
10
+ return false if redirecting_because_user_already_has_a_connected_orcid_profile
11
+ return false if redirecting_because_no_profile_request_was_found
12
+ respond_with(existing_profile_request)
13
+ end
14
+
15
+ def new
16
+ return false if redirecting_because_user_already_has_a_connected_orcid_profile
17
+ return false if redirecting_because_user_has_existing_profile_request
18
+ assign_attributes(new_profile_request)
19
+ respond_with(new_profile_request)
20
+ end
21
+
22
+ def create
23
+ return false if redirecting_because_user_already_has_a_connected_orcid_profile
24
+ return false if redirecting_because_user_has_existing_profile_request
25
+ assign_attributes(new_profile_request)
26
+ create_profile_request(new_profile_request)
27
+ respond_with(new_profile_request)
28
+ end
29
+
30
+ protected
31
+
32
+ def redirecting_because_no_profile_request_was_found
33
+ return false if existing_profile_request
34
+ flash[:notice] = I18n.t("orcid.requests.messages.existing_request_not_found")
35
+ redirect_to new_profile_request_path
36
+ true
37
+ end
38
+
39
+ def redirecting_because_user_has_existing_profile_request
40
+ return false if ! existing_profile_request
41
+ flash[:notice] = I18n.t("orcid.requests.messages.existing_request")
42
+ redirect_to profile_request_path
43
+ true
44
+ end
45
+
46
+ def existing_profile_request
47
+ @profile_request ||= Orcid::ProfileRequest.find_by_user(current_user)
48
+ end
49
+
50
+ def new_profile_request
51
+ @profile_request ||= Orcid::ProfileRequest.new(user: current_user)
52
+ end
53
+
54
+ def assign_attributes(profile_request)
55
+ profile_request.attributes = profile_request_params
56
+ end
57
+
58
+ def create_profile_request(profile_request)
59
+ profile_request.save && Orcid.enqueue(profile_request)
60
+ end
61
+
62
+ def profile_request_params
63
+ return {} unless params.has_key?(:profile_request)
64
+ params[:profile_request].permit(:given_names, :family_name, :primary_email, :primary_email_confirmation)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,19 @@
1
+ module Orcid
2
+ class Configuration
3
+ attr_reader :store
4
+ def initialize(store = ::ENV)
5
+ @store = store
6
+ end
7
+
8
+ attr_writer :provider_name
9
+ def provider_name
10
+ @provider_name ||= 'orcid'
11
+ end
12
+
13
+ attr_writer :authentication_model
14
+ def authentication_model
15
+ @authentication_model ||= Devise::MultiAuth::Authentication
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ module Orcid
2
+ class Profile
3
+
4
+ attr_reader :orcid_profile_id, :mapper, :remote_service, :xml_renderer, :xml_parser
5
+ private :mapper
6
+ def initialize(orcid_profile_id, config = {})
7
+ @orcid_profile_id = orcid_profile_id
8
+ @mapper = config.fetch(:mapper) { ::Mappy }
9
+ @remote_service = config.fetch(:remote_service) { Orcid::RemoteWorkService }
10
+ @xml_renderer = config.fetch(:xml_renderer) { Orcid::Work::XmlRenderer }
11
+ @xml_parser = config.fetch(:xml_parser) { Orcid::Work::XmlParser }
12
+ end
13
+
14
+ def remote_works(options = {})
15
+ @remote_works = nil if options.fetch(:force, false)
16
+ @remote_works ||= begin
17
+ response = remote_service.call(orcid_profile_id, request_method: :get)
18
+ xml_parser.call(response)
19
+ end
20
+ end
21
+
22
+ def append_new_work(*works)
23
+ orcid_works = normalize_work(*works)
24
+ xml = xml_renderer.call(orcid_works)
25
+ remote_service.call(orcid_profile_id, request_method: :post, body: xml)
26
+ end
27
+
28
+ def replace_works_with(*works)
29
+ orcid_works = normalize_work(*works)
30
+ xml = xml_renderer.call(orcid_works)
31
+ remote_service.call(orcid_profile_id, request_method: :put, body: xml)
32
+ end
33
+
34
+ protected
35
+
36
+ # Note: We can handle
37
+ def normalize_work(*works)
38
+ Array(works).flatten.compact.collect do |work|
39
+ mapper.map(work, target: 'orcid/work')
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ module Orcid
2
+ # Responsible for connecting an authenticated user to the ORCID profile that
3
+ # the user searched for and selected.
4
+ class ProfileConnection
5
+ include Virtus.model
6
+ include ActiveModel::Validations
7
+ extend ActiveModel::Naming
8
+ attribute :email
9
+ attribute :orcid_profile_id
10
+ attribute :user
11
+
12
+ validates :user, presence: true
13
+ validates :orcid_profile_id, presence: true
14
+
15
+ def persisted?; false; end
16
+
17
+ def save(config = {})
18
+ persister = config.fetch(:persister) { Orcid.method(:connect_user_and_orcid_profile) }
19
+ valid? ? persister.call(user, orcid_profile_id) : false
20
+ end
21
+
22
+ def with_orcid_profile_candidates
23
+ if query_for_orcid_profile_candidates?
24
+ yield(orcid_profile_candidates)
25
+ end
26
+ end
27
+ attr_writer :orcid_profile_querier
28
+ protected
29
+ def orcid_profile_candidates
30
+ @orcid_profile_candidates ||= orcid_profile_querier.call({q: "email:#{email}"})
31
+ end
32
+ def query_for_orcid_profile_candidates?
33
+ email.present?
34
+ end
35
+
36
+ def orcid_profile_querier
37
+ @orcid_profile_querier ||= ProfileLookupService
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,84 @@
1
+ module Orcid
2
+ # Responsible for:
3
+ # * acknowledging that an ORCID Profile was requested
4
+ # * submitting a request for an ORCID Profile
5
+ # * handling the response for the ORCID Profile creation
6
+ class ProfileRequest < ActiveRecord::Base
7
+
8
+ def self.find_by_user(user)
9
+ where(user: user).first
10
+ end
11
+
12
+ self.table_name = :orcid_profile_requests
13
+
14
+ validates :user_id, presence: true, uniqueness: true
15
+ validates :given_names, presence: true
16
+ validates :family_name, presence: true
17
+ validates :primary_email, presence: true, email: true, confirmation: true
18
+
19
+ belongs_to :user
20
+
21
+ def run(options = {})
22
+ # Why dependency injection? Because this is going to be a plugin, and things
23
+ # can't possibly be simple.
24
+ before_run_validator = options.fetch(:before_run_validator) { method(:validate_before_run) }
25
+ return false unless before_run_validator.call(self)
26
+
27
+ payload_xml_builder = options.fetch(:payload_xml_builder) { method(:xml_payload) }
28
+ profile_creation_service = options.fetch(:profile_creation_service) { Orcid::ProfileCreationService }
29
+ profile_creation_responder = options.fetch(:profile_creation_responder) { method(:handle_profile_creation_response) }
30
+
31
+ orcid_profile_id = profile_creation_service.call(payload_xml_builder.call(attributes))
32
+ profile_creation_responder.call(orcid_profile_id)
33
+ end
34
+
35
+ def validate_before_run(context = self)
36
+
37
+ if context.orcid_profile_id?
38
+ context.errors.add(:base, "#{context.class} ID=#{context.to_param} already has an assigned :orcid_profile_id #{context.orcid_profile_id.inspect}")
39
+ return false
40
+ end
41
+
42
+ if user_orcid_profile = Orcid.profile_for(context.user)
43
+ context.errors.add(:base, "#{context.class} ID=#{context.to_param}'s associated user #{context.user.to_param} already has an assigned :orcid_profile_id #{user_orcid_profile.to_param}")
44
+ return false
45
+ end
46
+
47
+ true
48
+ end
49
+
50
+ # NOTE: This one lies -> http://support.orcid.org/knowledgebase/articles/177522-create-an-id-technical-developer
51
+ # NOTE: This one was true at 2014-02-06:14:55 -> http://support.orcid.org/knowledgebase/articles/162412-tutorial-create-a-new-record-using-curl
52
+ def xml_payload(input = attributes)
53
+ attrs = input.with_indifferent_access
54
+ returning_value = <<-XML_TEMPLATE
55
+ <?xml version="1.0" encoding="UTF-8"?>
56
+ <orcid-message
57
+ xmlns:xsi="http://www.orcid.org/ns/orcid https://raw.github.com/ORCID/ORCID-Source/master/orcid-model/src/main/resources/orcid-message-1.1.xsd"
58
+ xmlns="http://www.orcid.org/ns/orcid">
59
+ <message-version>1.1</message-version>
60
+ <orcid-profile>
61
+ <orcid-bio>
62
+ <personal-details>
63
+ <given-names>#{attrs.fetch('given_names')}</given-names>
64
+ <family-name>#{attrs.fetch('family_name')}</family-name>
65
+ </personal-details>
66
+ <contact-details>
67
+ <email primary="true">#{attrs.fetch('primary_email')}</email>
68
+ </contact-details>
69
+ </orcid-bio>
70
+ </orcid-profile>
71
+ </orcid-message>
72
+ XML_TEMPLATE
73
+ returning_value.strip
74
+ end
75
+
76
+ def handle_profile_creation_response(orcid_profile_id)
77
+ self.class.transaction do
78
+ update_column(:orcid_profile_id, orcid_profile_id)
79
+ Orcid.connect_user_and_orcid_profile(user, orcid_profile_id)
80
+ end
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,90 @@
1
+ module Orcid
2
+ # A well-defined data structure that coordinates with its :template in order
3
+ # to generate XML that can be POSTed/PUT as an Orcid Work.
4
+ class Work
5
+ VALID_WORK_TYPES = [
6
+ "artistic-performance","book-chapter","book-review","book","conference-abstract","conference-paper","conference-poster","data-set","dictionary-entry","disclosure","dissertation","edited-book","encyclopedia-entry","invention","journal-article","journal-issue","lecture-speech","license","magazine-article","manual","newsletter-article","newspaper-article","online-resource","other","patent","registered-copyright","report","research-technique","research-tool","spin-off-company","standards-and-policy","supervised-student-publication","technical-standard","test","translation","trademark","website","working-paper",
7
+ ].freeze
8
+
9
+ include Virtus.model
10
+ include ActiveModel::Validations
11
+ extend ActiveModel::Naming
12
+
13
+ attribute :title, String
14
+ validates :title, presence: true
15
+
16
+ attribute :work_type, String
17
+ validates :work_type, presence: true, inclusion: { in: VALID_WORK_TYPES }
18
+
19
+ attribute :put_code, String
20
+
21
+ def to_xml
22
+ XmlRenderer.call(self)
23
+ end
24
+
25
+ def ==(comparison_object)
26
+ super || comparison_object.instance_of?(self.class) &&
27
+ id.present? &&
28
+ comparison_object.id == id
29
+ end
30
+
31
+ def id
32
+ if put_code.present?
33
+ put_code
34
+ elsif title.present? && work_type.present?
35
+ [title, work_type]
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ class XmlRenderer
42
+ def self.call(works, options = {})
43
+ new(works, options).call
44
+ end
45
+
46
+ attr_reader :works, :template
47
+ def initialize(works, options = {})
48
+ self.works = works
49
+ @template = options.fetch(:template_path) { Orcid::Engine.root.join('app/templates/orcid/work.template.v1.1.xml.erb').read }
50
+ end
51
+
52
+ def call
53
+ ERB.new(template).result(binding)
54
+ end
55
+
56
+ protected
57
+ def works=(thing)
58
+ @works = Array(thing)
59
+ end
60
+
61
+ end
62
+
63
+ class XmlParser
64
+ def self.call(xml)
65
+ new(xml).call
66
+ end
67
+
68
+ attr_reader :xml
69
+ def initialize(xml)
70
+ @xml = xml
71
+ end
72
+
73
+ def call
74
+ document = Nokogiri::XML.parse(xml)
75
+ document.css('orcid-works orcid-work').collect do |node|
76
+ transform(node)
77
+ end
78
+ end
79
+
80
+ private
81
+ def transform(node)
82
+ Orcid::Work.new.tap do |work|
83
+ work.put_code = node.attributes.fetch("put-code").value
84
+ work.title = node.css('work-title title').text
85
+ work.work_type = node.css('work-type').text
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,35 @@
1
+ # Responsible for minting a new ORCID for the given payload.
2
+ module Orcid
3
+ class ProfileCreationService
4
+
5
+ def self.call(payload, config = {})
6
+ new(config).call(payload)
7
+ end
8
+
9
+ attr_reader :token, :path, :headers
10
+ def initialize(config = {})
11
+ @token = config.fetch(:token) { Orcid.client_credentials_token('/orcid-profile/create') }
12
+ @path = config.fetch(:path) { "v1.1/orcid-profile" }
13
+ @headers = config.fetch(:headers) { default_headers }
14
+ end
15
+
16
+ def call(payload)
17
+ response = deliver(payload)
18
+ parse(response)
19
+ end
20
+
21
+ protected
22
+ def deliver(body)
23
+ token.post(path, body: body, headers: headers)
24
+ end
25
+
26
+ def parse(response)
27
+ uri = URI.parse(response.headers.fetch(:location))
28
+ uri.path.sub(/\A\//, "").split("/").first
29
+ end
30
+
31
+ def default_headers
32
+ { "Accept" => 'application/xml', 'Content-Type'=>'application/vdn.orcid+xml' }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,65 @@
1
+ module Orcid
2
+ class ProfileLookupService
3
+ class SearchResponse
4
+ delegate :fetch, :has_key?, :[], to: :@attributes
5
+ def initialize(attributes = {})
6
+ @attributes = attributes.with_indifferent_access
7
+ end
8
+ def id
9
+ @attributes.fetch(:id)
10
+ end
11
+
12
+ def label
13
+ @attributes.fetch(:label)
14
+ end
15
+ end
16
+
17
+ def self.call(query, config = {})
18
+ new(config).call(query)
19
+ end
20
+
21
+ attr_reader :token, :path, :headers, :response_builder
22
+ def initialize(config = {})
23
+ @token = config.fetch(:token) { Orcid.client_credentials_token('/read-public') }
24
+ @response_builder = config.fetch(:response_builder) { SearchResponse }
25
+ @path = config.fetch(:path) { "v1.1/search/orcid-bio/" }
26
+ @headers = config.fetch(:headers) {
27
+ {
28
+ :accept => 'application/orcid+json',
29
+ 'Content-Type'=>'application/orcid+xml'
30
+ }
31
+ }
32
+ end
33
+
34
+ def call(parameters)
35
+ response = deliver(parameters)
36
+ parse(response.body)
37
+ end
38
+ alias_method :search, :call
39
+
40
+ protected
41
+ attr_reader :host, :access_token
42
+ def deliver(parameters)
43
+ token.get(path, headers: headers, params: parameters)
44
+ end
45
+
46
+ def parse(document)
47
+ json = JSON.parse(document)
48
+
49
+ json.fetch('orcid-search-results').fetch('orcid-search-result').each_with_object([]) do |result, returning_value|
50
+ profile = result.fetch('orcid-profile')
51
+ identifier = profile.fetch('orcid-identifier').fetch('path')
52
+ orcid_bio = profile.fetch('orcid-bio')
53
+ given_names = orcid_bio.fetch('personal-details').fetch('given-names').fetch('value')
54
+ family_name = orcid_bio.fetch('personal-details').fetch('family-name').fetch('value')
55
+ emails = orcid_bio.fetch('contact-details').fetch('email').collect {|email| email.fetch('value') }
56
+ label = "#{given_names} #{family_name}"
57
+ label << " (" << emails.join(",") << ")" if emails.present?
58
+ label << " [ORCID: #{identifier}]"
59
+
60
+ returning_value << response_builder.new("id" => identifier, "label" => label)
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,51 @@
1
+ module Orcid
2
+ class RemoteWorkService
3
+ def self.call(orcid_profile_id, options = {})
4
+ new(orcid_profile_id, options).call
5
+ end
6
+
7
+ attr_reader :headers, :token, :orcid_profile_id, :body, :request_method, :path
8
+ def initialize(orcid_profile_id, options = {})
9
+ @orcid_profile_id = orcid_profile_id
10
+ @request_method = options.fetch(:request_method) { :get }
11
+ @body = options.fetch(:body) { "" }
12
+ @token = options.fetch(:token) { Orcid.access_token_for(orcid_profile_id) }
13
+ @headers = options.fetch(:headers) { default_headers }
14
+ @path = options.fetch(:path) { default_path }
15
+ end
16
+
17
+ # :post will append works to the Orcid Profile
18
+ # :put will replace the existing Orcid Profile works with the payload
19
+ # :get will retrieve the Orcid Profile
20
+ # http://support.orcid.org/knowledgebase/articles/177528-add-works-technical-developer
21
+ def call
22
+ response = deliver
23
+ response.body
24
+ end
25
+
26
+ protected
27
+ def deliver
28
+ token.request(request_method, path, body: body, headers: headers)
29
+ rescue OAuth2::Error => e
30
+ raise RemoteServiceError.new(
31
+ response_body: e.response.body,
32
+ response_status: e.response.status,
33
+ client: token.client,
34
+ token: token,
35
+ request_method: request_method,
36
+ request_path: path,
37
+ request_body: body,
38
+ request_headers: headers
39
+ )
40
+ end
41
+
42
+ def default_headers
43
+ { 'Accept' => 'application/xml', 'Content-Type'=>'application/orcid+xml' }
44
+ end
45
+
46
+ def default_path
47
+ "v1.1/#{orcid_profile_id}/orcid-works/"
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <orcid-message xmlns="http://www.orcid.org/ns/orcid"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="https://raw.github.com/ORCID/ORCID-Source/master/orcid-model/src/main/resources/orcid-message-1.1.xsd">
5
+ <message-version>1.1</message-version>
6
+ <orcid-profile>
7
+ <orcid-activities>
8
+ <orcid-works><% works.each do |work| %>
9
+ <orcid-work>
10
+ <work-title>
11
+ <title><%= work.title %></title>
12
+ </work-title>
13
+ <work-type><%= work.work_type %></work-type>
14
+ </orcid-work>
15
+ <% end %></orcid-works>
16
+ </orcid-activities>
17
+ </orcid-profile>
18
+ </orcid-message>
@@ -0,0 +1,17 @@
1
+ <%= simple_form_for(profile_connection, as: :profile_connection, url: orcid.new_profile_connection_path, method: :get, html: {class: 'search-form'}) do |f| %>
2
+ <%= field_set_tag("Search ORCID Profiles", class: 'accessible-hidden') do %>
3
+ <%= f.input :email, type: :search %>
4
+ <% end %>
5
+ <button type="submit" class="search-submit btn btn-primary" id="keyword-search-submit" tabindex="2">
6
+ <i class="icon-search icon-white"></i><span class="accessible-hidden">Search</span>
7
+ </button>
8
+ <% end %>
9
+
10
+ <% profile_connection.with_orcid_profile_candidates do |candidates| %>
11
+ <%= simple_form_for(profile_connection, as: :profile_connection, url: orcid.profile_connections_path,) do |f| %>
12
+ <%= field_set_tag("Select an ORCID Profile", class: 'accessible-hidden') do %>
13
+ <%= f.collection_radio_buttons :orcid_profile_id, candidates, :id, :label %>
14
+ <% end %>
15
+ <%= f.submit %>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <%= simple_form_for profile_request, as: :profile_request, url: orcid.profile_request_path do |f| %>
2
+ <%= field_set_tag do%>
3
+ <%= f.input :given_names %>
4
+ <%= f.input :family_name %>
5
+ <%= f.input :primary_email %>
6
+ <%= f.input :primary_email_confirmation %>
7
+ <% end %>
8
+ <%= f.submit %>
9
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <p><strong>Requestor:</strong> <%= profile_request.user %></p>
2
+ <p><strong>Given names:</strong> <%= profile_request.given_names %></p>
3
+ <p><strong>Family name:</strong> <%= profile_request.family_name %></p>
4
+ <p><strong>Primary email:</strong> <%= profile_request.primary_email %></p>
5
+ <p><strong>ORCID Profile ID:</strong> <%= profile_request.orcid_profile_id %></p>
@@ -0,0 +1,28 @@
1
+ # Files in the config/locales directory are used for internationalization
2
+ # and are automatically loaded by Rails. If you want to use locales other
3
+ # than English, add the necessary files in this directory.
4
+ #
5
+ # To use the locales, use `I18n.t`:
6
+ #
7
+ # I18n.t 'hello'
8
+ #
9
+ # In views, this is aliased to just `t`:
10
+ #
11
+ # <%= t('hello') %>
12
+ #
13
+ # To use a different locale, set it with `I18n.locale`:
14
+ #
15
+ # I18n.locale = :es
16
+ #
17
+ # This would use the information in config/locales/es.yml.
18
+ #
19
+ # To learn more, please read the Rails Internationalization guide
20
+ # available at http://guides.rubyonrails.org/i18n.html.
21
+
22
+ en:
23
+ orcid:
24
+ requests:
25
+ messages:
26
+ existing_request_not_found: "Unable to find an existing Orcid Profile request. Go ahead and create one."
27
+ existing_request: "You have already submitted an Orcid Profile request."
28
+ previously_connected_profile: "You have already connected to an Orcid Profile (%{orcid_profile_id})."
data/config/routes.rb CHANGED
@@ -1,2 +1,4 @@
1
1
  Orcid::Engine.routes.draw do
2
+ resource :profile_request, only: [:show, :new, :create]
3
+ resources :profile_connections, only: [:new, :create, :index]
2
4
  end
@@ -0,0 +1,12 @@
1
+ class CreateOrcidProfileRequests < ActiveRecord::Migration
2
+ def change
3
+ create_table :orcid_profile_requests do |t|
4
+ t.integer :user_id, unique: true, index: true, null: false
5
+ t.string :given_names, null: false
6
+ t.string :family_name, null: false
7
+ t.string :primary_email, null: false
8
+ t.string :orcid_profile_id, unique: true, index: true
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
data/lib/orcid.rb CHANGED
@@ -1,4 +1,72 @@
1
- require "orcid/engine"
1
+ require 'orcid/engine'
2
+ require 'orcid/configuration'
3
+ require 'orcid/exceptions'
4
+
5
+ require 'mappy'
6
+ require 'devise_multi_auth'
7
+ require 'virtus'
8
+ require 'omniauth-orcid'
9
+ require 'email_validator'
10
+ require 'simple_form'
2
11
 
3
12
  module Orcid
13
+
14
+ class << self
15
+ attr_accessor :configuration
16
+ end
17
+
18
+ module_function
19
+ def configure
20
+ self.configuration ||= Configuration.new
21
+ yield(configuration)
22
+ end
23
+
24
+ def provider
25
+ @provider ||= Configuration::Provider.new
26
+ end
27
+
28
+ def provider_name
29
+ configuration.provider_name
30
+ end
31
+
32
+ def authentication_model
33
+ configuration.authentication_model
34
+ end
35
+
36
+
37
+ def connect_user_and_orcid_profile(user, orcid_profile_id, options = {})
38
+ authentication_model.create!(provider: provider_name, uid: orcid_profile_id, user: user)
39
+ end
40
+
41
+ def access_token_for(orcid_profile_id, options = {})
42
+ client = options.fetch(:client) { oauth_client }
43
+ tokenizer = options.fetch(:tokenizer) { authentication_model }
44
+ tokenizer.to_access_token(uid: orcid_profile_id, provider: provider_name, client: client)
45
+ end
46
+
47
+ def profile_for(user)
48
+ if auth = authentication_model.where(provider: provider_name, user: user).first
49
+ Orcid::Profile.new(auth.uid)
50
+ else
51
+ nil
52
+ end
53
+ end
54
+
55
+ def enqueue(object)
56
+ object.run
57
+ end
58
+
59
+ def oauth_client
60
+ # passing the site: option as Orcid's Sandbox has an invalid certificate
61
+ # for the api.sandbox-1.orcid.org
62
+ @oauth_client ||= Devise::MultiAuth.oauth_client_for(
63
+ provider_name, options: { site: provider.site_url }
64
+ )
65
+ end
66
+
67
+ def client_credentials_token(scope, options = {})
68
+ tokenizer = options.fetch(:tokenizer) { oauth_client.client_credentials }
69
+ tokenizer.get_token(scope: scope)
70
+ end
71
+
4
72
  end
@@ -0,0 +1,20 @@
1
+ module Orcid
2
+ class Configuration
3
+ attr_reader :store
4
+ def initialize(store = ::ENV)
5
+ @store = store
6
+ end
7
+
8
+ attr_writer :provider_name
9
+ def provider_name
10
+ @provider_name ||= 'orcid'
11
+ end
12
+
13
+ attr_writer :authentication_model
14
+ def authentication_model
15
+ @authentication_model ||= Devise::MultiAuth::Authentication
16
+ end
17
+
18
+ end
19
+ end
20
+ require 'orcid/configuration/provider'
@@ -0,0 +1,45 @@
1
+ module Orcid
2
+ class Configuration::Provider
3
+ attr_reader :store
4
+ def initialize(store = ::ENV)
5
+ @store = store
6
+ end
7
+
8
+ attr_writer :authentication_scope
9
+ def authentication_scope
10
+ @authentication_scope ||= store.fetch('ORCID_APP_AUTHENTICATION_SCOPE') {
11
+ "/authenticate,/orcid-works/create,/orcid-works/update,/read-public,/orcid-grants/create"
12
+ }
13
+ end
14
+
15
+ attr_writer :site_url
16
+ def site_url
17
+ @site_url ||= store.fetch('ORCID_SITE_URL') { "http://api.sandbox-1.orcid.org" }
18
+ end
19
+
20
+ attr_writer :token_url
21
+ def token_url
22
+ @token_url ||= store.fetch('ORCID_TOKEN_URL') { "https://api.sandbox-1.orcid.org/oauth/token" }
23
+ end
24
+
25
+ attr_writer :signin_via_json_url
26
+ def signin_via_json_url
27
+ @signin_via_json_url ||= store.fetch('ORCID_REMOTE_SIGNIN_URL') { "https://sandbox-1.orcid.org/signin/auth.json" }
28
+ end
29
+
30
+ attr_writer :authorize_url
31
+ def authorize_url
32
+ @authorize_url ||= store.fetch('ORCID_AUTHORIZE_URL') { "https://sandbox-1.orcid.org/oauth/authorize" }
33
+ end
34
+
35
+ attr_writer :id
36
+ def id
37
+ @id ||= store.fetch('ORCID_APP_ID')
38
+ end
39
+
40
+ attr_writer :secret
41
+ def secret
42
+ @secret ||= store.fetch('ORCID_APP_SECRET')
43
+ end
44
+ end
45
+ end
data/lib/orcid/engine.rb CHANGED
@@ -1,5 +1,19 @@
1
1
  module Orcid
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Orcid
4
+
5
+ initializer 'orcid.initializers' do |app|
6
+ app.config.paths.add 'app/services', eager_load: true
7
+ app.config.autoload_paths += %W(
8
+ #{config.root}/app/services
9
+ )
10
+ end
11
+
12
+ config.before_initialize do |app|
13
+ Orcid.configure do |config|
14
+ config.provider_name = 'orcid'
15
+ end
16
+ end
17
+
4
18
  end
5
19
  end
@@ -0,0 +1,28 @@
1
+ module Orcid
2
+ # Because in trouble shooting what all goes into this remote call,
3
+ # you may very well want all of this.
4
+ class RemoteServiceError < RuntimeError
5
+ def initialize(options)
6
+ text = []
7
+ text << "-- Client --"
8
+ if client = options[:client]
9
+ text << "id:\n\t#{client.id.inspect}"
10
+ text << "site:\n\t#{client.site.inspect}"
11
+ text << "options:\n\t#{client.options.inspect}"
12
+ end
13
+ text << "\n-- Token --"
14
+ if token = options[:token]
15
+ text << "access_token:\n\t#{token.token.inspect}"
16
+ text << "refresh_token:\n\t#{token.refresh_token.inspect}"
17
+ end
18
+ text << "\n-- Request --"
19
+ text << "path:\n\t#{options[:request_path].inspect}" if options[:request_path]
20
+ text << "headers:\n\t#{options[:request_headers].inspect}" if options[:request_headers]
21
+ text << "body:\n\t#{options[:request_body]}" if options[:request_body]
22
+ text << "\n-- Response --"
23
+ text << "status:\n\t#{options[:response_status].inspect}" if options[:response_status]
24
+ text << "body:\n\t#{options[:response_body]}" if options[:response_body]
25
+ super(text.join("\n"))
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,54 @@
1
+ require 'rest_client'
2
+ class RequestSandboxAuthorizationCode
3
+
4
+ def self.call(options = {}, config = {})
5
+ new(config).call(options)
6
+ end
7
+
8
+ attr_reader :cookies, :access_scope, :authorize_url, :login_url
9
+ attr_reader :oauth_redirect_uri, :orcid_client_id, :authorization_code, :orcid_client_secret
10
+
11
+ def initialize(options = {})
12
+ @orcid_client_id = options.fetch(:orcid_client_id) { Orcid.provider.id }
13
+ @orcid_client_secret = options.fetch(:orcid_client_secret) { Orcid.provider.secret }
14
+ @login_url = options.fetch(:login_url) { Orcid.provider.signin_via_json_url }
15
+ @authorize_url = options.fetch(:authorize_url) { Orcid.provider.authorize_url }
16
+ @oauth_redirect_uri = options.fetch(:oauth_redirect_uri) { 'https://developers.google.com/oauthplayground' }
17
+ @access_scope = options.fetch(:scope) { Orcid.provider.authentication_scope }
18
+ end
19
+
20
+ def call(options = {})
21
+ orcid_profile_id = options.fetch(:orcid_profile_id) { ENV['ORCID_CLAIMED_PROFILE_ID'] }
22
+ password = options.fetch(:password) { ENV['ORCID_CLAIMED_PROFILE_PASSWORD']}
23
+
24
+ login_to_orcid(orcid_profile_id, password)
25
+ request_authorization
26
+ request_authorization_code
27
+ end
28
+
29
+ attr_writer :cookies
30
+ private :cookies
31
+
32
+ private
33
+ def login_to_orcid(orcid_profile_id, password)
34
+ response = RestClient.post(login_url, userId: orcid_profile_id, password: password )
35
+ if ! JSON.parse(response)["success"]
36
+ raise "Response not successful: \n#{response}"
37
+ else
38
+ self.cookies = response.cookies
39
+ end
40
+ end
41
+
42
+ def request_authorization
43
+ parameters = { client_id: orcid_client_id, response_type: 'code', scope: access_scope, redirect_uri: oauth_redirect_uri }
44
+ RestClient.get(authorize_url, {params: parameters, cookies: cookies})
45
+ end
46
+
47
+ def request_authorization_code
48
+ RestClient.post(authorize_url, {user_oauth_approval: true}, {cookies: cookies})
49
+ rescue RestClient::Found => e
50
+ uri = URI.parse(e.response.headers.fetch(:location))
51
+ CGI::parse(uri.query).fetch('code').first
52
+ end
53
+
54
+ end
data/lib/orcid/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Orcid
2
- VERSION = "0.0.1.pre"
2
+ VERSION = "0.0.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orcid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Friesen
@@ -24,6 +24,104 @@ dependencies:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: 4.0.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: mappy
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: devise-multi_auth
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: omniauth-orcid
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: virtus
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: email_validator
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simple_form
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
27
125
  - !ruby/object:Gem::Dependency
28
126
  name: sqlite3
29
127
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +136,146 @@ dependencies:
38
136
  - - '>='
39
137
  - !ruby/object:Gem::Version
40
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: engine_cart
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rspec-rails
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: database_cleaner
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: factory_girl
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: rspec-html-matchers
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - '>='
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - '>='
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: capybara
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - '>='
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - '>='
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: capybara-webkit
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - '>='
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - '>='
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: webmock
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - '>='
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - '>='
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
251
+ - !ruby/object:Gem::Dependency
252
+ name: figaro
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - '>='
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - '>='
263
+ - !ruby/object:Gem::Version
264
+ version: '0'
265
+ - !ruby/object:Gem::Dependency
266
+ name: rest_client
267
+ requirement: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - '>='
270
+ - !ruby/object:Gem::Version
271
+ version: '0'
272
+ type: :development
273
+ prerelease: false
274
+ version_requirements: !ruby/object:Gem::Requirement
275
+ requirements:
276
+ - - '>='
277
+ - !ruby/object:Gem::Version
278
+ version: '0'
41
279
  description: A Rails engine for orcid.org integration.
42
280
  email:
43
281
  - jeremy.n.friesen@gmail.com
@@ -48,10 +286,30 @@ files:
48
286
  - app/assets/javascripts/orcid/application.js
49
287
  - app/assets/stylesheets/orcid/application.css
50
288
  - app/controllers/orcid/application_controller.rb
289
+ - app/controllers/orcid/profile_connections_controller.rb
290
+ - app/controllers/orcid/profile_requests_controller.rb
51
291
  - app/helpers/orcid/application_helper.rb
292
+ - app/models/orcid/configuration.rb
293
+ - app/models/orcid/profile.rb
294
+ - app/models/orcid/profile_connection.rb
295
+ - app/models/orcid/profile_request.rb
296
+ - app/models/orcid/work.rb
297
+ - app/services/orcid/profile_creation_service.rb
298
+ - app/services/orcid/profile_lookup_service.rb
299
+ - app/services/orcid/remote_work_service.rb
300
+ - app/templates/orcid/work.template.v1.1.xml.erb
52
301
  - app/views/layouts/orcid/application.html.erb
302
+ - app/views/orcid/profile_connections/new.html.erb
303
+ - app/views/orcid/profile_requests/new.html.erb
304
+ - app/views/orcid/profile_requests/show.html.erb
305
+ - config/locales/orcid.en.yml
53
306
  - config/routes.rb
307
+ - db/migrate/20140205185338_create_orcid_profile_requests.rb
308
+ - lib/orcid/configuration/provider.rb
309
+ - lib/orcid/configuration.rb
54
310
  - lib/orcid/engine.rb
311
+ - lib/orcid/exceptions.rb
312
+ - lib/orcid/spec_support.rb
55
313
  - lib/orcid/version.rb
56
314
  - lib/orcid.rb
57
315
  - lib/tasks/orcid_tasks.rake
@@ -72,9 +330,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
72
330
  version: '0'
73
331
  required_rubygems_version: !ruby/object:Gem::Requirement
74
332
  requirements:
75
- - - '>'
333
+ - - '>='
76
334
  - !ruby/object:Gem::Version
77
- version: 1.3.1
335
+ version: '0'
78
336
  requirements: []
79
337
  rubyforge_project:
80
338
  rubygems_version: 2.0.14