orcid 0.0.1.pre → 0.0.1

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.
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